diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 40199304e6ce0..03d70ad8df1d0 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -222,6 +222,18 @@ auto Context::DiagnoseDuplicateName(SemIRLoc dup_def, SemIRLoc prev_def) .Emit(); } +auto Context::DiagnosePoisonedName(SemIRLoc loc) -> void { + // TODO: Improve the diagnostic to replace NodeId::Invalid with the location + // where the name was poisoned. See discussion in + // https://github.com/carbon-language/carbon-lang/pull/4654#discussion_r1876607172 + CARBON_DIAGNOSTIC(NameUseBeforeDecl, Error, + "name used before it was declared"); + CARBON_DIAGNOSTIC(NameUseBeforeDeclNote, Note, "declared here"); + emitter_->Build(SemIR::LocId::Invalid, NameUseBeforeDecl) + .Note(loc, NameUseBeforeDeclNote) + .Emit(); +} + auto Context::DiagnoseNameNotFound(SemIRLoc loc, SemIR::NameId name_id) -> void { CARBON_DIAGNOSTIC(NameNotFound, Error, "name `{0}` not found", SemIR::NameId); @@ -354,6 +366,8 @@ auto Context::LookupUnqualifiedName(Parse::NodeId node_id, scope_stack().LookupInLexicalScopes(name_id); // Walk the non-lexical scopes and perform lookups into each of them. + // Collect scopes to poison this name when it's found. + llvm::SmallVector scopes_to_poison; for (auto [index, lookup_scope_id, specific_id] : llvm::reverse(non_lexical_scopes)) { if (auto non_lexical_result = @@ -361,8 +375,17 @@ auto Context::LookupUnqualifiedName(Parse::NodeId node_id, LookupScope{.name_scope_id = lookup_scope_id, .specific_id = specific_id}, /*required=*/false); - non_lexical_result.inst_id.is_valid()) { - return non_lexical_result; + !non_lexical_result.inst_id.is_poisoned()) { + if (non_lexical_result.inst_id.is_valid()) { + // Poison the scopes for this name. + for (const auto [scope_id, specific_id] : scopes_to_poison) { + name_scopes().Get(scope_id).AddPoison(name_id); + } + + return non_lexical_result; + } + scopes_to_poison.push_back( + {.name_scope_id = lookup_scope_id, .specific_id = specific_id}); } } @@ -616,7 +639,8 @@ auto Context::LookupQualifiedName(SemIR::LocId loc_id, SemIR::NameId name_id, result.specific_id = specific_id; } - if (required && !result.inst_id.is_valid()) { + if (required && + (!result.inst_id.is_valid() || result.inst_id.is_poisoned())) { if (!has_error) { if (prohibited_accesses.empty()) { DiagnoseMemberNameNotFound(loc_id, name_id, lookup_scopes); diff --git a/toolchain/check/context.h b/toolchain/check/context.h index f884db6dfe33e..7f24d004eca95 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -246,6 +246,9 @@ class Context { // Prints a diagnostic for a duplicate name. auto DiagnoseDuplicateName(SemIRLoc dup_def, SemIRLoc prev_def) -> void; + // Prints a diagnostic for a poisoned name. + auto DiagnosePoisonedName(SemIRLoc loc) -> void; + // Prints a diagnostic for a missing name. auto DiagnoseNameNotFound(SemIRLoc loc, SemIR::NameId name_id) -> void; diff --git a/toolchain/check/decl_name_stack.cpp b/toolchain/check/decl_name_stack.cpp index ae98f100ebfc0..ca0407c84fa71 100644 --- a/toolchain/check/decl_name_stack.cpp +++ b/toolchain/check/decl_name_stack.cpp @@ -33,6 +33,9 @@ auto DeclNameStack::NameContext::prev_inst_id() -> SemIR::InstId { case NameContext::State::Unresolved: return SemIR::InstId::Invalid; + case NameContext::State::Poisoned: + return SemIR::InstId::PoisonedName; + case NameContext::State::Finished: CARBON_FATAL("Finished state should only be used internally"); } @@ -167,12 +170,15 @@ auto DeclNameStack::AddName(NameContext name_context, SemIR::InstId target_id, } } -auto DeclNameStack::AddNameOrDiagnoseDuplicate(NameContext name_context, - SemIR::InstId target_id, - SemIR::AccessKind access_kind) - -> void { +auto DeclNameStack::AddNameOrDiagnose(NameContext name_context, + SemIR::InstId target_id, + SemIR::AccessKind access_kind) -> void { if (auto id = name_context.prev_inst_id(); id.is_valid()) { - context_->DiagnoseDuplicateName(target_id, id); + if (id.is_poisoned()) { + context_->DiagnosePoisonedName(target_id); + } else { + context_->DiagnoseDuplicateName(target_id, id); + } } else { AddName(name_context, target_id, access_kind); } @@ -260,6 +266,9 @@ auto DeclNameStack::ApplyAndLookupName(NameContext& name_context, // Invalid indicates an unresolved name. Store it and return. name_context.unresolved_name_id = name_id; name_context.state = NameContext::State::Unresolved; + } else if (resolved_inst_id.is_poisoned()) { + name_context.unresolved_name_id = name_id; + name_context.state = NameContext::State::Poisoned; } else { // Store the resolved instruction and continue for the target scope // update. @@ -277,8 +286,14 @@ static auto CheckQualifierIsResolved( CARBON_FATAL("No qualifier to resolve"); case DeclNameStack::NameContext::State::Resolved: + if (name_context.resolved_inst_id.is_poisoned()) { + context.DiagnoseNameNotFound(name_context.loc_id, + name_context.unresolved_name_id); + return false; + } return true; + case DeclNameStack::NameContext::State::Poisoned: case DeclNameStack::NameContext::State::Unresolved: // Because more qualifiers were found, we diagnose that the earlier // qualifier failed to resolve. @@ -367,6 +382,10 @@ auto DeclNameStack::ResolveAsScope(const NameContext& name_context, return InvalidResult; } + if (name_context.resolved_inst_id.is_poisoned()) { + return InvalidResult; + } + auto new_params = DeclParams( name.name_loc_id, name.first_param_node_id, name.last_param_node_id, name.implicit_param_patterns_id, name.param_patterns_id); diff --git a/toolchain/check/decl_name_stack.h b/toolchain/check/decl_name_stack.h index a7c0209fb4b4a..d985e90fde478 100644 --- a/toolchain/check/decl_name_stack.h +++ b/toolchain/check/decl_name_stack.h @@ -78,6 +78,9 @@ class DeclNameStack { // An identifier didn't resolve. Unresolved, + // An identifier was poisoned in this scope. + Poisoned, + // The name has already been finished. This is not set in the name // returned by `FinishName`, but is used internally to track that // `FinishName` has already been called. @@ -231,9 +234,8 @@ class DeclNameStack { SemIR::AccessKind access_kind) -> void; // Adds a name to name lookup. Prints a diagnostic for name conflicts. - auto AddNameOrDiagnoseDuplicate(NameContext name_context, - SemIR::InstId target_id, - SemIR::AccessKind access_kind) -> void; + auto AddNameOrDiagnose(NameContext name_context, SemIR::InstId target_id, + SemIR::AccessKind access_kind) -> void; // Adds a name to name lookup, or returns the existing instruction if this // name has already been declared in this scope. diff --git a/toolchain/check/handle_alias.cpp b/toolchain/check/handle_alias.cpp index 6d15f388422ba..237c5dcce9bed 100644 --- a/toolchain/check/handle_alias.cpp +++ b/toolchain/check/handle_alias.cpp @@ -71,7 +71,7 @@ auto HandleParseNode(Context& context, Parse::AliasId /*node_id*/) -> bool { // Add the name of the binding to the current scope. context.decl_name_stack().PopScope(); - context.decl_name_stack().AddNameOrDiagnoseDuplicate( + context.decl_name_stack().AddNameOrDiagnose( name_context, alias_id, introducer.modifier_set.GetAccessKind()); return true; } diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index c613374896969..3d54e62bbbba2 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -113,6 +113,12 @@ static auto MergeOrAddName(Context& context, Parse::AnyClassDeclId node_id, return; } + if (prev_id.is_poisoned()) { + // This is a declaration of a poisoned name. + context.DiagnosePoisonedName(class_decl_id); + return; + } + auto prev_class_id = SemIR::ClassId::Invalid; auto prev_import_ir_id = SemIR::ImportIRId::Invalid; auto prev = context.insts().Get(prev_id); @@ -546,7 +552,7 @@ auto HandleParseNode(Context& context, Parse::BaseDeclId node_id) -> bool { } // Bind the name `base` in the class to the base field. - context.decl_name_stack().AddNameOrDiagnoseDuplicate( + context.decl_name_stack().AddNameOrDiagnose( context.decl_name_stack().MakeUnqualifiedName(node_id, SemIR::NameId::Base), class_info.base_id, introducer.modifier_set.GetAccessKind()); diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 18677edf52713..92083bd7e5f66 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -123,6 +123,11 @@ static auto TryMergeRedecl(Context& context, Parse::AnyFunctionDeclId node_id, return; } + if (prev_id.is_poisoned()) { + context.DiagnosePoisonedName(function_info.latest_decl_id()); + return; + } + auto prev_function_id = SemIR::FunctionId::Invalid; auto prev_import_ir_id = SemIR::ImportIRId::Invalid; CARBON_KIND_SWITCH(context.insts().Get(prev_id)) { diff --git a/toolchain/check/handle_interface.cpp b/toolchain/check/handle_interface.cpp index a981d728f09d7..ce310929e4443 100644 --- a/toolchain/check/handle_interface.cpp +++ b/toolchain/check/handle_interface.cpp @@ -66,8 +66,13 @@ static auto BuildInterfaceDecl(Context& context, auto existing_id = context.decl_name_stack().LookupOrAddName( name_context, interface_decl_id, introducer.modifier_set.GetAccessKind()); if (existing_id.is_valid()) { - if (auto existing_interface_decl = - context.insts().Get(existing_id).TryAs()) { + if (existing_id.is_poisoned()) { + // This is a declaration of a poisoned name. + context.DiagnosePoisonedName(interface_decl_id); + } else if (auto existing_interface_decl = + context.insts() + .Get(existing_id) + .TryAs()) { auto existing_interface = context.interfaces().Get(existing_interface_decl->interface_id); if (CheckRedeclParamsMatch( diff --git a/toolchain/check/handle_let_and_var.cpp b/toolchain/check/handle_let_and_var.cpp index 5967111812c6e..ec21542e5ea21 100644 --- a/toolchain/check/handle_let_and_var.cpp +++ b/toolchain/check/handle_let_and_var.cpp @@ -94,8 +94,8 @@ static auto BuildAssociatedConstantDecl(Context& context, auto assoc_id = BuildAssociatedEntity(context, interface_id, decl_id); auto name_context = context.decl_name_stack().MakeUnqualifiedName(pattern.loc_id, name_id); - context.decl_name_stack().AddNameOrDiagnoseDuplicate(name_context, assoc_id, - access_kind); + context.decl_name_stack().AddNameOrDiagnose(name_context, assoc_id, + access_kind); } // Adds name bindings. Returns the resulting ID for the references. @@ -109,16 +109,16 @@ static auto HandleNameBinding(Context& context, SemIR::InstId pattern_id, auto name_context = context.decl_name_stack().MakeUnqualifiedName( context.insts().GetLocId(pattern_id), context.entity_names().Get(bind_name->entity_name_id).name_id); - context.decl_name_stack().AddNameOrDiagnoseDuplicate( - name_context, pattern_id, access_kind); + context.decl_name_stack().AddNameOrDiagnose(name_context, pattern_id, + access_kind); return bind_name->value_id; } else if (auto field_decl = context.insts().TryGetAs(pattern_id)) { // Introduce the field name into the class. auto name_context = context.decl_name_stack().MakeUnqualifiedName( context.insts().GetLocId(pattern_id), field_decl->name_id); - context.decl_name_stack().AddNameOrDiagnoseDuplicate( - name_context, pattern_id, access_kind); + context.decl_name_stack().AddNameOrDiagnose(name_context, pattern_id, + access_kind); return pattern_id; } else { // TODO: Handle other kinds of pattern. diff --git a/toolchain/check/handle_namespace.cpp b/toolchain/check/handle_namespace.cpp index f4b6e4b22b4f4..e4f027440466d 100644 --- a/toolchain/check/handle_namespace.cpp +++ b/toolchain/check/handle_namespace.cpp @@ -43,10 +43,13 @@ auto HandleParseNode(Context& context, Parse::NamespaceId node_id) -> bool { auto existing_inst_id = context.decl_name_stack().LookupOrAddName( name_context, namespace_id, SemIR::AccessKind::Public); if (existing_inst_id.is_valid()) { - // If there's a name conflict with a namespace, "merge" by using the - // previous declaration. Otherwise, diagnose the issue. - if (auto existing = - context.insts().TryGetAs(existing_inst_id)) { + if (existing_inst_id.is_poisoned()) { + context.DiagnosePoisonedName(namespace_id); + } else if (auto existing = context.insts().TryGetAs( + existing_inst_id)) { + // If there's a name conflict with a namespace, "merge" by using the + // previous declaration. Otherwise, diagnose the issue. + // Point at the other namespace. namespace_inst.name_scope_id = existing->name_scope_id; diff --git a/toolchain/check/import.cpp b/toolchain/check/import.cpp index debb25096df6b..ac05a052bc1a3 100644 --- a/toolchain/check/import.cpp +++ b/toolchain/check/import.cpp @@ -110,6 +110,7 @@ static auto AddNamespace(Context& context, SemIR::TypeId namespace_type_id, SemIR::InstId::Invalid, SemIR::AccessKind::Public); if (!inserted) { auto prev_inst_id = parent_scope->GetEntry(entry_id).inst_id; + CARBON_CHECK(!prev_inst_id.is_poisoned()); if (auto namespace_inst = context.insts().TryGetAs(prev_inst_id)) { if (diagnose_duplicate_namespace) { @@ -333,6 +334,9 @@ static auto ImportScopeFromApiFile(Context& context, auto& impl_scope = context.name_scopes().Get(impl_scope_id); for (const auto& api_entry : api_scope.entries()) { + if (api_entry.inst_id.is_poisoned()) { + continue; + } auto impl_name_id = CopyNameFromImportIR(context, api_sem_ir, api_entry.name_id); if (auto ns = diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index a4d51fd8d4c75..26b0b9576b69b 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -1194,6 +1194,9 @@ static auto AddNameScopeImportRefs(ImportContext& context, const SemIR::NameScope& import_scope, SemIR::NameScope& new_scope) -> void { for (auto entry : import_scope.entries()) { + if (entry.inst_id.is_poisoned()) { + continue; + } auto ref_id = AddImportRef(context, entry.inst_id); new_scope.AddRequired({.name_id = GetLocalNameId(context, entry.name_id), .inst_id = ref_id, @@ -2907,6 +2910,9 @@ static auto GetInstForLoad(Context& context, } auto LoadImportRef(Context& context, SemIR::InstId inst_id) -> void { + if (inst_id.is_poisoned()) { + return; + } auto inst = context.insts().TryGetAs(inst_id); if (!inst) { return; diff --git a/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon b/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon index 2fe30b9760353..2fa916936f275 100644 --- a/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon +++ b/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon @@ -12,52 +12,253 @@ library "[[@TEST_NAME]]"; -namespace N; class C {}; // Both N.F1 and N.F2 use N.C and not C. +namespace N; class N.C {} fn N.F1(x: C); fn N.F2(x: C) { N.F1(x); } -// --- poison_without_usage.carbon +// --- poison.carbon library "[[@TEST_NAME]]"; +class C {}; + namespace N; +// Here we use C and poison N.C. +fn N.F1(x: C); + +// --- fail_poison_class_without_usage.carbon +// CHECK:STDERR: fail_poison_class_without_usage.carbon: error: name used before it was declared [NameUseBeforeDecl] + +library "[[@TEST_NAME]]"; + class C {}; +namespace N; // Here we use C and poison N.C. fn N.F1(x: C); -// TODO: Should fail here since C was poisoned for namespace N when it was used -// in N context without qualification. +// Should fail here since C was poisoned for namespace N when it was used in N +// context without qualification. +// CHECK:STDERR: fail_poison_class_without_usage.carbon:[[@LINE+4]]:1: note: declared here [NameUseBeforeDeclNote] +// CHECK:STDERR: class N.C {} +// CHECK:STDERR: ^~~~~~~~~~~ +// CHECK:STDERR: class N.C {} -// --- fail_poison_with_usage.carbon +// --- fail_poison_interface_without_usage.carbon +// CHECK:STDERR: fail_poison_interface_without_usage.carbon: error: name used before it was declared [NameUseBeforeDecl] + +library "[[@TEST_NAME]]"; + +interface I {}; + +namespace N; +// Here we use I and poison N.I. +fn N.F1(x: I); + +// Should fail here since I was poisoned for namespace N when it was used in N +// context without qualification. +// CHECK:STDERR: fail_poison_interface_without_usage.carbon:[[@LINE+4]]:1: note: declared here [NameUseBeforeDeclNote] +// CHECK:STDERR: interface N.I {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~ +// CHECK:STDERR: +interface N.I {} + +// --- fail_poison_namespace_without_usage.carbon +// CHECK:STDERR: fail_poison_namespace_without_usage.carbon: error: name used before it was declared [NameUseBeforeDecl] library "[[@TEST_NAME]]"; +class C {}; + namespace N; +// Here we use C and poison N.C. +fn N.F1(x: C); + +// Should fail here since C was poisoned for namespace N when it was used in N +// context without qualification. +// CHECK:STDERR: fail_poison_namespace_without_usage.carbon:[[@LINE+4]]:1: note: declared here [NameUseBeforeDeclNote] +// CHECK:STDERR: namespace N.C; +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: +namespace N.C; + +// --- fail_poison_member_without_usage.carbon +// CHECK:STDERR: fail_poison_member_without_usage.carbon: error: name used before it was declared [NameUseBeforeDecl] + +library "[[@TEST_NAME]]"; + +class C1 {}; + +class D { + // Here we use C1 and poison D.C1. + fn F1(x: C1); + + class C2 {}; + // Should fail here since C1 was poisoned for namespace class D when it was + // used in D context without qualification. + // CHECK:STDERR: fail_poison_member_without_usage.carbon:[[@LINE+4]]:7: note: declared here [NameUseBeforeDeclNote] + // CHECK:STDERR: var C1: C2; + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + var C1: C2; +} + +// --- fail_poison_function_without_usage.carbon +// CHECK:STDERR: fail_poison_function_without_usage.carbon: error: name used before it was declared [NameUseBeforeDecl] + +library "[[@TEST_NAME]]"; + +class C {}; + +namespace N; +// Here we use C and poison N.C. +fn N.F1(x: C); + +// Should fail here since C was poisoned for namespace N when it was used in N +// context without qualification. +// CHECK:STDERR: fail_poison_function_without_usage.carbon:[[@LINE+4]]:1: note: declared here [NameUseBeforeDeclNote] +// CHECK:STDERR: fn N.C(); +// CHECK:STDERR: ^~~~~~~~~ +// CHECK:STDERR: +fn N.C(); + +// --- fail_use_undefined_poisoned_name.carbon + +library "[[@TEST_NAME]]"; + +class C {}; + +namespace N; +// Here we use C and poison N.C. +fn N.F1() -> C; + +// Try to use N.C which was never defined and poisoned. +// CHECK:STDERR: fail_use_undefined_poisoned_name.carbon:[[@LINE+4]]:14: error: member name `C` not found in `N` [MemberNameNotFoundInScope] +// CHECK:STDERR: fn N.F2() -> N.C; +// CHECK:STDERR: ^~~ +// CHECK:STDERR: +fn N.F2() -> N.C; + +// --- fail_poison_with_usage.carbon +// CHECK:STDERR: fail_poison_with_usage.carbon: error: name used before it was declared [NameUseBeforeDecl] + +library "[[@TEST_NAME]]"; + class C {}; +namespace N; // Here we use C and poison N.C. fn N.F1(x: C); -// TODO: Should fail here since C was poisoned for namespace N when it was used -// in N context without qualification. +// Should fail here since C was poisoned for namespace N when it was used in N +// context without qualification. +// CHECK:STDERR: fail_poison_with_usage.carbon:[[@LINE+4]]:1: note: declared here [NameUseBeforeDeclNote] +// CHECK:STDERR: class N.C {} +// CHECK:STDERR: ^~~~~~~~~~~ +// CHECK:STDERR: class N.C {} -// TODO: Should not fail here since both N.F2() and N.F1() input is the class C -// and not class N.C. -// CHECK:STDERR: fail_poison_with_usage.carbon:[[@LINE+6]]:22: error: `Core.ImplicitAs` implicitly referenced here, but package `Core` not found [CoreNotFound] -// CHECK:STDERR: fn N.F2(x: C) { N.F1(x); } -// CHECK:STDERR: ^ -// CHECK:STDERR: fail_poison_with_usage.carbon:[[@LINE-11]]:9: note: initializing function parameter [InCallToFunctionParam] -// CHECK:STDERR: fn N.F1(x: C); -// CHECK:STDERR: ^~~~ +// Should not fail here since both N.F2() and N.F1() input is the class C and +// not class N.C. fn N.F2(x: C) { N.F1(x); } +// --- fail_poison_multiple_scopes.carbon +// CHECK:STDERR: fail_poison_multiple_scopes.carbon: error: name used before it was declared [NameUseBeforeDecl] + +library "[[@TEST_NAME]]"; + +class C {}; + +namespace N1; +namespace N1.N2; +namespace N1.N2.N3; +class N1.N2.N3.D1 { + interface D2 { + class D3 { + // Here we use C and poison: + // * N1.C + // * N1.N2.C + // * N1.N2.N3.C + // * N1.N2.N3.D1.C + // * N1.N2.N3.D1.D2.C + // * N1.N2.N3.D1.D2.D3.C + fn F(x: C); + + // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+5]]:7: note: declared here [NameUseBeforeDeclNote] + // CHECK:STDERR: class C {} + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_poison_multiple_scopes.carbon: error: name used before it was declared [NameUseBeforeDecl] + class C {} + } + // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+5]]:5: note: declared here [NameUseBeforeDeclNote] + // CHECK:STDERR: class C {} + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_poison_multiple_scopes.carbon: error: name used before it was declared [NameUseBeforeDecl] + class C {} + } + // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+5]]:3: note: declared here [NameUseBeforeDeclNote] + // CHECK:STDERR: class C {} + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_poison_multiple_scopes.carbon: error: name used before it was declared [NameUseBeforeDecl] + class C {} +} + +// CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+5]]:1: note: declared here [NameUseBeforeDeclNote] +// CHECK:STDERR: class N1.C {} +// CHECK:STDERR: ^~~~~~~~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_poison_multiple_scopes.carbon: error: name used before it was declared [NameUseBeforeDecl] +class N1.C {} + +// CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+5]]:1: note: declared here [NameUseBeforeDeclNote] +// CHECK:STDERR: interface N1.N2.C {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_poison_multiple_scopes.carbon: error: name used before it was declared [NameUseBeforeDecl] +interface N1.N2.C {} + +// CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+4]]:1: note: declared here [NameUseBeforeDeclNote] +// CHECK:STDERR: class N1.N2.N3.C {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +class N1.N2.N3.C {} + +// --- fail_alias.carbon +// CHECK:STDERR: fail_alias.carbon: error: name used before it was declared [NameUseBeforeDecl] + +library "[[@TEST_NAME]]"; + +class C {} + +namespace N; +// CHECK:STDERR: fail_alias.carbon:[[@LINE+3]]:9: note: declared here [NameUseBeforeDeclNote] +// CHECK:STDERR: alias N.C = C; +// CHECK:STDERR: ^ +alias N.C = C; + +// --- ignored_poison_in_import.carbon + +library "[[@TEST_NAME]]"; +import library "poison"; + +// This doesn't fail. +class N.C {} + +// --- poison.impl.carbon + +impl library "[[@TEST_NAME]]"; + +// TODO: This should fail since N.C was poisoned in the api. +class N.C {} + // CHECK:STDOUT: --- no_poison.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -74,15 +275,15 @@ fn N.F2(x: C) { N.F1(x); } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl.loc4 // CHECK:STDOUT: .N = %N -// CHECK:STDOUT: .C = %C.decl.loc5 // CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl.loc4: type = class_decl @C.1 [template = constants.%C.1] {} {} // CHECK:STDOUT: %N: = namespace [template] { // CHECK:STDOUT: .C = %C.decl.loc8 // CHECK:STDOUT: .F1 = %F1.decl // CHECK:STDOUT: .F2 = %F2.decl // CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl.loc5: type = class_decl @C.1 [template = constants.%C.1] {} {} // CHECK:STDOUT: %C.decl.loc8: type = class_decl @C.2 [template = constants.%C.2] {} {} // CHECK:STDOUT: %F1.decl: %F1.type = fn_decl @F1 [template = constants.%F1] { // CHECK:STDOUT: %x.patt: %C.2 = binding_pattern x @@ -129,125 +330,677 @@ fn N.F2(x: C) { N.F1(x); } // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- poison_without_usage.carbon +// CHECK:STDOUT: --- poison.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { -// CHECK:STDOUT: %C.1: type = class_type @C.1 [template] +// CHECK:STDOUT: %C: type = class_type @C [template] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] // CHECK:STDOUT: %F1.type: type = fn_type @F1 [template] // CHECK:STDOUT: %F1: %F1.type = struct_value () [template] -// CHECK:STDOUT: %C.2: type = class_type @C.2 [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .N = %N -// CHECK:STDOUT: .C = %C.decl.loc5 // CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} // CHECK:STDOUT: %N: = namespace [template] { // CHECK:STDOUT: .F1 = %F1.decl -// CHECK:STDOUT: .C = %C.decl.loc12 // CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl.loc5: type = class_decl @C.1 [template = constants.%C.1] {} {} // CHECK:STDOUT: %F1.decl: %F1.type = fn_decl @F1 [template = constants.%F1] { -// CHECK:STDOUT: %x.patt: %C.1 = binding_pattern x -// CHECK:STDOUT: %x.param_patt: %C.1 = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: %x.patt: %C = binding_pattern x +// CHECK:STDOUT: %x.param_patt: %C = value_param_pattern %x.patt, runtime_param0 // CHECK:STDOUT: } { -// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl.loc5 [template = constants.%C.1] -// CHECK:STDOUT: %x.param: %C.1 = value_param runtime_param0 -// CHECK:STDOUT: %x: %C.1 = bind_name x, %x.param +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %x.param: %C = value_param runtime_param0 +// CHECK:STDOUT: %x: %C = bind_name x, %x.param // CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl.loc12: type = class_decl @C.2 [template = constants.%C.2] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @C.1 { +// CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C.1 +// CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @C.2 { +// CHECK:STDOUT: fn @F1(%x.param_patt: %C); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_poison_class_without_usage.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: %F1.type: type = fn_type @F1 [template] +// CHECK:STDOUT: %F1: %F1.type = struct_value () [template] +// CHECK:STDOUT: %.1: type = class_type @.1 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .N = %N +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} +// CHECK:STDOUT: %N: = namespace [template] { +// CHECK:STDOUT: .F1 = %F1.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F1.decl: %F1.type = fn_decl @F1 [template = constants.%F1] { +// CHECK:STDOUT: %x.patt: %C = binding_pattern x +// CHECK:STDOUT: %x.param_patt: %C = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %x.param: %C = value_param runtime_param0 +// CHECK:STDOUT: %x: %C = bind_name x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %.decl: type = class_decl @.1 [template = constants.%.1] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C.2 +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @.1 { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%.1 +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F1(%x.param_patt: %C); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_poison_interface_without_usage.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %I.type: type = facet_type <@I> [template] +// CHECK:STDOUT: %Self.1: %I.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: %F1.type: type = fn_type @F1 [template] +// CHECK:STDOUT: %F1: %F1.type = struct_value () [template] +// CHECK:STDOUT: %.type: type = facet_type <@.1> [template] +// CHECK:STDOUT: %Self.2: %.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: .N = %N +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%I.type] {} {} +// CHECK:STDOUT: %N: = namespace [template] { +// CHECK:STDOUT: .F1 = %F1.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F1.decl: %F1.type = fn_decl @F1 [template = constants.%F1] { +// CHECK:STDOUT: %x.patt: %I.type = binding_pattern x +// CHECK:STDOUT: %x.param_patt: %I.type = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [template = constants.%I.type] +// CHECK:STDOUT: %x.param: %I.type = value_param runtime_param0 +// CHECK:STDOUT: %x: %I.type = bind_name x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %.decl: type = interface_decl @.1 [template = constants.%.type] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I { +// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.1] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @.1 { +// CHECK:STDOUT: %Self: %.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F1(%x.param_patt: %I.type); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_poison_namespace_without_usage.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: %F1.type: type = fn_type @F1 [template] +// CHECK:STDOUT: %F1: %F1.type = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .N = %N +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} +// CHECK:STDOUT: %N: = namespace [template] { +// CHECK:STDOUT: .F1 = %F1.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F1.decl: %F1.type = fn_decl @F1 [template = constants.%F1] { +// CHECK:STDOUT: %x.patt: %C = binding_pattern x +// CHECK:STDOUT: %x.param_patt: %C = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %x.param: %C = value_param runtime_param0 +// CHECK:STDOUT: %x: %C = bind_name x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc17: = namespace [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F1(%x.param_patt: %C); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_poison_member_without_usage.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C1: type = class_type @C1 [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type.1: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: %D: type = class_type @D [template] +// CHECK:STDOUT: %F1.type: type = fn_type @F1 [template] +// CHECK:STDOUT: %F1: %F1.type = struct_value () [template] +// CHECK:STDOUT: %C2: type = class_type @C2 [template] +// CHECK:STDOUT: %D.elem: type = unbound_element_type %D, %C2 [template] +// CHECK:STDOUT: %struct_type.C1: type = struct_type {.C1: %C2} [template] +// CHECK:STDOUT: %complete_type.2: = complete_type_witness %struct_type.C1 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C1 = %C1.decl +// CHECK:STDOUT: .D = %D.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C1.decl: type = class_decl @C1 [template = constants.%C1] {} {} +// CHECK:STDOUT: %D.decl: type = class_decl @D [template = constants.%D] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C1 { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type.1] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C1 +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @D { +// CHECK:STDOUT: %F1.decl: %F1.type = fn_decl @F1 [template = constants.%F1] { +// CHECK:STDOUT: %x.patt: %C1 = binding_pattern x +// CHECK:STDOUT: %x.param_patt: %C1 = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C1.ref: type = name_ref C1, file.%C1.decl [template = constants.%C1] +// CHECK:STDOUT: %x.param: %C1 = value_param runtime_param0 +// CHECK:STDOUT: %x: %C1 = bind_name x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %C2.decl: type = class_decl @C2 [template = constants.%C2] {} {} +// CHECK:STDOUT: %C2.ref: type = name_ref C2, %C2.decl [template = constants.%C2] +// CHECK:STDOUT: %.loc18: %D.elem = field_decl C1, element0 [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.C1 [template = constants.%complete_type.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%D +// CHECK:STDOUT: .F1 = %F1.decl +// CHECK:STDOUT: .C2 = %C2.decl +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C2 { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type.1] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C2 +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F1(%x.param_patt: %C1); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_poison_function_without_usage.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: %F1.type: type = fn_type @F1 [template] +// CHECK:STDOUT: %F1: %F1.type = struct_value () [template] +// CHECK:STDOUT: %.type: type = fn_type @.1 [template] +// CHECK:STDOUT: %.1: %.type = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .N = %N +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} +// CHECK:STDOUT: %N: = namespace [template] { +// CHECK:STDOUT: .F1 = %F1.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F1.decl: %F1.type = fn_decl @F1 [template = constants.%F1] { +// CHECK:STDOUT: %x.patt: %C = binding_pattern x +// CHECK:STDOUT: %x.param_patt: %C = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %x.param: %C = value_param runtime_param0 +// CHECK:STDOUT: %x: %C = bind_name x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %.decl: %.type = fn_decl @.1 [template = constants.%.1] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F1(%x.param_patt: %C); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @.1(); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_use_undefined_poisoned_name.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: %F1.type: type = fn_type @F1 [template] +// CHECK:STDOUT: %F1: %F1.type = struct_value () [template] +// CHECK:STDOUT: %F2.type: type = fn_type @F2 [template] +// CHECK:STDOUT: %F2: %F2.type = struct_value () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .N = %N +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} +// CHECK:STDOUT: %N: = namespace [template] { +// CHECK:STDOUT: .F1 = %F1.decl +// CHECK:STDOUT: .F2 = %F2.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F1.decl: %F1.type = fn_decl @F1 [template = constants.%F1] { +// CHECK:STDOUT: %return.patt: %C = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %C = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %return.param: ref %C = out_param runtime_param0 +// CHECK:STDOUT: %return: ref %C = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %F2.decl: %F2.type = fn_decl @F2 [template = constants.%F2] { +// CHECK:STDOUT: %return.patt: = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %N.ref: = name_ref N, file.%N [template = file.%N] +// CHECK:STDOUT: %C.ref: = name_ref C, [template = ] +// CHECK:STDOUT: %return.param: ref = out_param runtime_param0 +// CHECK:STDOUT: %return: ref = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @F1(%x.param_patt: %C.1); +// CHECK:STDOUT: fn @F1() -> %C; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F2() -> ; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_poison_with_usage.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { -// CHECK:STDOUT: %C.1: type = class_type @C.1 [template] +// CHECK:STDOUT: %C: type = class_type @C [template] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] // CHECK:STDOUT: %F1.type: type = fn_type @F1 [template] // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [template] // CHECK:STDOUT: %F1: %F1.type = struct_value () [template] -// CHECK:STDOUT: %C.2: type = class_type @C.2 [template] +// CHECK:STDOUT: %.1: type = class_type @.1 [template] // CHECK:STDOUT: %F2.type: type = fn_type @F2 [template] // CHECK:STDOUT: %F2: %F2.type = struct_value () [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl // CHECK:STDOUT: .N = %N -// CHECK:STDOUT: .C = %C.decl.loc5 // CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} // CHECK:STDOUT: %N: = namespace [template] { // CHECK:STDOUT: .F1 = %F1.decl -// CHECK:STDOUT: .C = %C.decl.loc12 // CHECK:STDOUT: .F2 = %F2.decl // CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl.loc5: type = class_decl @C.1 [template = constants.%C.1] {} {} // CHECK:STDOUT: %F1.decl: %F1.type = fn_decl @F1 [template = constants.%F1] { -// CHECK:STDOUT: %x.patt: %C.1 = binding_pattern x -// CHECK:STDOUT: %x.param_patt: %C.1 = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: %x.patt: %C = binding_pattern x +// CHECK:STDOUT: %x.param_patt: %C = value_param_pattern %x.patt, runtime_param0 // CHECK:STDOUT: } { -// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl.loc5 [template = constants.%C.1] -// CHECK:STDOUT: %x.param: %C.1 = value_param runtime_param0 -// CHECK:STDOUT: %x: %C.1 = bind_name x, %x.param +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %x.param: %C = value_param runtime_param0 +// CHECK:STDOUT: %x: %C = bind_name x, %x.param // CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl.loc12: type = class_decl @C.2 [template = constants.%C.2] {} {} +// CHECK:STDOUT: %.decl: type = class_decl @.1 [template = constants.%.1] {} {} // CHECK:STDOUT: %F2.decl: %F2.type = fn_decl @F2 [template = constants.%F2] { -// CHECK:STDOUT: %x.patt: %C.2 = binding_pattern x -// CHECK:STDOUT: %x.param_patt: %C.2 = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: %x.patt: %C = binding_pattern x +// CHECK:STDOUT: %x.param_patt: %C = value_param_pattern %x.patt, runtime_param0 // CHECK:STDOUT: } { -// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl.loc12 [template = constants.%C.2] -// CHECK:STDOUT: %x.param: %C.2 = value_param runtime_param0 -// CHECK:STDOUT: %x: %C.2 = bind_name x, %x.param +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %x.param: %C = value_param runtime_param0 +// CHECK:STDOUT: %x: %C = bind_name x, %x.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @C.1 { +// CHECK:STDOUT: class @C { // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C.1 +// CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @C.2 { +// CHECK:STDOUT: class @.1 { // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C.2 +// CHECK:STDOUT: .Self = constants.%.1 // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @F1(%x.param_patt: %C.1); +// CHECK:STDOUT: fn @F1(%x.param_patt: %C); // CHECK:STDOUT: -// CHECK:STDOUT: fn @F2(%x.param_patt: %C.2) { +// CHECK:STDOUT: fn @F2(%x.param_patt: %C) { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %N.ref: = name_ref N, file.%N [template = file.%N] // CHECK:STDOUT: %F1.ref: %F1.type = name_ref F1, file.%F1.decl [template = constants.%F1] -// CHECK:STDOUT: %x.ref: %C.2 = name_ref x, %x -// CHECK:STDOUT: %.loc22: %C.1 = converted %x.ref, [template = ] -// CHECK:STDOUT: %F1.call: init %empty_tuple.type = call %F1.ref() +// CHECK:STDOUT: %x.ref: %C = name_ref x, %x +// CHECK:STDOUT: %F1.call: init %empty_tuple.type = call %F1.ref(%x.ref) // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_poison_multiple_scopes.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: %D1: type = class_type @D1 [template] +// CHECK:STDOUT: %D2.type: type = facet_type <@D2> [template] +// CHECK:STDOUT: %Self.1: %D2.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: %D3.1: type = class_type @D3 [template] +// CHECK:STDOUT: %D3.2: type = class_type @D3, @D3(%Self.1) [symbolic] +// CHECK:STDOUT: %F.type: type = fn_type @F, @D3(%Self.1) [symbolic] +// CHECK:STDOUT: %F: %F.type = struct_value () [symbolic] +// CHECK:STDOUT: %.1: type = class_type @.1 [template] +// CHECK:STDOUT: %.2: type = class_type @.1, @.1(%Self.1) [symbolic] +// CHECK:STDOUT: %.3: type = class_type @.2 [template] +// CHECK:STDOUT: %.4: type = class_type @.2, @.2(%Self.1) [symbolic] +// CHECK:STDOUT: %.5: type = class_type @.3 [template] +// CHECK:STDOUT: %.6: type = class_type @.4 [template] +// CHECK:STDOUT: %.type: type = facet_type <@.6> [template] +// CHECK:STDOUT: %Self.2: %.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: %.7: type = class_type @.5 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .N1 = %N1 +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} +// CHECK:STDOUT: %N1: = namespace [template] { +// CHECK:STDOUT: .N2 = %N2 +// CHECK:STDOUT: } +// CHECK:STDOUT: %N2: = namespace [template] { +// CHECK:STDOUT: .N3 = %N3 +// CHECK:STDOUT: } +// CHECK:STDOUT: %N3: = namespace [template] { +// CHECK:STDOUT: .D1 = %D1.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %D1.decl: type = class_decl @D1 [template = constants.%D1] {} {} +// CHECK:STDOUT: %.decl.loc49: type = class_decl @.4 [template = constants.%.6] {} {} +// CHECK:STDOUT: %.decl.loc56: type = interface_decl @.6 [template = constants.%.type] {} {} +// CHECK:STDOUT: %.decl.loc62: type = class_decl @.5 [template = constants.%.7] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @D2 { +// CHECK:STDOUT: %Self: %D2.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.1] +// CHECK:STDOUT: %D3.decl: type = class_decl @D3 [template = constants.%D3.1] {} {} +// CHECK:STDOUT: %.decl: type = class_decl @.2 [template = constants.%.3] {} {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .D3 = %D3.decl +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @.6 { +// CHECK:STDOUT: %Self: %.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @D1 { +// CHECK:STDOUT: %D2.decl: type = interface_decl @D2 [template = constants.%D2.type] {} {} +// CHECK:STDOUT: %.decl: type = class_decl @.3 [template = constants.%.5] {} {} +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%D1 +// CHECK:STDOUT: .D2 = %D2.decl +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @D3(@D2.%Self: %D2.type) { +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %Self: %D2.type = bind_symbolic_name Self, 0 [symbolic = %Self (constants.%Self.1)] +// CHECK:STDOUT: %F.type: type = fn_type @F, @D3(%Self) [symbolic = %F.type (constants.%F.type)] +// CHECK:STDOUT: %F: @D3.%F.type (%F.type) = struct_value () [symbolic = %F (constants.%F)] +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %F.decl: @D3.%F.type (%F.type) = fn_decl @F [symbolic = @D3.%F (constants.%F)] { +// CHECK:STDOUT: %x.patt: %C = binding_pattern x +// CHECK:STDOUT: %x.param_patt: %C = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: %x.param: %C = value_param runtime_param0 +// CHECK:STDOUT: %x: %C = bind_name x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %.decl: type = class_decl @.1 [template = constants.%.1] {} {} +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%D3.2 +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @.1(@D2.%Self: %D2.type) { +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%.2 +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @.2(@D2.%Self: %D2.type) { +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%.4 +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @.3 { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%.5 +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @.4 { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%.6 +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @.5 { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%.7 +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @F(@D2.%Self: %D2.type) { +// CHECK:STDOUT: +// CHECK:STDOUT: fn(%x.param_patt: %C); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @D3(constants.%Self.1) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%Self.1) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @.1(constants.%Self.1) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @D3(%Self) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @.2(constants.%Self.1) {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_alias.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .N = %N +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} +// CHECK:STDOUT: %N: = namespace [template] {} +// CHECK:STDOUT: %C.ref: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %.loc11: type = bind_alias , %C.decl [template = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- ignored_poison_in_import.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %import_ref.1 = import_ref Main//poison, C, unloaded +// CHECK:STDOUT: %import_ref.2: = import_ref Main//poison, N, loaded +// CHECK:STDOUT: %N: = namespace %import_ref.2, [template] { +// CHECK:STDOUT: .F1 = %import_ref.3 +// CHECK:STDOUT: .C = file.%C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = imports.%import_ref.1 +// CHECK:STDOUT: .N = imports.%N +// CHECK:STDOUT: } +// CHECK:STDOUT: %default.import = import +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- poison.impl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %import_ref.1 = import_ref Main//poison, C, unloaded +// CHECK:STDOUT: %import_ref.2: = import_ref Main//poison, N, loaded +// CHECK:STDOUT: %N: = namespace %import_ref.2, [template] { +// CHECK:STDOUT: .F1 = %import_ref.3 +// CHECK:STDOUT: .C = file.%C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .C = imports.%import_ref.1 +// CHECK:STDOUT: .N = imports.%N +// CHECK:STDOUT: } +// CHECK:STDOUT: %default.import.loc2_6.1 = import +// CHECK:STDOUT: %default.import.loc2_6.2 = import +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index ceb0df8691386..bd7eb61b566d3 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -318,6 +318,8 @@ CARBON_DIAGNOSTIC_KIND(RealMantissaTooLargeForI64) CARBON_DIAGNOSTIC_KIND(RealExponentTooLargeForI64) CARBON_DIAGNOSTIC_KIND(NameDeclDuplicate) CARBON_DIAGNOSTIC_KIND(NameDeclPrevious) +CARBON_DIAGNOSTIC_KIND(NameUseBeforeDecl) +CARBON_DIAGNOSTIC_KIND(NameUseBeforeDeclNote) CARBON_DIAGNOSTIC_KIND(RepeatedConst) CARBON_DIAGNOSTIC_KIND(IncompleteTypeInAdaptDecl) CARBON_DIAGNOSTIC_KIND(IncompleteTypeInBaseDecl) diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 79cd833ea3183..6ee487912caf0 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -626,6 +626,10 @@ class FormatterImpl { } for (auto [name_id, inst_id, access_kind] : scope.entries()) { + if (inst_id.is_poisoned()) { + // TODO: Add poisoned names. + continue; + } Indent(); out_ << "."; FormatName(name_id); diff --git a/toolchain/sem_ir/ids.cpp b/toolchain/sem_ir/ids.cpp index b32b403a2a8a7..696d7cdf280c7 100644 --- a/toolchain/sem_ir/ids.cpp +++ b/toolchain/sem_ir/ids.cpp @@ -12,6 +12,8 @@ namespace Carbon::SemIR { auto InstId::Print(llvm::raw_ostream& out) const -> void { if (IsSingletonInstId(*this)) { out << Label << "(" << SingletonInstKinds[index] << ")"; + } else if (is_poisoned()) { + out << ""; } else { IdBase::Print(out); } diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index 4fb961c5e2cf5..8d98d70978967 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -39,12 +39,19 @@ struct InstId : public IdBase { // An explicitly invalid ID. static const InstId Invalid; + // Represents that the name in this scope was poisoned by using it without + // qualifications. + static const InstId PoisonedName; + using IdBase::IdBase; + constexpr auto is_poisoned() const -> bool { return *this == PoisonedName; } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr InstId InstId::Invalid = InstId(InvalidIndex); +constexpr InstId InstId::PoisonedName = InstId(InvalidIndex - 1); // An ID of an instruction that is referenced absolutely by another instruction. // This should only be used as the type of a field within a typed instruction diff --git a/toolchain/sem_ir/name_scope.cpp b/toolchain/sem_ir/name_scope.cpp index bd064afc9fcbd..811aa72d7f9ff 100644 --- a/toolchain/sem_ir/name_scope.cpp +++ b/toolchain/sem_ir/name_scope.cpp @@ -22,6 +22,9 @@ auto NameScope::Print(llvm::raw_ostream& out) const -> void { out << ", names: {"; llvm::ListSeparator sep; for (auto entry : names_) { + if (entry.inst_id.is_poisoned()) { + continue; + } out << sep << entry.name_id << ": " << entry.inst_id; } out << "}"; @@ -30,6 +33,9 @@ auto NameScope::Print(llvm::raw_ostream& out) const -> void { } auto NameScope::AddRequired(Entry name_entry) -> void { + CARBON_CHECK(!name_entry.inst_id.is_poisoned(), + "Cannot add a poisoned name: {0}. Use AddPoison()", + name_entry.name_id); auto add_name = [&] { EntryId index(names_.size()); names_.push_back(name_entry); @@ -43,6 +49,8 @@ auto NameScope::AddRequired(Entry name_entry) -> void { auto NameScope::LookupOrAdd(SemIR::NameId name_id, InstId inst_id, AccessKind access_kind) -> std::pair { + CARBON_CHECK(!inst_id.is_poisoned(), + "Cannot add a poisoned name: {0}. Use AddPoison()", name_id); auto insert_result = name_map_.Insert(name_id, EntryId(names_.size())); if (!insert_result.is_inserted()) { return {false, EntryId(insert_result.value())}; @@ -53,6 +61,15 @@ auto NameScope::LookupOrAdd(SemIR::NameId name_id, InstId inst_id, return {true, EntryId(names_.size() - 1)}; } +auto NameScope::AddPoison(NameId name_id) -> void { + auto insert_result = name_map_.Insert(name_id, EntryId(names_.size())); + CARBON_CHECK(insert_result.is_inserted(), + "Trying to poison an existing name: {0}", name_id); + names_.push_back({.name_id = name_id, + .inst_id = InstId::PoisonedName, + .access_kind = AccessKind::Public}); +} + auto NameScopeStore::GetInstIfValid(NameScopeId scope_id) const -> std::pair> { if (!scope_id.is_valid()) { diff --git a/toolchain/sem_ir/name_scope.h b/toolchain/sem_ir/name_scope.h index 0de02513db5aa..860666183e6d2 100644 --- a/toolchain/sem_ir/name_scope.h +++ b/toolchain/sem_ir/name_scope.h @@ -42,6 +42,7 @@ class NameScope : public Printable { auto entries() const -> llvm::ArrayRef { return names_; } // Get a specific Name entry based on an EntryId that return from a lookup. + // // The Entry could become invalidated if the scope object is invalidated or if // a name is added. auto GetEntry(EntryId entry_id) const -> const Entry& { @@ -50,7 +51,7 @@ class NameScope : public Printable { auto GetEntry(EntryId entry_id) -> Entry& { return names_[entry_id.index]; } // Searches for the given name and returns an EntryId if found or nullopt if - // not. + // not. The returned entry may be poisoned. auto Lookup(NameId name_id) const -> std::optional { auto lookup = name_map_.Lookup(name_id); if (!lookup) { @@ -59,15 +60,20 @@ class NameScope : public Printable { return lookup.value(); } - // Adds a new name known to not exist. + // Adds a new name known to not exist. Must not be poisoned. auto AddRequired(Entry name_entry) -> void; - // If the given name already exists, return true and an EntryId. - // If not, adds the name using inst_id and access_kind and returns false and - // an EntryId. + // If the given name already exists, return true with the EntryId; the entry + // might be poisoned. Otherwise, adds the name using inst_id and access_kind + // and returns false with the new EntryId. + // + // This cannot be used to add poisoned entries; use AddPoison instead. auto LookupOrAdd(SemIR::NameId name_id, InstId inst_id, AccessKind access_kind) -> std::pair; + // Adds a new poisoned name. + auto AddPoison(NameId name_id) -> void; + auto extended_scopes() const -> llvm::ArrayRef { return extended_scopes_; } @@ -111,7 +117,8 @@ class NameScope : public Printable { } private: - // Names in the scope. + // Names in the scope, including poisoned names. + // // Entries could become invalidated if the scope object is invalidated or if a // name is added. // @@ -172,7 +179,7 @@ class NameScopeStore { } // Adds a name that is required to exist in a name scope, such as `Self`. - // These must never conflict. + // The name must never conflict. inst_id must not be poisoned. auto AddRequiredName(NameScopeId scope_id, NameId name_id, InstId inst_id) -> void { Get(scope_id).AddRequired({.name_id = name_id, diff --git a/toolchain/sem_ir/name_scope_test.cpp b/toolchain/sem_ir/name_scope_test.cpp index fa6068d80b27b..1b246d9673037 100644 --- a/toolchain/sem_ir/name_scope_test.cpp +++ b/toolchain/sem_ir/name_scope_test.cpp @@ -146,6 +146,43 @@ TEST(NameScope, LookupOrAdd) { } } +TEST(NameScope, Poison) { + int id = 0; + + InstId scope_inst_id(++id); + NameId scope_name_id(++id); + NameScopeId parent_scope_id(++id); + NameScope name_scope(scope_inst_id, scope_name_id, parent_scope_id); + + NameId poison1(++id); + name_scope.AddPoison(poison1); + EXPECT_THAT(name_scope.entries(), + ElementsAre(NameScopeEntryEquals( + NameScope::Entry({.name_id = poison1, + .inst_id = InstId::PoisonedName, + .access_kind = AccessKind::Public})))); + + NameId poison2(++id); + name_scope.AddPoison(poison2); + EXPECT_THAT(name_scope.entries(), + ElementsAre(NameScopeEntryEquals(NameScope::Entry( + {.name_id = poison1, + .inst_id = InstId::PoisonedName, + .access_kind = AccessKind::Public})), + NameScopeEntryEquals(NameScope::Entry( + {.name_id = poison2, + .inst_id = InstId::PoisonedName, + .access_kind = AccessKind::Public})))); + + auto lookup = name_scope.Lookup(poison1); + ASSERT_NE(lookup, std::nullopt); + EXPECT_THAT(name_scope.GetEntry(*lookup), + NameScopeEntryEquals( + NameScope::Entry({.name_id = poison1, + .inst_id = InstId::PoisonedName, + .access_kind = AccessKind::Public}))); +} + TEST(NameScope, ExtendedScopes) { int id = 0;