Skip to content

Commit 6c9a581

Browse files
authored
Switch GetExprCategory to be table-driven. (#6371)
Avoid using a large switch that needs to be manually extended when adding a new kind of instruction. Instead, the expression category for an instruction is now specified when defining the `InstKind`. In passing, add a distinct expression category value for patterns. This isn't used for much except some error checking at the moment, but it keeps the number of instructions that we need to manually classify as `NotExpr` despite having a type very low.
1 parent 2b30157 commit 6c9a581

File tree

9 files changed

+263
-271
lines changed

9 files changed

+263
-271
lines changed

toolchain/check/convert.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,6 +1625,7 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
16251625
current_category) {
16261626
case SemIR::ExprCategory::NotExpr:
16271627
case SemIR::ExprCategory::Mixed:
1628+
case SemIR::ExprCategory::Pattern:
16281629
CARBON_FATAL("Unexpected expression {0} after builtin conversions",
16291630
sem_ir.insts().Get(expr_id));
16301631

toolchain/check/cpp/type_mapping.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,9 @@ auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr* {
262262
case SemIR::ExprCategory::Error:
263263
return nullptr;
264264

265+
case SemIR::ExprCategory::Pattern:
266+
CARBON_FATAL("Passing a pattern as a function argument");
267+
265268
case SemIR::ExprCategory::DurableRef:
266269
value_kind = clang::ExprValueKind::VK_LValue;
267270
break;

toolchain/lower/file_context.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ auto FileContext::GetConstant(SemIR::ConstantId const_id,
232232

233233
case SemIR::ExprCategory::NotExpr:
234234
case SemIR::ExprCategory::Error:
235+
case SemIR::ExprCategory::Pattern:
235236
case SemIR::ExprCategory::Mixed:
236237
CARBON_FATAL("Unexpected category {0} for lowered constant {1}", cat,
237238
sem_ir().insts().Get(const_inst_id));

toolchain/lower/handle_aggregates.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ static auto GetAggregateElement(FunctionContext& context,
6464
switch (SemIR::GetExprCategory(context.sem_ir(), aggr_inst_id)) {
6565
case SemIR::ExprCategory::Error:
6666
case SemIR::ExprCategory::NotExpr:
67+
case SemIR::ExprCategory::Pattern:
6768
case SemIR::ExprCategory::Initializing:
6869
case SemIR::ExprCategory::Mixed:
6970
CARBON_FATAL(

toolchain/sem_ir/expr_info.cpp

Lines changed: 55 additions & 207 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,24 @@
44

55
#include "toolchain/sem_ir/expr_info.h"
66

7+
#include <concepts>
8+
79
#include "common/check.h"
810
#include "toolchain/base/kind_switch.h"
11+
#include "toolchain/sem_ir/ids.h"
12+
#include "toolchain/sem_ir/inst_kind.h"
913
#include "toolchain/sem_ir/typed_insts.h"
1014

1115
namespace Carbon::SemIR {
1216

17+
// Returns the InstId represented by an instruction operand.
18+
static auto AsAnyInstId(Inst::ArgAndKind arg) -> InstId {
19+
if (auto inst_id = arg.TryAs<SemIR::InstId>()) {
20+
return *inst_id;
21+
}
22+
return arg.As<SemIR::AbsoluteInstId>();
23+
}
24+
1325
auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
1426
const File* ir = &file;
1527

@@ -19,228 +31,64 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
1931

2032
while (true) {
2133
auto untyped_inst = ir->insts().Get(inst_id);
22-
CARBON_KIND_SWITCH(untyped_inst) {
23-
case AdaptDecl::Kind:
24-
case Assign::Kind:
25-
case BaseDecl::Kind:
26-
case Branch::Kind:
27-
case BranchIf::Kind:
28-
case BranchWithArg::Kind:
29-
case FieldDecl::Kind:
30-
case FunctionDecl::Kind:
31-
case ImplDecl::Kind:
32-
case NameBindingDecl::Kind:
33-
case Namespace::Kind:
34-
case OutParamPattern::Kind:
35-
case RefBindingPattern::Kind:
36-
case RefParamPattern::Kind:
37-
case RequireImplsDecl::Kind:
38-
case RequirementBaseFacetType::Kind:
39-
case RequirementEquivalent::Kind:
40-
case RequirementImpls::Kind:
41-
case RequirementRewrite::Kind:
42-
case Return::Kind:
43-
case ReturnSlotPattern::Kind:
44-
case ReturnExpr::Kind:
45-
case SymbolicBindingPattern::Kind:
46-
case TuplePattern::Kind:
47-
case ValueBindingPattern::Kind:
48-
case ValueParamPattern::Kind:
49-
case VarParamPattern::Kind:
50-
case VarPattern::Kind:
51-
return ExprCategory::NotExpr;
52-
53-
case ImportRefUnloaded::Kind:
54-
case ImportRefLoaded::Kind: {
55-
auto import_ir_inst = ir->import_ir_insts().Get(
56-
untyped_inst.As<AnyImportRef>().import_ir_inst_id);
57-
ir = ir->import_irs().Get(import_ir_inst.ir_id()).sem_ir;
58-
inst_id = import_ir_inst.inst_id();
59-
continue;
60-
}
61-
62-
case CARBON_KIND(AsCompatible inst): {
63-
inst_id = inst.source_id;
64-
continue;
65-
}
34+
auto category_from_kind = untyped_inst.kind().expr_category();
6635

67-
case CARBON_KIND(AliasBinding inst): {
68-
inst_id = inst.value_id;
69-
continue;
70-
}
71-
case CARBON_KIND(ExportDecl inst): {
72-
inst_id = inst.value_id;
73-
continue;
74-
}
75-
case CARBON_KIND(NameRef inst): {
76-
inst_id = inst.value_id;
77-
continue;
78-
}
79-
80-
case CARBON_KIND(Converted inst): {
81-
inst_id = inst.result_id;
82-
continue;
83-
}
84-
85-
case CARBON_KIND(ImplWitnessAssociatedConstant inst): {
86-
inst_id = inst.inst_id;
87-
continue;
88-
}
89-
90-
case CARBON_KIND(SpecificConstant inst): {
91-
inst_id = inst.inst_id;
92-
continue;
93-
}
94-
95-
case AccessMemberAction::Kind:
96-
case AccessOptionalMemberAction::Kind:
97-
case AddrOf::Kind:
98-
case ArrayType::Kind:
99-
case AssociatedConstantDecl::Kind:
100-
case AssociatedEntity::Kind:
101-
case AssociatedEntityType::Kind:
102-
case AutoType::Kind:
103-
case SymbolicBinding::Kind:
104-
case AcquireValue::Kind:
105-
case ValueBinding::Kind:
106-
case BlockArg::Kind:
107-
case BoolLiteral::Kind:
108-
case BoolType::Kind:
109-
case BoundMethod::Kind:
110-
case BoundMethodType::Kind:
111-
case CharLiteralType::Kind:
112-
case CharLiteralValue::Kind:
113-
case ClassDecl::Kind:
114-
case ClassType::Kind:
115-
case CompleteTypeWitness::Kind:
116-
case ConstType::Kind:
117-
case ConvertToValueAction::Kind:
118-
case CppOverloadSetType::Kind:
119-
case CppOverloadSetValue::Kind:
120-
case CustomLayoutType::Kind:
121-
case FacetAccessType::Kind:
122-
case FacetType::Kind:
123-
case FacetValue::Kind:
124-
case FloatLiteralType::Kind:
125-
case FloatLiteralValue::Kind:
126-
case FloatType::Kind:
127-
case FloatValue::Kind:
128-
case FunctionType::Kind:
129-
case FunctionTypeWithSelfType::Kind:
130-
case GenericClassType::Kind:
131-
case GenericInterfaceType::Kind:
132-
case GenericNamedConstraintType::Kind:
133-
case LookupImplWitness::Kind:
134-
case ImplWitness::Kind:
135-
case ImplWitnessAccess::Kind:
136-
case ImplWitnessAccessSubstituted::Kind:
137-
case ImplWitnessTable::Kind:
138-
case ImportCppDecl::Kind:
139-
case ImportDecl::Kind:
140-
case InstType::Kind:
141-
case InstValue::Kind:
142-
case IntLiteralType::Kind:
143-
case IntType::Kind:
144-
case IntValue::Kind:
145-
case InterfaceDecl::Kind:
146-
case MaybeUnformedType::Kind:
147-
case NamedConstraintDecl::Kind:
148-
case NamespaceType::Kind:
149-
case PartialType::Kind:
150-
case PatternType::Kind:
151-
case PointerType::Kind:
152-
case RefineTypeAction::Kind:
153-
case RequireCompleteType::Kind:
154-
case SpecificFunction::Kind:
155-
case SpecificFunctionType::Kind:
156-
case SpecificImplFunction::Kind:
157-
case StringLiteral::Kind:
158-
case StructType::Kind:
159-
case StructValue::Kind:
160-
case SymbolicBindingType::Kind:
161-
case TupleType::Kind:
162-
case TupleValue::Kind:
163-
case TypeOfInst::Kind:
164-
case TypeType::Kind:
165-
case UnaryOperatorNot::Kind:
166-
case UnboundElementType::Kind:
167-
case UninitializedValue::Kind:
168-
case ValueOfInitializer::Kind:
169-
case ValueParam::Kind:
170-
case VtableType::Kind:
171-
case WhereExpr::Kind:
172-
case WitnessType::Kind:
173-
return value_category;
174-
175-
case ErrorInst::Kind:
176-
return ExprCategory::Error;
177-
178-
case CARBON_KIND(ArrayIndex inst): {
179-
inst_id = inst.array_id;
180-
continue;
181-
}
182-
183-
case VtablePtr::Kind:
184-
case VtableDecl::Kind:
185-
return ExprCategory::EphemeralRef;
36+
// If this instruction kind has a fixed category, return it.
37+
if (auto fixed_category = category_from_kind.TryAsFixedCategory()) {
38+
return *fixed_category == ExprCategory::Value ? value_category
39+
: *fixed_category;
40+
}
18641

187-
case CARBON_KIND(ClassElementAccess inst): {
42+
// Handle any special cases that use
43+
// ComputedExprCategory::DependsOnOperands.
44+
auto handle_special_case = [&]<typename TypedInstT>(TypedInstT inst) {
45+
if constexpr (std::same_as<TypedInstT, ClassElementAccess>) {
18846
inst_id = inst.base_id;
18947
// A value of class type is a pointer to an object representation.
19048
// Therefore, if the base is a value, the result is an ephemeral
19149
// reference.
19250
value_category = ExprCategory::EphemeralRef;
193-
continue;
51+
} else if constexpr (std::same_as<TypedInstT, ImportRefLoaded> ||
52+
std::same_as<TypedInstT, ImportRefUnloaded>) {
53+
auto import_ir_inst = ir->import_ir_insts().Get(inst.import_ir_inst_id);
54+
ir = ir->import_irs().Get(import_ir_inst.ir_id()).sem_ir;
55+
inst_id = import_ir_inst.inst_id();
56+
} else {
57+
static_assert(
58+
TypedInstT::Kind.expr_category().TryAsComputedCategory() !=
59+
ComputedExprCategory::DependsOnOperands,
60+
"Missing expression category computation for type");
19461
}
62+
};
19563

196-
case CARBON_KIND(StructAccess inst): {
197-
inst_id = inst.struct_id;
198-
continue;
64+
// If the category depends on the operands of the instruction, determine it.
65+
// Usually this means the category is the same as the category of an
66+
// operand.
67+
switch (*category_from_kind.TryAsComputedCategory()) {
68+
case ComputedExprCategory::ValueIfHasType: {
69+
return untyped_inst.kind().has_type() ? value_category
70+
: ExprCategory::NotExpr;
19971
}
20072

201-
case CARBON_KIND(TupleAccess inst): {
202-
inst_id = inst.tuple_id;
203-
continue;
73+
case ComputedExprCategory::SameAsFirstOperand: {
74+
inst_id = AsAnyInstId(untyped_inst.arg0_and_kind());
75+
break;
20476
}
20577

206-
case CARBON_KIND(SpliceBlock inst): {
207-
inst_id = inst.result_id;
208-
continue;
78+
case ComputedExprCategory::SameAsSecondOperand: {
79+
inst_id = AsAnyInstId(untyped_inst.arg1_and_kind());
80+
break;
20981
}
21082

211-
case SpliceInst::Kind:
212-
// TODO: Add ExprCategory::Dependent.
213-
return value_category;
214-
215-
case StructLiteral::Kind:
216-
case TupleLiteral::Kind:
217-
return ExprCategory::Mixed;
218-
219-
case ArrayInit::Kind:
220-
case Call::Kind:
221-
case InitializeFrom::Kind:
222-
case InPlaceInit::Kind:
223-
case ClassInit::Kind:
224-
case StructInit::Kind:
225-
case TupleInit::Kind:
226-
return ExprCategory::Initializing;
227-
228-
case RefBinding::Kind:
229-
case Deref::Kind:
230-
case VarStorage::Kind:
231-
case ReturnSlot::Kind:
232-
return ExprCategory::DurableRef;
233-
234-
case Temporary::Kind:
235-
case TemporaryStorage::Kind:
236-
case ValueAsRef::Kind:
237-
return ExprCategory::EphemeralRef;
238-
239-
case OutParam::Kind:
240-
case RefParam::Kind:
241-
// TODO: Consider introducing a separate category for OutParam:
242-
// unlike other DurableRefs, it permits initialization.
243-
return ExprCategory::DurableRef;
83+
case ComputedExprCategory::DependsOnOperands: {
84+
switch (untyped_inst.kind()) {
85+
#define CARBON_SEM_IR_INST_KIND(TypedInstT) \
86+
case TypedInstT::Kind: \
87+
handle_special_case(untyped_inst.As<TypedInstT>()); \
88+
break;
89+
#include "toolchain/sem_ir/inst_kind.def"
90+
}
91+
}
24492
}
24593
}
24694
}

toolchain/sem_ir/expr_info.h

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,10 @@
99

1010
#include "toolchain/sem_ir/file.h"
1111
#include "toolchain/sem_ir/ids.h"
12+
#include "toolchain/sem_ir/inst_kind.h"
1213

1314
namespace Carbon::SemIR {
1415

15-
// The expression category of a sem_ir instruction. See /docs/design/values.md
16-
// for details.
17-
enum class ExprCategory : int8_t {
18-
// This instruction does not correspond to an expression, and as such has no
19-
// category.
20-
NotExpr,
21-
// The category of this instruction is not known due to an error.
22-
Error,
23-
// This instruction represents a value expression.
24-
Value,
25-
// This instruction represents a durable reference expression, that denotes an
26-
// object that outlives the current full expression context.
27-
DurableRef,
28-
// This instruction represents an ephemeral reference expression, that denotes
29-
// an object that does not outlive the current full expression context.
30-
EphemeralRef,
31-
// This instruction represents an initializing expression, that describes how
32-
// to initialize an object.
33-
Initializing,
34-
// This instruction represents a syntactic combination of expressions that are
35-
// permitted to have different expression categories. This is used for tuple
36-
// and struct literals, where the subexpressions for different elements can
37-
// have different categories.
38-
Mixed,
39-
Last = Mixed
40-
};
41-
4216
// Returns the expression category for an instruction.
4317
auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory;
4418

toolchain/sem_ir/formatter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,7 @@ auto Formatter::FormatInstLhs(InstId inst_id, Inst inst) -> void {
10021002
case ExprCategory::NotExpr:
10031003
case ExprCategory::Error:
10041004
case ExprCategory::Value:
1005+
case ExprCategory::Pattern:
10051006
case ExprCategory::Mixed:
10061007
break;
10071008
case ExprCategory::DurableRef:

0 commit comments

Comments
 (0)