Skip to content

Commit 6d559ae

Browse files
committed
entity-name-binding
1 parent 2d1de16 commit 6d559ae

File tree

13 files changed

+220
-114
lines changed

13 files changed

+220
-114
lines changed

toolchain/check/cpp/import.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,13 +1423,15 @@ static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
14231423
SemIR::LocId param_loc_id =
14241424
AddImportIRInst(context.sem_ir(), param->getLocation());
14251425

1426-
// TODO: Fix this once templates are supported.
1427-
bool is_template = false;
1428-
// TODO: Fix this once generics are supported.
1429-
bool is_generic = false;
1426+
auto entity_name_id = context.entity_names().AddSymbolicBindingName(
1427+
name_id, context.scope_stack().PeekNameScopeId(),
1428+
// TODO: Fix this once generics are supported.
1429+
SemIR::CompileTimeBindIndex::None,
1430+
// TODO: Fix this once templates are supported.
1431+
/*is_template=*/false);
14301432
SemIR::InstId pattern_id =
1431-
AddBindingPattern(context, param_loc_id, name_id, type_id,
1432-
type_expr_region_id, is_generic, is_template)
1433+
AddBindingPatternWithEntityName(context, param_loc_id, entity_name_id,
1434+
type_id, type_expr_region_id)
14331435
.pattern_id;
14341436
pattern_id = AddPatternInst(
14351437
context, {param_loc_id,

toolchain/check/handle_binding_pattern.cpp

Lines changed: 122 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -30,40 +30,31 @@ auto HandleParseNode(Context& context, Parse::UnderscoreNameId node_id)
3030

3131
// TODO: make this function shorter by factoring pieces out.
3232
static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
33+
Parse::NodeId name_node,
34+
SemIR::EntityNameId entity_name_id,
35+
Parse::NodeId type_node,
36+
SemIR::InstId parsed_type_inst_id,
3337
Parse::NodeKind node_kind) -> bool {
3438
// TODO: split this into smaller, more focused functions.
35-
auto [type_node, parsed_type_id] = context.node_stack().PopExprWithNodeId();
3639
auto [cast_type_inst_id, cast_type_id] =
37-
ExprAsType(context, type_node, parsed_type_id);
40+
ExprAsType(context, type_node, parsed_type_inst_id);
3841

3942
SemIR::ExprRegionId type_expr_region_id =
4043
EndSubpatternAsExpr(context, cast_type_inst_id);
4144

42-
// The name in a template binding may be wrapped in `template`.
43-
bool is_generic = node_kind == Parse::NodeKind::CompileTimeBindingPattern;
44-
auto is_template =
45-
context.node_stack()
46-
.PopAndDiscardSoloNodeIdIf<Parse::NodeKind::TemplateBindingName>();
47-
// A non-generic template binding is diagnosed by the parser.
48-
is_template &= is_generic;
49-
50-
auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
45+
const auto& entity_name = context.entity_names().Get(entity_name_id);
46+
auto name_id = entity_name.name_id;
47+
CARBON_CHECK((node_kind == Parse::NodeKind::CompileTimeBindingPattern) ==
48+
entity_name.bind_index().has_value());
5149

5250
const DeclIntroducerState& introducer =
5351
context.decl_introducer_state_stack().innermost();
5452

5553
auto make_binding_pattern = [&]() -> SemIR::InstId {
5654
// TODO: Eventually the name will need to support associations with other
5755
// scopes, but right now we don't support qualified names here.
58-
auto binding =
59-
AddBindingPattern(context, name_node, name_id, cast_type_id,
60-
type_expr_region_id, is_generic, is_template);
61-
62-
// TODO: If `is_generic`, then `binding.bind_id is a BindSymbolicName. Subst
63-
// the `.Self` of type `type` in the `cast_type_id` type (a `FacetType`)
64-
// with the `binding.bind_id` itself, and build a new pattern with that.
65-
// This is kind of cyclical. So we need to reuse the EntityNameId, which
66-
// will also reuse the CompileTimeBinding for the new BindSymbolicName.
56+
auto binding = AddBindingPatternWithEntityName(
57+
context, name_node, entity_name_id, cast_type_id, type_expr_region_id);
6758

6859
if (name_id != SemIR::NameId::Underscore) {
6960
// Add name to lookup immediately, so it can be used in the rest of the
@@ -100,7 +91,8 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
10091
case Lex::TokenKind::Fn: {
10192
if (context.full_pattern_stack().CurrentKind() ==
10293
FullPatternStack::Kind::ImplicitParamList &&
103-
!(is_generic || name_id == SemIR::NameId::SelfValue)) {
94+
(node_kind != Parse::NodeKind::CompileTimeBindingPattern &&
95+
name_id != SemIR::NameId::SelfValue)) {
10496
CARBON_DIAGNOSTIC(
10597
ImplictParamMustBeConstant, Error,
10698
"implicit parameters of functions must be constant or `self`");
@@ -130,7 +122,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
130122
"`self` parameter only allowed on functions");
131123
context.emitter().Emit(node_id, SelfParameterNotAllowed);
132124
had_error = true;
133-
} else if (!is_generic) {
125+
} else if (node_kind != Parse::NodeKind::CompileTimeBindingPattern) {
134126
CARBON_DIAGNOSTIC(GenericParamMustBeConstant, Error,
135127
"parameters of generic types must be constant");
136128
context.emitter().Emit(node_id, GenericParamMustBeConstant);
@@ -149,6 +141,12 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
149141
// Replace the parameter with `ErrorInst` so that we don't try
150142
// constructing a generic based on it.
151143
result_inst_id = SemIR::ErrorInst::InstId;
144+
if (node_kind == Parse::NodeKind::CompileTimeBindingPattern) {
145+
// Push an error for the EntityName's binding index, since we're not
146+
// constructing an entity with make_binding_pattern().
147+
context.scope_stack().PushCompileTimeBinding(
148+
SemIR::ErrorInst::InstId);
149+
}
152150
} else {
153151
result_inst_id = make_binding_pattern();
154152
if (node_kind == Parse::NodeKind::LetBindingPattern) {
@@ -193,8 +191,6 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
193191
}
194192
auto binding_pattern_id = make_binding_pattern();
195193
if (node_kind == Parse::NodeKind::VarBindingPattern) {
196-
CARBON_CHECK(!is_generic);
197-
198194
if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Returned)) {
199195
// TODO: Should we check this for the `var` as a whole, rather than
200196
// for the name binding?
@@ -216,23 +212,93 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
216212

217213
auto HandleParseNode(Context& context, Parse::LetBindingPatternId node_id)
218214
-> bool {
219-
return HandleAnyBindingPattern(context, node_id,
215+
auto [type_node, type_inst_id] = context.node_stack().PopExprWithNodeId();
216+
217+
// `template` is incorrect here, but is diagnosed in parse.
218+
context.node_stack()
219+
.PopAndDiscardSoloNodeIdIf<Parse::NodeKind::TemplateBindingName>();
220+
221+
auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
222+
auto entity_name_id = context.entity_names().Add(
223+
{.name_id = name_id,
224+
.parent_scope_id = context.scope_stack().PeekNameScopeId()});
225+
226+
return HandleAnyBindingPattern(context, node_id, name_node, entity_name_id,
227+
type_node, type_inst_id,
220228
Parse::NodeKind::LetBindingPattern);
221229
}
222230

223231
auto HandleParseNode(Context& context, Parse::VarBindingPatternId node_id)
224232
-> bool {
225-
return HandleAnyBindingPattern(context, node_id,
233+
auto [type_node, type_inst_id] = context.node_stack().PopExprWithNodeId();
234+
235+
// `template` is incorrect here, but is diagnosed in parse.
236+
context.node_stack()
237+
.PopAndDiscardSoloNodeIdIf<Parse::NodeKind::TemplateBindingName>();
238+
239+
auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
240+
auto entity_name_id = context.entity_names().Add(
241+
{.name_id = name_id,
242+
.parent_scope_id = context.scope_stack().PeekNameScopeId()});
243+
244+
return HandleAnyBindingPattern(context, node_id, name_node, entity_name_id,
245+
type_node, type_inst_id,
226246
Parse::NodeKind::VarBindingPattern);
227247
}
228248

229249
auto HandleParseNode(Context& context,
230250
Parse::CompileTimeBindingPatternStartId node_id) -> bool {
231-
// Make a scope to contain the `.Self` facet value for use in the type of the
232-
// compile time binding. This is popped when handling the
233-
// CompileTimeBindingPatternId.
234-
context.scope_stack().PushForSameRegion();
251+
auto is_template =
252+
context.node_stack()
253+
.PopAndDiscardSoloNodeIdIf<Parse::NodeKind::TemplateBindingName>();
254+
255+
auto name_id = context.node_stack().PeekNameId();
256+
auto entity_name_id = SemIR::EntityNameId::None;
257+
258+
const DeclIntroducerState& introducer =
259+
context.decl_introducer_state_stack().innermost();
260+
if (introducer.kind == Lex::TokenKind::Let) {
261+
// Disallow `let` outside of function and interface definitions.
262+
// TODO: Find a less brittle way of doing this. A `scope_inst_id` of `None`
263+
// can represent a block scope, but is also used for other kinds of scopes
264+
// that aren't necessarily part of a function decl.
265+
// We don't need to check if the scope is an interface here as this is
266+
// already caught in the parse phase by the separated associated constant
267+
// logic.
268+
auto scope_inst_id = context.scope_stack().PeekInstId();
269+
if (scope_inst_id.has_value()) {
270+
auto scope_inst = context.insts().Get(scope_inst_id);
271+
if (!scope_inst.Is<SemIR::FunctionDecl>()) {
272+
context.TODO(
273+
node_id,
274+
"`let` compile time binding outside function or interface");
275+
entity_name_id = context.entity_names().Add(
276+
{.name_id = name_id,
277+
.parent_scope_id = context.scope_stack().PeekNameScopeId()});
278+
}
279+
}
280+
}
281+
282+
// This compile time binding is added but not pushed because we don't have the
283+
// BindSymbolicName for it until we get to the CompileTimeBindingPatternId
284+
// node. This leaves the CompileTimeBinding stack in a fragile state while the
285+
// binding's facet type is checked, but only the index is used during type
286+
// checking. The instruction wouldn't be needed until building a generic,
287+
// which we don't do in this scope.
288+
//
289+
// We avoid making a CompileTimeBinding in the case where a compile-time
290+
// binding is disallowed. In that case, we already constructed an
291+
// `entity_name_id` above.
292+
if (!entity_name_id.has_value()) {
293+
auto bind_index = context.scope_stack().AddCompileTimeBinding();
294+
entity_name_id = context.entity_names().AddSymbolicBindingName(
295+
name_id, context.scope_stack().PeekNameScopeId(), bind_index,
296+
is_template);
297+
}
235298

299+
// TODO: Construct a SymbolicBindingType for `entity_name_id` instead of a
300+
// BindSymbolicName for a `PeriodSelf` EntityName.
301+
//
236302
// The `.Self` must have a type of `FacetType`, so that it gets wrapped in
237303
// `FacetAccessType` when used in a type position, such as in `U:! I(.Self)`.
238304
// This allows substitution with other facet values without requiring an
@@ -244,7 +310,13 @@ auto HandleParseNode(Context& context,
244310
{.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id});
245311
auto type_id = context.types().GetTypeIdForTypeConstantId(const_id);
246312

313+
// Make a scope to contain the `.Self` facet value for use in the type of the
314+
// compile time binding. This is popped when handling the
315+
// CompileTimeBindingPatternId.
316+
context.scope_stack().PushForSameRegion();
247317
MakePeriodSelfFacetValue(context, type_id);
318+
319+
context.node_stack().Push(node_id, entity_name_id);
248320
return true;
249321
}
250322

@@ -254,30 +326,29 @@ auto HandleParseNode(Context& context,
254326
// CompileTimeBindingPatternStart.
255327
context.scope_stack().Pop();
256328

329+
auto [type_node, type_inst_id] = context.node_stack().PopExprWithNodeId();
330+
331+
auto entity_name_id =
332+
context.node_stack()
333+
.Pop<Parse::NodeKind::CompileTimeBindingPatternStart>();
334+
335+
// The NameId was already used to construct the `entity_name_id`.
336+
auto [name_node, _] = context.node_stack().PopNameWithNodeId();
337+
257338
auto node_kind = Parse::NodeKind::CompileTimeBindingPattern;
258-
const DeclIntroducerState& introducer =
259-
context.decl_introducer_state_stack().innermost();
260-
if (introducer.kind == Lex::TokenKind::Let) {
261-
// Disallow `let` outside of function and interface definitions.
262-
// TODO: Find a less brittle way of doing this. A `scope_inst_id` of `None`
263-
// can represent a block scope, but is also used for other kinds of scopes
264-
// that aren't necessarily part of a function decl.
265-
// We don't need to check if the scope is an interface here as this is
266-
// already caught in the parse phase by the separated associated constant
267-
// logic.
268-
auto scope_inst_id = context.scope_stack().PeekInstId();
269-
if (scope_inst_id.has_value()) {
270-
auto scope_inst = context.insts().Get(scope_inst_id);
271-
if (!scope_inst.Is<SemIR::FunctionDecl>()) {
272-
context.TODO(
273-
node_id,
274-
"`let` compile time binding outside function or interface");
275-
node_kind = Parse::NodeKind::LetBindingPattern;
276-
}
277-
}
339+
if (!context.entity_names().Get(entity_name_id).bind_index().has_value()) {
340+
// This indicates that CompileTimeBindingPatternStartId found a `let` in an
341+
// incorrect scope. We treat them as runtime bindings instead of compile
342+
// time bindings, since we avoided introducing a CompileTimeBindingIndex
343+
// that won't be used.
344+
node_kind = Parse::NodeKind::LetBindingPattern;
278345
}
279346

280-
return HandleAnyBindingPattern(context, node_id, node_kind);
347+
auto success =
348+
HandleAnyBindingPattern(context, node_id, name_node, entity_name_id,
349+
type_node, type_inst_id, node_kind);
350+
351+
return success;
281352
}
282353

283354
auto HandleParseNode(Context& context,

toolchain/check/node_stack.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ class NodeStack {
200200
return PopWithNodeId<SemIR::NameId>();
201201
}
202202

203+
// Peeks a name from the top of the stack.
204+
auto PeekNameId() const -> SemIR::NameId;
205+
203206
// Pops the top of the stack and returns the node_id and the ID.
204207
template <const Parse::NodeKind& RequiredParseKind>
205208
auto PopWithNodeId() -> auto {
@@ -442,6 +445,8 @@ class NodeStack {
442445
case Parse::NodeKind::DefaultLibrary:
443446
case Parse::NodeKind::LibraryName:
444447
return Id::KindFor<SemIR::LibraryNameId>();
448+
case Parse::NodeKind::CompileTimeBindingPatternStart:
449+
return Id::KindFor<SemIR::EntityNameId>();
445450
case Parse::NodeKind::BuiltinName:
446451
case Parse::NodeKind::ChoiceIntroducer:
447452
case Parse::NodeKind::ClassIntroducer:
@@ -481,7 +486,6 @@ class NodeStack {
481486
case Parse::NodeKind::CallExprComma:
482487
case Parse::NodeKind::ChoiceAlternativeListComma:
483488
case Parse::NodeKind::CodeBlock:
484-
case Parse::NodeKind::CompileTimeBindingPatternStart:
485489
case Parse::NodeKind::ContinueStatementStart:
486490
case Parse::NodeKind::CorePackageName:
487491
case Parse::NodeKind::ExportIntroducer:
@@ -640,6 +644,10 @@ inline auto NodeStack::PopExprWithNodeId()
640644
return PopWithNodeId<Parse::NodeCategory::Expr>();
641645
}
642646

647+
inline auto NodeStack::PeekNameId() const -> SemIR::NameId {
648+
return Peek<Id::KindFor<SemIR::NameId>()>();
649+
}
650+
643651
inline auto NodeStack::PeekPattern() const -> SemIR::InstId {
644652
return Peek<Id::KindFor<SemIR::InstId>()>();
645653
}

toolchain/check/pattern.cpp

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,22 @@ auto EndSubpatternAsNonExpr(Context& context) -> void {
4848

4949
auto AddBindingPattern(Context& context, SemIR::LocId name_loc,
5050
SemIR::NameId name_id, SemIR::TypeId type_id,
51-
SemIR::ExprRegionId type_region_id, bool is_generic,
52-
bool is_template) -> BindingPatternInfo {
53-
auto entity_name_id = context.entity_names().AddSymbolicBindingName(
54-
name_id, context.scope_stack().PeekNameScopeId(),
55-
is_generic ? context.scope_stack().AddCompileTimeBinding()
56-
: SemIR::CompileTimeBindIndex::None,
57-
is_template);
51+
SemIR::ExprRegionId type_region_id)
52+
-> BindingPatternInfo {
53+
auto entity_name_id = context.entity_names().Add(
54+
{.name_id = name_id,
55+
.parent_scope_id = context.scope_stack().PeekNameScopeId()});
56+
return AddBindingPatternWithEntityName(context, name_loc, entity_name_id,
57+
type_id, type_region_id);
58+
}
59+
60+
auto AddBindingPatternWithEntityName(Context& context, SemIR::LocId name_loc,
61+
SemIR::EntityNameId entity_name_id,
62+
SemIR::TypeId type_id,
63+
SemIR::ExprRegionId type_region_id)
64+
-> BindingPatternInfo {
65+
auto& entity_name = context.entity_names().Get(entity_name_id);
66+
bool is_generic = entity_name.bind_index().has_value();
5867

5968
auto bind_id = SemIR::InstId::None;
6069
if (is_generic) {
@@ -138,8 +147,7 @@ auto AddSelfParamPattern(Context& context, SemIR::LocId loc_id,
138147
SemIR::TypeId type_id) -> SemIR::InstId {
139148
SemIR::InstId pattern_id =
140149
AddBindingPattern(context, loc_id, SemIR::NameId::SelfValue, type_id,
141-
type_expr_region_id, /*is_generic=*/false,
142-
/*is_template=*/false)
150+
type_expr_region_id)
143151
.pattern_id;
144152

145153
pattern_id = AddPatternInst<SemIR::ValueParamPattern>(

toolchain/check/pattern.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,23 @@ struct BindingPatternInfo {
3535

3636
// TODO: Add EndSubpatternAsPattern, when needed.
3737

38-
// Creates a binding pattern. Returns the binding pattern and the bind name
39-
// instruction.
38+
// Creates a binding pattern and EntityName for a `name_id`. Returns the binding
39+
// pattern and the bind name instruction.
40+
//
41+
// To make a generic binding with a CompileTimeBindIndex, construct the
42+
// EntityName and use `AddBindingPatternWithEntityName()`.
4043
auto AddBindingPattern(Context& context, SemIR::LocId name_loc,
4144
SemIR::NameId name_id, SemIR::TypeId type_id,
42-
SemIR::ExprRegionId type_region_id, bool is_generic,
43-
bool is_template) -> BindingPatternInfo;
45+
SemIR::ExprRegionId type_region_id)
46+
-> BindingPatternInfo;
47+
48+
// Creates a binding pattern with a pre-created EntityName. Returns the binding
49+
// pattern and the bind name instruction.
50+
auto AddBindingPatternWithEntityName(Context& context, SemIR::LocId name_loc,
51+
SemIR::EntityNameId entity_name_id,
52+
SemIR::TypeId type_id,
53+
SemIR::ExprRegionId type_region_id)
54+
-> BindingPatternInfo;
4455

4556
// Creates storage for `var` patterns nested within the given pattern at the
4657
// current location in the output SemIR. For a `returned var`, this

0 commit comments

Comments
 (0)