mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 19:10:14 +01:00
Basic checking for match_first blocks (#7481)
Push a scope_stack entry for match_first blocks, and handle impls being inside those scope entries. An impl should not use the `match_first` block as its "enclosing scope" for the purpose of deciding if the impl is a redecl of another impl. We should look through it to the class or namespace the impl (and match_first) are located inside. We don't yet store the relationship between the impl and its match_first block, nor then can we use it in impl lookup for prioritization.
This commit is contained in:
@@ -258,6 +258,16 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id)
|
||||
// The impl decl has a scope stack entry for the DeclNameStack, so we look at
|
||||
// the parent scope of that.
|
||||
auto parent_scope_inst_id = context.scope_stack().PeekParentInstId();
|
||||
while (parent_scope_inst_id.has_value()) {
|
||||
if (auto match_first = context.insts().TryGetAs<SemIR::MatchFirstDecl>(
|
||||
parent_scope_inst_id)) {
|
||||
parent_scope_inst_id = match_first->enclosing_scope_inst_id;
|
||||
// TODO: Save the match first as an identifier on the Impl for choosing a
|
||||
// best candidate later.
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto impl_id = SemIR::ImplId::None;
|
||||
{
|
||||
|
||||
@@ -8,18 +8,24 @@
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::MatchFirstIntroducerId node_id)
|
||||
-> bool {
|
||||
return context.TODO(node_id, "HandleMatchFirstIntroducer");
|
||||
auto HandleParseNode(Context& /*context*/,
|
||||
Parse::MatchFirstIntroducerId /*node_id*/) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context,
|
||||
Parse::MatchFirstDefinitionStartId node_id) -> bool {
|
||||
return context.TODO(node_id, "HandleMatchFirstDefinitionStart");
|
||||
auto enclosing_scope_inst_id = context.scope_stack().PeekInstId();
|
||||
auto decl_id = AddInst<SemIR::MatchFirstDecl>(
|
||||
context, node_id, {.enclosing_scope_inst_id = enclosing_scope_inst_id});
|
||||
context.scope_stack().PushForMatchFirstBlock(decl_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::MatchFirstId node_id) -> bool {
|
||||
return context.TODO(node_id, "HandleMatchFirst");
|
||||
auto HandleParseNode(Context& context, Parse::MatchFirstId /*node_id*/)
|
||||
-> bool {
|
||||
context.scope_stack().Pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -128,6 +128,11 @@ auto ScopeStack::PushForFunctionBody(SemIR::InstId scope_inst_id) -> void {
|
||||
destroy_id_stack_.PushArray();
|
||||
}
|
||||
|
||||
auto ScopeStack::PushForMatchFirstBlock(SemIR::InstId scope_inst_id) -> void {
|
||||
Push(scope_inst_id, SemIR::NameScopeId::None, SemIR::SpecificId::None,
|
||||
/*lexical_lookup_has_load_error=*/false);
|
||||
}
|
||||
|
||||
auto ScopeStack::Pop(bool check_unused) -> void {
|
||||
auto scope = scope_stack_.pop_back_val();
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ class ScopeStack {
|
||||
// Pushes a function scope.
|
||||
auto PushForFunctionBody(SemIR::InstId scope_inst_id) -> void;
|
||||
|
||||
// Pushes a match_first block scope.
|
||||
auto PushForMatchFirstBlock(SemIR::InstId scope_inst_id) -> void;
|
||||
|
||||
// Pops the top scope from scope_stack_. Removes names from lexical_lookup_.
|
||||
// If `check_unused` is set, checks and emits diagnostics for unused names.
|
||||
auto Pop(bool check_unused = false) -> void;
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon
|
||||
// ^ARGS: -v compile %s
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/match_first/basic.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/match_first/basic.carbon
|
||||
|
||||
// --- empty.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
match_first {}
|
||||
|
||||
// --- todo_fail_class_inside.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
match_first {
|
||||
// TODO: Only impls are allowed inside match_first.
|
||||
class C;
|
||||
}
|
||||
|
||||
// --- todo_fail_interface_inside.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
match_first {
|
||||
// TODO: Only impls are allowed inside match_first.
|
||||
interface Z;
|
||||
}
|
||||
|
||||
// --- todo_fail_constraint_inside.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
match_first {
|
||||
// TODO: Only impls are allowed inside match_first.
|
||||
constraint N;
|
||||
}
|
||||
|
||||
// --- todo_fail_function_inside.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
match_first {
|
||||
// TODO: Only impls are allowed inside match_first.
|
||||
fn F();
|
||||
}
|
||||
|
||||
// --- todo_fail_final_inside.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface Z {}
|
||||
|
||||
match_first {
|
||||
// TODO: Can't write `final` inside the match first.
|
||||
final impl () as Z {}
|
||||
}
|
||||
|
||||
// --- decl.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface Z {}
|
||||
|
||||
match_first {
|
||||
impl () as Z;
|
||||
}
|
||||
|
||||
impl () as Z {}
|
||||
|
||||
// --- definition.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface Z {}
|
||||
|
||||
match_first {
|
||||
impl () as Z {}
|
||||
}
|
||||
|
||||
// --- redecl.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface Z {}
|
||||
|
||||
impl () as Z;
|
||||
|
||||
match_first {
|
||||
impl () as Z {}
|
||||
}
|
||||
|
||||
// --- inside_class.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface Z {}
|
||||
|
||||
class C {
|
||||
match_first {
|
||||
impl Self as Z {}
|
||||
}
|
||||
}
|
||||
|
||||
// --- todo_fail_inside_match_first.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface Z {}
|
||||
|
||||
match_first {
|
||||
// TODO: Can't write match_first in another match_first, that puts the same
|
||||
// impl in two match_first blocks.
|
||||
match_first {
|
||||
impl () as Z {}
|
||||
}
|
||||
}
|
||||
@@ -103,6 +103,7 @@ CARBON_SEM_IR_INST_KIND(InterfaceDecl)
|
||||
CARBON_SEM_IR_INST_KIND(InterfaceWithSelfDecl)
|
||||
CARBON_SEM_IR_INST_KIND(LookupImplWitness)
|
||||
CARBON_SEM_IR_INST_KIND(MarkInPlaceInit)
|
||||
CARBON_SEM_IR_INST_KIND(MatchFirstDecl)
|
||||
CARBON_SEM_IR_INST_KIND(MaybeUnformedType)
|
||||
CARBON_SEM_IR_INST_KIND(NameBindingDecl)
|
||||
CARBON_SEM_IR_INST_KIND(NamedConstraintDecl)
|
||||
|
||||
@@ -942,6 +942,16 @@ struct GenericNamedConstraintType {
|
||||
SpecificId enclosing_specific_id;
|
||||
};
|
||||
|
||||
// A `match_first` declaration.
|
||||
struct MatchFirstDecl {
|
||||
static constexpr auto Kind =
|
||||
InstKind::MatchFirstDecl.Define<Parse::MatchFirstDefinitionStartId>(
|
||||
{.ir_name = "match_first",
|
||||
.constant_kind = InstConstantKind::AlwaysUnique,
|
||||
.is_lowered = false});
|
||||
SemIR::InstId enclosing_scope_inst_id;
|
||||
};
|
||||
|
||||
// An `impl` declaration.
|
||||
struct ImplDecl {
|
||||
static constexpr auto Kind = InstKind::ImplDecl.Define<Parse::AnyImplDeclId>(
|
||||
|
||||
Reference in New Issue
Block a user