Disallow compile time bindings where they aren't clearly supported. (#4338)

This is resolving a fuzz-discovered crash related to function suspends
and compile time bind indices. Although the crash originally came from
clearly invalid syntax (missing the `=` inside a `class` decl), the
syntax with a value should also be valid but has the same crash.

This approach disallows compile-time bindings in contexts that can
create ambiguous results, particularly class declarations. These are an
issue because a suspended function can have let declarations after it.
I'm allowing them in function bodies and interface scopes.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This commit is contained in:
Jon Ross-Perkins
2024-09-25 17:06:09 +00:00
committed by GitHub
co-authored by Richard Smith
parent 49a8efbe1b
commit 87678cc374
8 changed files with 946 additions and 281 deletions
+17 -3
View File
@@ -32,6 +32,8 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
inst_id.is_valid() && context.insts().Is<SemIR::InterfaceDecl>(inst_id);
}
bool needs_compile_time_binding = is_generic && !is_associated_constant;
// Create the appropriate kind of binding for this pattern.
auto make_bind_name = [&](SemIR::TypeId type_id,
SemIR::InstId value_id) -> SemIR::LocIdAndInst {
@@ -42,7 +44,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
.parent_scope_id = context.scope_stack().PeekNameScopeId(),
// TODO: Don't allocate a compile-time binding index for an associated
// constant declaration.
.bind_index = is_generic && !is_associated_constant
.bind_index = needs_compile_time_binding
? context.scope_stack().AddCompileTimeBinding()
: SemIR::CompileTimeBindIndex::Invalid});
if (is_generic) {
@@ -63,7 +65,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
// stack.
auto push_bind_name = [&](SemIR::InstId bind_id) {
context.node_stack().Push(node_id, bind_id);
if (is_generic && !is_associated_constant) {
if (needs_compile_time_binding) {
context.scope_stack().PushCompileTimeBinding(bind_id);
}
};
@@ -200,7 +202,19 @@ auto HandleParseNode(Context& context, Parse::BindingPatternId node_id)
auto HandleParseNode(Context& context,
Parse::CompileTimeBindingPatternId node_id) -> bool {
return HandleAnyBindingPattern(context, node_id, /*is_generic=*/true);
bool is_generic = true;
if (context.decl_introducer_state_stack().innermost().kind ==
Lex::TokenKind::Let) {
auto scope_inst = context.insts().Get(context.scope_stack().PeekInstId());
if (!scope_inst.Is<SemIR::InterfaceDecl>() &&
!scope_inst.Is<SemIR::FunctionDecl>()) {
context.TODO(node_id,
"`let` compile time binding outside function or interface");
is_generic = false;
}
}
return HandleAnyBindingPattern(context, node_id, is_generic);
}
auto HandleParseNode(Context& context, Parse::AddrId node_id) -> bool {