mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 08:51:04 +01:00
Decouple associated constants from let (#5973)
Decouples associated constants from being special cased in let handlers. Enforces associated constant grammar restrictions in parsing instead of checking. Closes #5411
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
#include "toolchain/check/type.h"
|
||||
#include "toolchain/check/type_completion.h"
|
||||
#include "toolchain/diagnostics/format_providers.h"
|
||||
#include "toolchain/parse/node_ids.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
#include "toolchain/sem_ir/inst.h"
|
||||
#include "toolchain/sem_ir/pattern.h"
|
||||
@@ -87,61 +88,6 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
|
||||
context.emitter().Emit(node_id, SelfOutsideImplicitParamList);
|
||||
}
|
||||
|
||||
// A binding in an interface scope declares an associated constant, not a
|
||||
// true binding, so we handle it separately.
|
||||
if (auto parent_interface_decl =
|
||||
context.scope_stack().GetCurrentScopeAs<SemIR::InterfaceDecl>();
|
||||
parent_interface_decl.has_value()) {
|
||||
// TODO: diagnose this during parsing, to avoid near-duplicate error
|
||||
// messages.
|
||||
if (!is_generic) {
|
||||
CARBON_DIAGNOSTIC(ExpectedSymbolicBindingInAssociatedConstant, Error,
|
||||
"found runtime binding pattern in associated constant "
|
||||
"declaration; expected a `:!` binding");
|
||||
context.emitter().Emit(node_id,
|
||||
ExpectedSymbolicBindingInAssociatedConstant);
|
||||
context.node_stack().Push(node_id, SemIR::ErrorInst::InstId);
|
||||
return true;
|
||||
}
|
||||
if (name_id == SemIR::NameId::Underscore) {
|
||||
// The action item here may be to document this as not allowed, and
|
||||
// add a proper diagnostic.
|
||||
context.TODO(node_id, "_ used as associated constant name");
|
||||
}
|
||||
cast_type_id = AsCompleteType(context, cast_type_id, type_node, [&] {
|
||||
CARBON_DIAGNOSTIC(IncompleteTypeInAssociatedConstantDecl, Error,
|
||||
"associated constant has incomplete type {0}",
|
||||
SemIR::TypeId);
|
||||
return context.emitter().Build(
|
||||
type_node, IncompleteTypeInAssociatedConstantDecl, cast_type_id);
|
||||
});
|
||||
if (is_template) {
|
||||
CARBON_DIAGNOSTIC(TemplateBindingInAssociatedConstantDecl, Error,
|
||||
"associated constant has `template` binding");
|
||||
context.emitter().Emit(type_node,
|
||||
TemplateBindingInAssociatedConstantDecl);
|
||||
}
|
||||
|
||||
SemIR::AssociatedConstantDecl assoc_const_decl = {
|
||||
.type_id = cast_type_id,
|
||||
.assoc_const_id = SemIR::AssociatedConstantId::None,
|
||||
.decl_block_id = SemIR::InstBlockId::None};
|
||||
auto decl_id = AddPlaceholderInstInNoBlock(
|
||||
context,
|
||||
context.parse_tree().As<Parse::CompileTimeBindingPatternId>(node_id),
|
||||
assoc_const_decl);
|
||||
assoc_const_decl.assoc_const_id = context.associated_constants().Add(
|
||||
{.name_id = name_id,
|
||||
.parent_scope_id = context.scope_stack().PeekNameScopeId(),
|
||||
.decl_id = decl_id,
|
||||
.generic_id = SemIR::GenericId::None,
|
||||
.default_value_id = SemIR::InstId::None});
|
||||
ReplaceInstBeforeConstantUse(context, decl_id, assoc_const_decl);
|
||||
|
||||
context.node_stack().Push(node_id, decl_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allocate an instruction of the appropriate kind, linked to the name for
|
||||
// error locations.
|
||||
switch (context.full_pattern_stack().CurrentKind()) {
|
||||
@@ -298,19 +244,20 @@ auto HandleParseNode(Context& context,
|
||||
context.scope_stack().Pop();
|
||||
|
||||
auto node_kind = Parse::NodeKind::CompileTimeBindingPattern;
|
||||
|
||||
const DeclIntroducerState& introducer =
|
||||
context.decl_introducer_state_stack().innermost();
|
||||
if (introducer.kind == Lex::TokenKind::Let) {
|
||||
// Disallow `let` outside of function and interface definitions.
|
||||
// TODO: Find a less brittle way of doing this. A `scope_inst_id` of `None`
|
||||
// can represent a block scope, but is also used for other kinds of scopes
|
||||
// that aren't necessarily part of an interface or function decl.
|
||||
// that aren't necessarily part of a function decl.
|
||||
// We don't need to check if the scope is an interface here as this is
|
||||
// already caught in the parse phase by the separated associated constant
|
||||
// logic.
|
||||
auto scope_inst_id = context.scope_stack().PeekInstId();
|
||||
if (scope_inst_id.has_value()) {
|
||||
auto scope_inst = context.insts().Get(scope_inst_id);
|
||||
if (!scope_inst.Is<SemIR::InterfaceDecl>() &&
|
||||
!scope_inst.Is<SemIR::FunctionDecl>()) {
|
||||
if (!scope_inst.Is<SemIR::FunctionDecl>()) {
|
||||
context.TODO(
|
||||
node_id,
|
||||
"`let` compile time binding outside function or interface");
|
||||
@@ -322,6 +269,47 @@ auto HandleParseNode(Context& context,
|
||||
return HandleAnyBindingPattern(context, node_id, node_kind);
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context,
|
||||
Parse::AssociatedConstantNameAndTypeId node_id) -> bool {
|
||||
auto [type_node, parsed_type_id] = context.node_stack().PopExprWithNodeId();
|
||||
auto [cast_type_inst_id, cast_type_id] =
|
||||
ExprAsType(context, type_node, parsed_type_id);
|
||||
|
||||
EndSubpatternAsExpr(context, cast_type_inst_id);
|
||||
|
||||
auto [name_node, name_id] = context.node_stack().PopNameWithNodeId();
|
||||
|
||||
if (name_id == SemIR::NameId::Underscore) {
|
||||
// The action item here may be to document this as not allowed, and
|
||||
// add a proper diagnostic.
|
||||
context.TODO(node_id, "_ used as associated constant name");
|
||||
}
|
||||
cast_type_id = AsCompleteType(context, cast_type_id, type_node, [&] {
|
||||
CARBON_DIAGNOSTIC(IncompleteTypeInAssociatedConstantDecl, Error,
|
||||
"associated constant has incomplete type {0}",
|
||||
SemIR::TypeId);
|
||||
return context.emitter().Build(
|
||||
type_node, IncompleteTypeInAssociatedConstantDecl, cast_type_id);
|
||||
});
|
||||
|
||||
SemIR::AssociatedConstantDecl assoc_const_decl = {
|
||||
.type_id = cast_type_id,
|
||||
.assoc_const_id = SemIR::AssociatedConstantId::None,
|
||||
.decl_block_id = SemIR::InstBlockId::None};
|
||||
auto decl_id =
|
||||
AddPlaceholderInstInNoBlock(context, node_id, assoc_const_decl);
|
||||
assoc_const_decl.assoc_const_id = context.associated_constants().Add(
|
||||
{.name_id = name_id,
|
||||
.parent_scope_id = context.scope_stack().PeekNameScopeId(),
|
||||
.decl_id = decl_id,
|
||||
.generic_id = SemIR::GenericId::None,
|
||||
.default_value_id = SemIR::InstId::None});
|
||||
ReplaceInstBeforeConstantUse(context, decl_id, assoc_const_decl);
|
||||
|
||||
context.node_stack().Push(node_id, decl_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::FieldNameAndTypeId node_id)
|
||||
-> bool {
|
||||
auto [type_node, parsed_type_id] = context.node_stack().PopExprWithNodeId();
|
||||
|
||||
Reference in New Issue
Block a user