mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
High level, replacing `Id::Invalid` with `Id::None` and `Id::is_valid` with `Id::has_value` for clarity, as discussed [here](https://discord.com/channels/655572317891461132/655578254970716160/1331664574545395794). The `IntId` refactoring is needed together with `AnyIdBase` because it's also used with `ValueStore`. Note, trying to be careful not to rewrite `EnumBase::InvalidIndex`, or `is_valid` in general (e.g., `IdKind::is_valid`). I've tried to sequence commits here: 1. Automatic replacements: - `((?:Id|Index)(?: |::|\(|Base(?:\(|::)))Invalid((?:Index)?\W)` -> `$1None$2` - `<invalid>` -> `<none>` - `InvalidNodeId` -> `NoneNodeId` - `/\*invalid\*/` -> `/*none*/` - `id((?:_|\(\))(?:\.|->))is_valid` -> `id$1has_value` 2. Manual edits: - In `int.h` and `int_test.cpp` - `IntT` has `is_value`, which I'm renaming to `is_embedded_value`. - Manual edits to comments in this file. - `AnyIdBase` and `IdBase` - Declaration of `is_valid` -> `has_value`, `InvalidIndex` -> `NoneIndex`. - In `ids.h` and `ids.cpp` - `is_valid` -> `has_value` - `// An explicitly invalid ID.` -> `// An ID with no value.`; similar for index - Various math on `InvalidIndex` -> `NoneIndex` - Various mentions of "valid" in comments - In `value_store.h`, for `IdT::Invalid`, plus one comment - In `impl.h` and `tokenized_buffer.h`, we had different initialization of `::None` values (versus `ids.h` syntax) that I fixed manually. - Spot checks to compile - Particularly where `is_valid` replacements didn't catch spots due to different naming. 3. Autoupdate tests 4. verbose.carbon (NOAUTOUPDATE) 5. Comment spot checks Note there are probably other mentions of "Invalid" that should be swept up, but I'd like to argue for merging and separating out remaining cleanup since this is so sweeping (and likely to hit merge conflicts from churn). We'll probably have lingering mentions of "invalid" for a bit regardless, just because there are uses of "invalid" in non-Id APIs.
159 lines
5.3 KiB
C++
159 lines
5.3 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "toolchain/base/int.h"
|
|
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <limits>
|
|
|
|
namespace Carbon::Testing {
|
|
|
|
struct IntStoreTestPeer {
|
|
static constexpr int MinAPWidth = IntStore::MinAPWidth;
|
|
|
|
static constexpr int32_t MaxIdEmbeddedValue = IntId::MaxValue;
|
|
static constexpr int32_t MinIdEmbeddedValue = IntId::MinValue;
|
|
};
|
|
|
|
namespace {
|
|
|
|
using ::testing::Eq;
|
|
|
|
static constexpr int MinAPWidth = IntStoreTestPeer::MinAPWidth;
|
|
|
|
static constexpr int32_t MaxIdEmbeddedValue =
|
|
IntStoreTestPeer::MaxIdEmbeddedValue;
|
|
static constexpr int32_t MinIdEmbeddedValue =
|
|
IntStoreTestPeer::MinIdEmbeddedValue;
|
|
|
|
TEST(IntStore, Basic) {
|
|
IntStore ints;
|
|
IntId id_0 = ints.Add(0);
|
|
IntId id_1 = ints.Add(1);
|
|
IntId id_2 = ints.Add(2);
|
|
IntId id_42 = ints.Add(42);
|
|
IntId id_n1 = ints.Add(-1);
|
|
IntId id_n42 = ints.Add(-42);
|
|
IntId id_nines = ints.Add(999'999'999'999);
|
|
IntId id_max64 = ints.Add(std::numeric_limits<int64_t>::max());
|
|
IntId id_min64 = ints.Add(std::numeric_limits<int64_t>::min());
|
|
|
|
for (IntId id :
|
|
{id_0, id_1, id_2, id_42, id_n1, id_n42, id_nines, id_max64, id_min64}) {
|
|
ASSERT_TRUE(id.has_value());
|
|
}
|
|
|
|
// Small values should be embedded.
|
|
EXPECT_THAT(id_0.AsValue(), Eq(0));
|
|
EXPECT_THAT(id_1.AsValue(), Eq(1));
|
|
EXPECT_THAT(id_2.AsValue(), Eq(2));
|
|
EXPECT_THAT(id_42.AsValue(), Eq(42));
|
|
EXPECT_THAT(id_n1.AsValue(), Eq(-1));
|
|
EXPECT_THAT(id_n42.AsValue(), Eq(-42));
|
|
|
|
// Rest should be indices as they don't fit as embedded values.
|
|
EXPECT_TRUE(!id_nines.is_embedded_value());
|
|
EXPECT_TRUE(id_nines.is_index());
|
|
EXPECT_TRUE(!id_max64.is_embedded_value());
|
|
EXPECT_TRUE(id_max64.is_index());
|
|
EXPECT_TRUE(!id_min64.is_embedded_value());
|
|
EXPECT_TRUE(id_min64.is_index());
|
|
|
|
// And round tripping all the way through the store should work.
|
|
EXPECT_THAT(ints.Get(id_0), Eq(0));
|
|
EXPECT_THAT(ints.Get(id_1), Eq(1));
|
|
EXPECT_THAT(ints.Get(id_2), Eq(2));
|
|
EXPECT_THAT(ints.Get(id_42), Eq(42));
|
|
EXPECT_THAT(ints.Get(id_n1), Eq(-1));
|
|
EXPECT_THAT(ints.Get(id_n42), Eq(-42));
|
|
EXPECT_THAT(ints.Get(id_nines), Eq(999'999'999'999));
|
|
EXPECT_THAT(ints.Get(id_max64), Eq(std::numeric_limits<int64_t>::max()));
|
|
EXPECT_THAT(ints.Get(id_min64), Eq(std::numeric_limits<int64_t>::min()));
|
|
}
|
|
|
|
// Helper struct to hold test values and the resulting IDs.
|
|
struct APAndId {
|
|
llvm::APInt ap;
|
|
IntId id = IntId::None;
|
|
};
|
|
|
|
TEST(IntStore, APSigned) {
|
|
IntStore ints;
|
|
|
|
llvm::APInt big_128_ap =
|
|
llvm::APInt(128, 0x1234'abcd'1234'abcd, /*isSigned=*/true) * 0xabcd'0000;
|
|
llvm::APInt max_embedded_ap(MinAPWidth, MaxIdEmbeddedValue,
|
|
/*isSigned=*/true);
|
|
llvm::APInt min_embedded_ap(MinAPWidth, MinIdEmbeddedValue,
|
|
/*isSigned=*/true);
|
|
|
|
APAndId ap_and_ids[] = {
|
|
{.ap = llvm::APInt(MinAPWidth, 1, /*isSigned=*/true)},
|
|
{.ap = llvm::APInt(MinAPWidth, 2, /*isSigned=*/true)},
|
|
{.ap = llvm::APInt(MinAPWidth, 999'999'999'999, /*isSigned=*/true)},
|
|
{.ap = big_128_ap},
|
|
{.ap = -big_128_ap},
|
|
{.ap =
|
|
big_128_ap.sext(512) * big_128_ap.sext(512) * big_128_ap.sext(512)},
|
|
{.ap =
|
|
-big_128_ap.sext(512) * big_128_ap.sext(512) * big_128_ap.sext(512)},
|
|
{.ap = max_embedded_ap},
|
|
{.ap = max_embedded_ap + 1},
|
|
{.ap = min_embedded_ap},
|
|
{.ap = min_embedded_ap - 1},
|
|
};
|
|
for (auto& [ap, id] : ap_and_ids) {
|
|
id = ints.AddSigned(ap);
|
|
ASSERT_TRUE(id.has_value()) << ap;
|
|
}
|
|
|
|
for (const auto& [ap, id] : ap_and_ids) {
|
|
// The sign extend here may be a no-op, but the original bit width is a
|
|
// reliable one at which to do the comparison.
|
|
EXPECT_THAT(ints.Get(id).sext(ap.getBitWidth()), Eq(ap));
|
|
}
|
|
}
|
|
|
|
TEST(IntStore, APUnsigned) {
|
|
IntStore ints;
|
|
|
|
llvm::APInt big_128_ap =
|
|
llvm::APInt(128, 0xabcd'abcd'abcd'abcd) * 0xabcd'0000'abcd'0000;
|
|
llvm::APInt max_embedded_ap(MinAPWidth, MaxIdEmbeddedValue);
|
|
|
|
APAndId ap_and_ids[] = {
|
|
{.ap = llvm::APInt(MinAPWidth, 1)},
|
|
{.ap = llvm::APInt(MinAPWidth, 2)},
|
|
{.ap = llvm::APInt(MinAPWidth, 999'999'999'999)},
|
|
{.ap = llvm::APInt(MinAPWidth, std::numeric_limits<uint64_t>::max())},
|
|
{.ap = llvm::APInt(MinAPWidth + 1, std::numeric_limits<uint64_t>::max()) +
|
|
1},
|
|
{.ap = big_128_ap},
|
|
{.ap =
|
|
big_128_ap.zext(512) * big_128_ap.zext(512) * big_128_ap.zext(512)},
|
|
{.ap = max_embedded_ap},
|
|
{.ap = max_embedded_ap + 1},
|
|
};
|
|
for (auto& [ap, id] : ap_and_ids) {
|
|
id = ints.AddUnsigned(ap);
|
|
ASSERT_TRUE(id.has_value()) << ap;
|
|
}
|
|
|
|
for (const auto& [ap, id] : ap_and_ids) {
|
|
auto stored_ap = ints.Get(id);
|
|
// Pick a bit width wide enough to represent both whatever is returned and
|
|
// the original value as a *signed* integer without any truncation.
|
|
int width = std::max(stored_ap.getBitWidth(), ap.getBitWidth() + 1);
|
|
// We sign extend the stored value and zero extend the original number. This
|
|
// ensures that anything added as unsigned ends up stored as a positive
|
|
// number even when sign extended.
|
|
EXPECT_THAT(stored_ap.sext(width), Eq(ap.zext(width)));
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace Carbon::Testing
|