Skip to content

Commit

Permalink
Add initial reference stability unit tests
Browse files Browse the repository at this point in the history
Summary: Adding a few initial unit tests for the future map reference stability checker. `F14ValueMap` and `F14VectorMap` do not provide reference stability guarantees.

Reviewed By: skcho

Differential Revision: D49535916

fbshipit-source-id: 2ae470cf71ae3aebe287002101a39eff9a3e856d
  • Loading branch information
nicovank authored and facebook-github-bot committed Sep 27, 2023
1 parent 22cc4b7 commit ca4c3b4
Showing 1 changed file with 258 additions and 0 deletions.
258 changes: 258 additions & 0 deletions infer/tests/codetoanalyze/cpp/pulse/reference_stability.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#include <initializer_list>
#include <unordered_map>
#include <utility>

// Keep a simplified skeleton of F14 maps for testing.
namespace folly {
struct F14HashToken;

template <typename Key, typename Mapped>
struct F14BasicMap {
using key_type = Key;
using mapped_type = Mapped;
using value_type = std::pair<const Key, Mapped>;
using size_type = std::size_t;
using iterator = value_type*;
using const_iterator = value_type const*;

F14BasicMap() noexcept;

explicit F14BasicMap(std::size_t initialCapacity);

template <typename InputIt>
F14BasicMap(InputIt first, InputIt last, std::size_t initialCapacity = 0);

F14BasicMap(F14BasicMap const& rhs);

F14BasicMap(F14BasicMap&& rhs);

F14BasicMap(std::initializer_list<value_type> init,
std::size_t initialCapacity = 0);

F14BasicMap& operator=(F14BasicMap const&);

F14BasicMap& operator=(F14BasicMap&&);

F14BasicMap& operator=(std::initializer_list<value_type> ilist);

iterator begin() noexcept;
const_iterator begin() const noexcept;
const_iterator cbegin() const noexcept;
iterator end() noexcept;
const_iterator end() const noexcept;
const_iterator cend() const noexcept;

void clear() noexcept;

std::pair<iterator, bool> insert(value_type const& value);
std::pair<iterator, bool> insert(value_type&& value);
iterator insert(const_iterator /*hint*/, value_type const& value);
iterator insert(const_iterator /*hint*/, value_type&& value);

template <class... Args>
iterator emplace_hint(const_iterator /*hint*/, Args&&... args);

template <class InputIt>
void insert(InputIt first, InputIt last);
void insert(std::initializer_list<value_type> ilist);

template <typename M>
std::pair<iterator, bool> insert_or_assign(key_type const& key, M&& obj);

template <typename M>
std::pair<iterator, bool> insert_or_assign(key_type&& key, M&& obj);

template <typename M>
std::pair<iterator, bool> insert_or_assign(F14HashToken const& token,
key_type const& key,
M&& obj);

template <typename M>
std::pair<iterator, bool> insert_or_assign(F14HashToken const& token,
key_type&& key,
M&& obj);

template <typename M>
iterator insert_or_assign(const_iterator /*hint*/,
key_type const& key,
M&& obj);

template <typename M>
iterator insert_or_assign(const_iterator /*hint*/, key_type&& key, M&& obj);

template <typename... Args>
std::pair<iterator, bool> emplace(Args&&... args);

template <typename... Args>
std::pair<iterator, bool> try_emplace(key_type const& key, Args&&... args);

template <typename... Args>
std::pair<iterator, bool> try_emplace(key_type&& key, Args&&... args);

template <typename... Args>
std::pair<iterator, bool> try_emplace_token(F14HashToken const& token,
key_type const& key,
Args&&... args);

template <typename... Args>
std::pair<iterator, bool> try_emplace_token(F14HashToken const& token,
key_type&& key,
Args&&... args);

template <typename... Args>
iterator try_emplace(const_iterator /*hint*/,
key_type const& key,
Args&&... args);

template <typename... Args>
iterator try_emplace(const_iterator /*hint*/, key_type&& key, Args&&... args);

iterator erase(const_iterator pos);

iterator erase(iterator pos);

iterator erase(const_iterator first, const_iterator last);

size_type erase(key_type const& key);

template <typename BeforeDestroy>
iterator eraseInto(const_iterator pos, BeforeDestroy&& beforeDestroy);

template <typename BeforeDestroy>
iterator eraseInto(iterator pos, BeforeDestroy&& beforeDestroy);

template <typename BeforeDestroy>
iterator eraseInto(const_iterator first,
const_iterator last,
BeforeDestroy&& beforeDestroy);

template <typename BeforeDestroy>
size_type eraseInto(key_type const& key, BeforeDestroy&& beforeDestroy);

mapped_type& at(key_type const& key);

mapped_type const& at(key_type const& key) const;

mapped_type& operator[](key_type const& key);

mapped_type& operator[](key_type&& key);

F14HashToken prehash(key_type const& key) const;

iterator find(key_type const& key);

const_iterator find(key_type const& key) const;

iterator find(F14HashToken const& token, key_type const& key);

const_iterator find(F14HashToken const& token, key_type const& key) const;

std::pair<iterator, iterator> equal_range(key_type const& key);

std::pair<const_iterator, const_iterator> equal_range(
key_type const& key) const;

void rehash(std::size_t bucketCapacity);

void reserve(std::size_t capacity);
};

template <typename K, typename V>
using F14ValueMap = F14BasicMap<K, V>;
template <typename K, typename V>
using F14VectorMap = F14BasicMap<K, V>;
template <typename K, typename V>
using F14FastMap = F14BasicMap<K, V>;
} // namespace folly

void unordered_map_ok() {
std::unordered_map<int, int> map = {{1, 1}, {2, 4}, {3, 9}};

// Obtain long-lived references to keys and values.
const auto& keyRef = map.begin()->first;
const auto& valueRef = map.begin()->second;

// Possible rehash, but std::unordered_map guarantees reference stability.
map[4] = 16;
const auto keyCopy = keyRef;
}

void folly_fastmap_bad_FN() {
folly::F14FastMap<int, int> map = {{1, 1}, {2, 4}, {3, 9}};

// Obtain long-lived references to keys and values.
const auto& keyRef = map.begin()->first;
const auto& valueRef = map.begin()->second;

// Possible rehash, references invalidated.
map.emplace(4, 16);
const auto keyCopy = keyRef;
}

void folly_fastmap_short_lived_ok() {
folly::F14FastMap<int, int> map = {{1, 1}, {2, 4}, {3, 9}};

{
// Use short-lived references in a limited scope.
const auto& keyRef = map.begin()->first;
const auto& valueRef = map.begin()->second;

const auto keyCopy = keyRef;
const auto valueCopy = valueRef;

// No need to access keyRef and valueRef outside this block.
// Short-lived references are still valid here.
}

// Modify the map (no potential issues with short-lived references).
map[4] = 16;

// Access elements using iterators (no long-lived references).
for (const auto& pair : map) {
const auto keyCopy = pair.first;
const auto valueCopy = pair.second;
}
}

void long_lived_but_unused_ref_ok() {
folly::F14FastMap<int, int> map = {{1, 1}, {2, 4}, {3, 9}};

const auto& value = map.at(1);

map.reserve(100);

// value should be marked as invalidated here, but no error reported.
}

// We know there is no growth, so iterators/references wouldn't be invalidated.
void no_growth_ok() {
folly::F14FastMap<int, int> map = {{1, 1}, {2, 4}, {3, 9}};

const auto& keyRef = map.at(1);

// We know that 1 is already a key in the map, so there won't be a rehash.
map[1] = 1;

const auto valueCopy = keyRef;
}

void folly_valuemap_bad_FN() {
folly::F14ValueMap<int, int> map = {{1, 1}, {2, 4}, {3, 9}};
const auto& valueRef = map.at(1);
map.clear();
const auto valueCopy = valueRef;
}

void folly_vectormap_bad_FN() {
folly::F14VectorMap<int, int> map = {{1, 1}, {2, 4}, {3, 9}};
const auto& valueRef = map.at(1);
map.clear();
const auto valueCopy = valueRef;
}

0 comments on commit ca4c3b4

Please sign in to comment.