Use an x-macro for special NameId values. (#5018)

Doing an in-file X-macro, though maybe we'll want to move it out to a
#include if we keep piling on more. I know there's `destroy` to add, and
possibly `copy` and `move`, but I don't know what threshold we'll want
for a separate file.

Note this does set up for an advantage where we can `switch` instead of
repeated `if` for special names, shifting to compile errors for new
values.

I also considered a simpler enum approach (with an implicit conversion
to `NameId`) but that runs into issues with things like
`Id::Kind::For<...>` as a consequence of calls like `name_id ==
special_name_id` (just requires another `operator==`) and
`context.node_stack().Push(node_id, SemIR::NameId::SelfType);` (a little
more complex how we'd want to handle it).
This commit is contained in:
Jon Ross-Perkins
2025-02-26 02:23:22 +00:00
committed by GitHub
parent 29c1f552c7
commit 9d85b23b4b
3 changed files with 95 additions and 70 deletions
+13 -19
View File
@@ -77,6 +77,9 @@ auto IntKind::Print(llvm::raw_ostream& out) const -> void {
}
}
// Double-check the special value mapping and constexpr evaluation.
static_assert(NameId::SpecialNameId::Vptr == *NameId::Vptr.AsSpecialNameId());
auto NameId::ForIdentifier(IdentifierId id) -> NameId {
if (id.index >= 0) {
return NameId(id.index);
@@ -105,25 +108,16 @@ auto NameId::Print(llvm::raw_ostream& out) const -> void {
return;
}
out << Label << "(";
if (*this == Base) {
out << "Base";
} else if (*this == Core) {
out << "Core";
} else if (*this == PackageNamespace) {
out << "PackageNamespace";
} else if (*this == PeriodSelf) {
out << "PeriodSelf";
} else if (*this == ReturnSlot) {
out << "ReturnSlot";
} else if (*this == SelfType) {
out << "SelfType";
} else if (*this == SelfValue) {
out << "SelfValue";
} else if (*this == Vptr) {
out << "Vptr";
} else {
CARBON_FATAL("Unknown index {0}", index);
IdBase::Print(out);
auto special_name_id = AsSpecialNameId();
CARBON_CHECK(special_name_id, "Unknown index {0}", index);
switch (*special_name_id) {
#define CARBON_SPECIAL_NAME_ID_FOR_PRINT(Name) \
case SpecialNameId::Name: \
out << #Name; \
break;
CARBON_SPECIAL_NAME_ID(CARBON_SPECIAL_NAME_ID_FOR_PRINT)
#undef CARBON_SPECIAL_NAME_ID_FOR_PRINT
}
out << ")";
}
+59 -30
View File
@@ -481,6 +481,31 @@ struct FloatKind : public IdBase<FloatKind> {
auto Print(llvm::raw_ostream& out) const -> void { out << "float"; }
};
// An X-macro for special names. Uses should look like:
//
// #define CARBON_SPECIAL_NAME_ID_FOR_XYZ(Name) ...
// CARBON_SPECIAL_NAME_ID(CARBON_SPECIAL_NAME_ID_FOR_XYZ)
// #undef CARBON_SPECIAL_NAME_ID_FOR_XYZ
#define CARBON_SPECIAL_NAME_ID(X) \
/* The name of `base`. */ \
X(Base) \
/* The name of the discriminant field (if any) in a choice. */ \
X(ChoiceDiscriminant) \
/* The name of the package `Core`. */ \
X(Core) \
/* The name of `package`. */ \
X(PackageNamespace) \
/* The name of `.Self`. */ \
X(PeriodSelf) \
/* The name of the return slot in a function. */ \
X(ReturnSlot) \
/* The name of `Self`. */ \
X(SelfType) \
/* The name of `self`. */ \
X(SelfValue) \
/* The name of `vptr`. */ \
X(Vptr)
// The ID of a name. A name is either a string or a special name such as
// `self`, `Self`, or `base`.
struct NameId : public IdBase<NameId> {
@@ -491,24 +516,19 @@ struct NameId : public IdBase<NameId> {
// An ID with no value.
static const NameId None;
// The name of `base`.
static const NameId Base;
// The name of the package `Core`.
static const NameId Core;
// The name of `package`.
static const NameId PackageNamespace;
// The name of `.Self`.
static const NameId PeriodSelf;
// The name of the return slot in a function.
static const NameId ReturnSlot;
// The name of `Self`.
static const NameId SelfType;
// The name of `self`.
static const NameId SelfValue;
// The name of `vptr`.
static const NameId Vptr;
// The name of the discriminant field (if any) in a choice.
static const NameId ChoiceDiscriminant;
// An enum of special names.
enum class SpecialNameId : uint8_t {
#define CARBON_SPECIAL_NAME_ID_FOR_ENUM(Name) Name,
CARBON_SPECIAL_NAME_ID(CARBON_SPECIAL_NAME_ID_FOR_ENUM)
#undef CARBON_SPECIAL_NAME_ID_FOR_ENUM
};
// For each SpecialNameId, provide a matching `NameId` instance for
// convenience.
#define CARBON_SPECIAL_NAME_ID_FOR_DECL(Name) static const NameId Name;
CARBON_SPECIAL_NAME_ID(CARBON_SPECIAL_NAME_ID_FOR_DECL)
#undef CARBON_SPECIAL_NAME_ID_FOR_DECL
// The number of non-index (<0) that exist, and will need storage in name
// lookup.
@@ -529,22 +549,31 @@ struct NameId : public IdBase<NameId> {
return index >= 0 ? IdentifierId(index) : IdentifierId::None;
}
// Expose special names for `switch`.
constexpr auto AsSpecialNameId() const -> std::optional<SpecialNameId> {
if (index >= NoneIndex) {
return std::nullopt;
}
return static_cast<SpecialNameId>(NoneIndex - 1 - index);
}
auto Print(llvm::raw_ostream& out) const -> void;
};
constexpr NameId NameId::None = NameId(NoneIndex);
constexpr NameId NameId::Base = NameId(NoneIndex - 1);
constexpr NameId NameId::Core = NameId(NoneIndex - 2);
constexpr NameId NameId::PackageNamespace = NameId(NoneIndex - 3);
constexpr NameId NameId::PeriodSelf = NameId(NoneIndex - 4);
constexpr NameId NameId::ReturnSlot = NameId(NoneIndex - 5);
constexpr NameId NameId::SelfType = NameId(NoneIndex - 6);
constexpr NameId NameId::SelfValue = NameId(NoneIndex - 7);
constexpr NameId NameId::Vptr = NameId(NoneIndex - 8);
constexpr NameId NameId::ChoiceDiscriminant = NameId(NoneIndex - 9);
constexpr int NameId::NonIndexValueCount = 10;
// Enforce the link between SpecialValueCount and the last special value.
static_assert(NameId::NonIndexValueCount == -NameId::ChoiceDiscriminant.index);
// Define the special `static const NameId` values.
#define CARBON_SPECIAL_NAME_ID_FOR_DEF(Name) \
constexpr NameId NameId::Name = \
NameId(NoneIndex - 1 - static_cast<int>(NameId::SpecialNameId::Name));
CARBON_SPECIAL_NAME_ID(CARBON_SPECIAL_NAME_ID_FOR_DEF)
#undef CARBON_SPECIAL_NAME_ID_FOR_DEF
// Count non-index values, including `None` and special names.
#define CARBON_SPECIAL_NAME_ID_FOR_COUNT(...) +1
constexpr int NameId::NonIndexValueCount =
1 CARBON_SPECIAL_NAME_ID(CARBON_SPECIAL_NAME_ID_FOR_COUNT);
#undef CARBON_SPECIAL_NAME_ID_FOR_COUNT
// The ID of a name scope.
struct NameScopeId : public IdBase<NameScopeId> {
+23 -21
View File
@@ -10,29 +10,31 @@ namespace Carbon::SemIR {
// Get the spelling to use for a special name.
static auto GetSpecialName(NameId name_id, bool for_ir) -> llvm::StringRef {
switch (name_id.index) {
case NameId::None.index:
return for_ir ? "" : "<none>";
case NameId::Base.index:
if (name_id == NameId::None) {
return for_ir ? "" : "<none>";
}
auto special_name_id = name_id.AsSpecialNameId();
CARBON_CHECK(special_name_id, "Not a special name");
switch (*special_name_id) {
case NameId::SpecialNameId::Base:
return "base";
case NameId::Core.index:
return "Core";
case NameId::PeriodSelf.index:
return ".Self";
case NameId::ReturnSlot.index:
return for_ir ? "return" : "<return slot>";
case NameId::PackageNamespace.index:
return "package";
case NameId::SelfType.index:
return "Self";
case NameId::SelfValue.index:
return "self";
case NameId::Vptr.index:
return for_ir ? "vptr" : "<vptr>";
case NameId::ChoiceDiscriminant.index:
case NameId::SpecialNameId::ChoiceDiscriminant:
return "discriminant";
default:
CARBON_FATAL("Unknown special name");
case NameId::SpecialNameId::Core:
return "Core";
case NameId::SpecialNameId::PackageNamespace:
return "package";
case NameId::SpecialNameId::PeriodSelf:
return ".Self";
case NameId::SpecialNameId::ReturnSlot:
return for_ir ? "return" : "<return slot>";
case NameId::SpecialNameId::SelfType:
return "Self";
case NameId::SpecialNameId::SelfValue:
return "self";
case NameId::SpecialNameId::Vptr:
return for_ir ? "vptr" : "<vptr>";
}
}