diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 7f881d1980f87..f35a74f3b750b 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -218,6 +218,14 @@ auto Context::DiagnoseDuplicateName(SemIRLoc dup_def, SemIRLoc prev_def) .Emit(); } +auto Context::DiagnosePoisonedName(SemIRLoc dup_def) -> void { + CARBON_DIAGNOSTIC( + NameDeclPoisoned, Error, + "cannot declare this name in this scope since is was already used " + "without qualification in the context of this scope"); + emitter_->Build(dup_def, NameDeclPoisoned).Emit(); +} + auto Context::DiagnoseNameNotFound(SemIRLoc loc, SemIR::NameId name_id) -> void { CARBON_DIAGNOSTIC(NameNotFound, Error, "name `{0}` not found", SemIR::NameId); @@ -328,6 +336,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. + std::vector scopes_to_poison; for (auto [index, lookup_scope_id, specific_id] : llvm::reverse(non_lexical_scopes)) { if (auto non_lexical_result = @@ -335,9 +345,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()) { + non_lexical_result.inst_id.is_valid() && + !non_lexical_result.inst_id.is_poisoned()) { + // 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}); } if (lexical_result.is_valid()) { @@ -589,7 +607,8 @@ auto Context::LookupQualifiedName(SemIRLoc loc, 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()) { DiagnoseNameNotFound(loc, name_id); diff --git a/toolchain/check/context.h b/toolchain/check/context.h index a281ce359893e..91ba7ab767880 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -253,6 +253,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 dup_def) -> 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 c8b795e6243c8..c09b84b33ec86 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"); } @@ -166,12 +169,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); } @@ -259,6 +265,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. @@ -276,9 +285,15 @@ 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::Unresolved: + case DeclNameStack::NameContext::State::Poisoned: // Because more qualifiers were found, we diagnose that the earlier // qualifier failed to resolve. context.DiagnoseNameNotFound(name_context.loc_id, @@ -366,6 +381,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 1a84eb4cc3417..30ee7807afc87 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 9e9acb41aac23..61bfa27f50f74 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 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); @@ -545,7 +551,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_let_and_var.cpp b/toolchain/check/handle_let_and_var.cpp index 4df6051a10b1c..4a258a36abd1a 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/import.cpp b/toolchain/check/import.cpp index a13f314a625c6..d0547dc0f118c 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) { diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index d8acc8ff02773..c7c16f6018c5d 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -1195,6 +1195,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, @@ -2714,6 +2717,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/class/no_prelude/indirect_import_member.carbon b/toolchain/check/testdata/class/no_prelude/indirect_import_member.carbon index 5612b9a0dd685..c39a1d506ac2b 100644 --- a/toolchain/check/testdata/class/no_prelude/indirect_import_member.carbon +++ b/toolchain/check/testdata/class/no_prelude/indirect_import_member.carbon @@ -47,7 +47,7 @@ library "[[@TEST_NAME]]"; import library "c"; class D { - alias C = C; + alias C = package.C; } // --- f.carbon @@ -214,6 +214,7 @@ var x: () = D.C.F(); // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @D { +// CHECK:STDOUT: %package.ref: = name_ref package, package [template = package] // CHECK:STDOUT: %C.ref: type = name_ref C, imports.%import_ref.1 [template = constants.%C] // CHECK:STDOUT: %C: type = bind_alias C, imports.%import_ref.1 [template = constants.%C] // CHECK:STDOUT: %.loc8: = complete_type_witness %.1 [template = constants.%.2] @@ -402,12 +403,12 @@ var x: () = D.C.F(); // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %import_ref.1: type = import_ref Main//e, inst+3, loaded [template = constants.%D] -// CHECK:STDOUT: %import_ref.2: = import_ref Main//e, inst+14, loaded [template = constants.%.2] +// CHECK:STDOUT: %import_ref.2: = import_ref Main//e, inst+15, loaded [template = constants.%.2] // CHECK:STDOUT: %import_ref.3 = import_ref Main//e, inst+4, unloaded -// CHECK:STDOUT: %import_ref.4: type = import_ref Main//e, inst+13, loaded [template = constants.%C] -// CHECK:STDOUT: %import_ref.5: = import_ref Main//e, inst+9, loaded [template = constants.%.2] -// CHECK:STDOUT: %import_ref.6 = import_ref Main//e, inst+10, unloaded -// CHECK:STDOUT: %import_ref.7: %F.type = import_ref Main//e, inst+11, loaded [template = constants.%F] +// CHECK:STDOUT: %import_ref.4: type = import_ref Main//e, inst+14, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.5: = import_ref Main//e, inst+10, loaded [template = constants.%.2] +// CHECK:STDOUT: %import_ref.6 = import_ref Main//e, inst+11, unloaded +// CHECK:STDOUT: %import_ref.7: %F.type = import_ref Main//e, inst+12, loaded [template = constants.%F] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -462,12 +463,12 @@ var x: () = D.C.F(); // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %import_ref.1: type = import_ref Main//e, inst+3, loaded [template = constants.%D] -// CHECK:STDOUT: %import_ref.2: = import_ref Main//e, inst+14, loaded [template = constants.%.2] +// CHECK:STDOUT: %import_ref.2: = import_ref Main//e, inst+15, loaded [template = constants.%.2] // CHECK:STDOUT: %import_ref.3 = import_ref Main//e, inst+4, unloaded -// CHECK:STDOUT: %import_ref.4: type = import_ref Main//e, inst+13, loaded [template = constants.%C] -// CHECK:STDOUT: %import_ref.5: = import_ref Main//e, inst+9, loaded [template = constants.%.2] -// CHECK:STDOUT: %import_ref.6 = import_ref Main//e, inst+10, unloaded -// CHECK:STDOUT: %import_ref.7: %F.type = import_ref Main//e, inst+11, loaded [template = constants.%F] +// CHECK:STDOUT: %import_ref.4: type = import_ref Main//e, inst+14, loaded [template = constants.%C] +// CHECK:STDOUT: %import_ref.5: = import_ref Main//e, inst+10, loaded [template = constants.%.2] +// CHECK:STDOUT: %import_ref.6 = import_ref Main//e, inst+11, unloaded +// CHECK:STDOUT: %import_ref.7: %F.type = import_ref Main//e, inst+12, loaded [template = constants.%F] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { 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 e6b439e3e8a73..e86801c90cd44 100644 --- a/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon +++ b/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon @@ -12,48 +12,152 @@ 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_without_usage.carbon + +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_without_usage.carbon:[[@LINE+4]]:1: error: cannot declare this name in this scope since is was already used without qualification in the context of this scope [NameDeclPoisoned] +// CHECK:STDERR: class N.C {} +// CHECK:STDERR: ^~~~~~~~~~~ +// CHECK:STDERR: class N.C {} // --- fail_poison_with_usage.carbon library "[[@TEST_NAME]]"; -namespace N; 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_with_usage.carbon:[[@LINE+4]]:1: error: cannot declare this name in this scope since is was already used without qualification in the context of this scope [NameDeclPoisoned] +// CHECK:STDERR: class N.C {} +// CHECK:STDERR: ^~~~~~~~~~~ +// CHECK:STDERR: +class N.C {} + +// 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 + +library "[[@TEST_NAME]]"; + +class C {}; + +namespace N1; +namespace N1.N2; +namespace N1.N2.N3; +class N1.N2.N3.D1 { + class 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+4]]:7: error: cannot declare this name in this scope since is was already used without qualification in the context of this scope [NameDeclPoisoned] + // CHECK:STDERR: class C {} + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: + class C {} + } + // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+4]]:5: error: cannot declare this name in this scope since is was already used without qualification in the context of this scope [NameDeclPoisoned] + // CHECK:STDERR: class C {} + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: + class C {} + } + // CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+4]]:3: error: cannot declare this name in this scope since is was already used without qualification in the context of this scope [NameDeclPoisoned] + // CHECK:STDERR: class C {} + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: + class C {} +} + +// CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+4]]:1: error: cannot declare this name in this scope since is was already used without qualification in the context of this scope [NameDeclPoisoned] +// CHECK:STDERR: class N1.C {} +// CHECK:STDERR: ^~~~~~~~~~~~ +// CHECK:STDERR: +class N1.C {} + +// CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+4]]:1: error: cannot declare this name in this scope since is was already used without qualification in the context of this scope [NameDeclPoisoned] +// CHECK:STDERR: class N1.N2.C {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~ +// CHECK:STDERR: +class N1.N2.C {} + +// CHECK:STDERR: fail_poison_multiple_scopes.carbon:[[@LINE+4]]:1: error: cannot declare this name in this scope since is was already used without qualification in the context of this scope [NameDeclPoisoned] +// CHECK:STDERR: class N1.N2.N3.C {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +class N1.N2.N3.C {} + +// --- fail_alias.carbon + +library "[[@TEST_NAME]]"; + +class C {} + +namespace N; +// CHECK:STDERR: fail_alias.carbon:[[@LINE+6]]:9: error: cannot declare this name in this scope since is was already used without qualification in the context of this scope [NameDeclPoisoned] +// CHECK:STDERR: alias N.C = C; +// CHECK:STDERR: ^ +// CHECK:STDERR: +// CHECK:STDERR: fail_poison_import: error: file extension of `.carbon` required for api [IncorrectExtension] +// CHECK:STDERR: +alias N.C = C; + +// --- fail_poison_import + +import library "poison"; + // TODO: Should fail here since C was poisoned for namespace N when it was used -// in N context without qualification. +// in N context without qualification in the imported library. 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] +// TODO: Should not fail here since N.C failed to be defined so both N.F1() and +// N.F2() take C. +// CHECK:STDERR: fail_poison_import:[[@LINE+7]]: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: fail_poison_import:[[@LINE-11]]:1: in import [InImport] +// CHECK:STDERR: poison.carbon:8:9: note: initializing function parameter [InCallToFunctionParam] // CHECK:STDERR: fn N.F1(x: C); // CHECK:STDERR: ^~~~ fn N.F2(x: C) { N.F1(x); } @@ -74,15 +178,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 @@ -103,11 +207,11 @@ fn N.F2(x: C) { N.F1(x); } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C.1 { -// CHECK:STDOUT: %.loc5: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: %.loc4: = complete_type_witness %.1 [template = constants.%.2] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C.1 -// CHECK:STDOUT: complete_type_witness = %.loc5 +// CHECK:STDOUT: complete_type_witness = %.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C.2 { @@ -129,125 +233,396 @@ 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: %.1: type = struct_type {} [template] // CHECK:STDOUT: %.2: = complete_type_witness %.1 [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: %.loc5: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %.loc4: = complete_type_witness %.1 [template = constants.%.2] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C.1 -// CHECK:STDOUT: complete_type_witness = %.loc5 +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: complete_type_witness = %.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @C.2 { -// CHECK:STDOUT: %.loc12: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: fn @F1(%x.param_patt: %C); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_poison_without_usage.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.2: = complete_type_witness %.1 [template] +// CHECK:STDOUT: %F1.type: type = fn_type @F1 [template] +// CHECK:STDOUT: %F1: %F1.type = struct_value () [template] +// CHECK:STDOUT: %.3: 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.%.3] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %.loc4: = complete_type_witness %.1 [template = constants.%.2] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C.2 -// CHECK:STDOUT: complete_type_witness = %.loc12 +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: complete_type_witness = %.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @F1(%x.param_patt: %C.1); +// CHECK:STDOUT: class @.1 { +// CHECK:STDOUT: %.loc16: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%.3 +// CHECK:STDOUT: complete_type_witness = %.loc16 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F1(%x.param_patt: %C); // 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: %.1: type = struct_type {} [template] // CHECK:STDOUT: %.2: = complete_type_witness %.1 [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: %.3: 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.%.3] {} {} // 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: %.loc5: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %.loc4: = complete_type_witness %.1 [template = constants.%.2] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C.1 -// CHECK:STDOUT: complete_type_witness = %.loc5 +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: complete_type_witness = %.loc4 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @C.2 { -// CHECK:STDOUT: %.loc12: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: class @.1 { +// CHECK:STDOUT: %.loc16: = complete_type_witness %.1 [template = constants.%.2] // CHECK:STDOUT: // CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%C.2 -// CHECK:STDOUT: complete_type_witness = %.loc12 +// CHECK:STDOUT: .Self = constants.%.3 +// CHECK:STDOUT: complete_type_witness = %.loc16 // 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: %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: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.2: = complete_type_witness %.1 [template] +// CHECK:STDOUT: %D1: type = class_type @D1 [template] +// CHECK:STDOUT: %D2: type = class_type @D2 [template] +// CHECK:STDOUT: %D3: type = class_type @D3 [template] +// CHECK:STDOUT: %F.type: type = fn_type @F [template] +// CHECK:STDOUT: %F: %F.type = struct_value () [template] +// CHECK:STDOUT: %.3: type = class_type @.1 [template] +// CHECK:STDOUT: %.4: type = class_type @.2 [template] +// CHECK:STDOUT: %.5: type = class_type @.3 [template] +// CHECK:STDOUT: %.6: type = class_type @.4 [template] +// CHECK:STDOUT: %.7: type = class_type @.5 [template] +// CHECK:STDOUT: %.8: type = class_type @.6 [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.loc44: type = class_decl @.4 [template = constants.%.6] {} {} +// CHECK:STDOUT: %.decl.loc50: type = class_decl @.5 [template = constants.%.7] {} {} +// CHECK:STDOUT: %.decl.loc56: type = class_decl @.6 [template = constants.%.8] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %.loc4: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: complete_type_witness = %.loc4 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @D1 { +// CHECK:STDOUT: %D2.decl: type = class_decl @D2 [template = constants.%D2] {} {} +// CHECK:STDOUT: %.decl: type = class_decl @.3 [template = constants.%.5] {} {} +// CHECK:STDOUT: %.loc38: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%D1 +// CHECK:STDOUT: .D2 = %D2.decl +// CHECK:STDOUT: complete_type_witness = %.loc38 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @D2 { +// CHECK:STDOUT: %D3.decl: type = class_decl @D3 [template = constants.%D3] {} {} +// CHECK:STDOUT: %.decl: type = class_decl @.2 [template = constants.%.4] {} {} +// CHECK:STDOUT: %.loc32: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%D2 +// CHECK:STDOUT: .D3 = %D3.decl +// CHECK:STDOUT: complete_type_witness = %.loc32 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @D3 { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = 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.%.3] {} {} +// CHECK:STDOUT: %.loc26: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%D3 +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: complete_type_witness = %.loc26 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @.1 { +// CHECK:STDOUT: %.loc25: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%.3 +// CHECK:STDOUT: complete_type_witness = %.loc25 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @.2 { +// CHECK:STDOUT: %.loc31: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%.4 +// CHECK:STDOUT: complete_type_witness = %.loc31 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @.3 { +// CHECK:STDOUT: %.loc37: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%.5 +// CHECK:STDOUT: complete_type_witness = %.loc37 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @.4 { +// CHECK:STDOUT: %.loc44: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%.6 +// CHECK:STDOUT: complete_type_witness = %.loc44 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @.5 { +// CHECK:STDOUT: %.loc50: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%.7 +// CHECK:STDOUT: complete_type_witness = %.loc50 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @.6 { +// CHECK:STDOUT: %.loc56: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%.8 +// CHECK:STDOUT: complete_type_witness = %.loc56 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%x.param_patt: %C); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_alias.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.2: = complete_type_witness %.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: %C.ref: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %.loc13: type = bind_alias , %C.decl [template = constants.%C] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %.loc4: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: complete_type_witness = %.loc4 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_poison_import +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C.1: type = class_type @C.1 [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.2: = complete_type_witness %.1 [template] +// CHECK:STDOUT: %F2.type: type = fn_type @F2 [template] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [template] +// CHECK:STDOUT: %F2: %F2.type = struct_value () [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: imports { +// CHECK:STDOUT: %import_ref.1 = import_ref Main//poison, inst+1, unloaded +// CHECK:STDOUT: %import_ref.2: = import_ref Main//poison, inst+6, loaded +// CHECK:STDOUT: %N: = namespace %import_ref.2, [template] { +// CHECK:STDOUT: .F1 = %import_ref.3 +// CHECK:STDOUT: .C = file.%C.decl +// CHECK:STDOUT: .F2 = file.%F2.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.3: %F1.type = import_ref Main//poison, inst+12, loaded [template = constants.%F1] +// CHECK:STDOUT: %import_ref.4: = import_ref Main//poison, inst+4, loaded [template = constants.%.2] +// CHECK:STDOUT: %import_ref.5 = import_ref Main//poison, inst+2, unloaded +// 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.1 [template = constants.%C.1] {} {} +// CHECK:STDOUT: %F2.decl: %F2.type = fn_decl @F2 [template = constants.%F2] { +// 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: } { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [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: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C.1 { +// CHECK:STDOUT: %.loc6: = complete_type_witness %.1 [template = constants.%.2] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C.1 +// CHECK:STDOUT: complete_type_witness = %.loc6 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C.2 { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = imports.%import_ref.5 +// CHECK:STDOUT: complete_type_witness = imports.%import_ref.4 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F2(%x.param_patt: %C.1) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %N.ref: = name_ref N, imports.%N [template = imports.%N] +// CHECK:STDOUT: %F1.ref: %F1.type = name_ref F1, imports.%import_ref.3 [template = constants.%F1] +// CHECK:STDOUT: %x.ref: %C.1 = name_ref x, %x +// CHECK:STDOUT: %.loc17: %C.2 = converted %x.ref, [template = ] // CHECK:STDOUT: %F1.call: init %empty_tuple.type = call %F1.ref() // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: fn @F1(%x.param_patt: %C.2); +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 0c1ca5e674cdf..4da4d763548a2 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -313,6 +313,7 @@ CARBON_DIAGNOSTIC_KIND(CompoundMemberAccessDoesNotUseBase) CARBON_DIAGNOSTIC_KIND(RealMantissaTooLargeForI64) CARBON_DIAGNOSTIC_KIND(RealExponentTooLargeForI64) CARBON_DIAGNOSTIC_KIND(NameDeclDuplicate) +CARBON_DIAGNOSTIC_KIND(NameDeclPoisoned) CARBON_DIAGNOSTIC_KIND(NameDeclPrevious) CARBON_DIAGNOSTIC_KIND(RepeatedConst) CARBON_DIAGNOSTIC_KIND(IncompleteTypeInAdaptDecl) diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 0bcfb8570e4a9..d7142734d59b0 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -603,6 +603,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.h b/toolchain/sem_ir/ids.h index 6d7d431bfa0a8..4138072d51639 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -39,6 +39,10 @@ struct InstId : public IdBase, public Printable { // 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; + // Builtin inst IDs. #define CARBON_SEM_IR_BUILTIN_INST_KIND(Name) static const InstId Builtin##Name; #include "toolchain/sem_ir/inst_kind.def" @@ -54,10 +58,14 @@ struct InstId : public IdBase, public Printable { using IdBase::IdBase; + constexpr auto is_poisoned() const -> bool { + return index == PoisonedNameIndex; + } + // Returns true if the instruction is a builtin. Requires is_valid. auto is_builtin() const -> bool { CARBON_CHECK(is_valid()); - return index < BuiltinInstKind::ValidCount; + return !is_poisoned() && index < BuiltinInstKind::ValidCount; } // Returns the BuiltinInstKind. Requires is_builtin. @@ -70,6 +78,8 @@ struct InstId : public IdBase, public Printable { out << "inst"; if (!is_valid()) { IdBase::Print(out); + } else if (is_poisoned()) { + out << ""; } else if (is_builtin()) { out << builtin_inst_kind(); } else { @@ -78,9 +88,12 @@ struct InstId : public IdBase, public Printable { out << "+" << index - BuiltinInstKind::ValidCount; } } + + static constexpr int32_t PoisonedNameIndex = InvalidIndex - 1; }; constexpr InstId InstId::Invalid = InstId(InvalidIndex); +constexpr InstId InstId::PoisonedName = InstId(PoisonedNameIndex); #define CARBON_SEM_IR_BUILTIN_INST_KIND(Name) \ constexpr InstId InstId::Builtin##Name = \ diff --git a/toolchain/sem_ir/name_scope.cpp b/toolchain/sem_ir/name_scope.cpp index d87e9d2e96626..4182368215667 100644 --- a/toolchain/sem_ir/name_scope.cpp +++ b/toolchain/sem_ir/name_scope.cpp @@ -20,6 +20,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 << "}"; @@ -28,6 +31,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); @@ -46,9 +52,25 @@ auto NameScope::LookupOrAdd(SemIR::NameId name_id, InstId inst_id, return {false, EntryId(insert_result.value())}; } + CARBON_CHECK(!inst_id.is_poisoned(), + "Cannot add a poisoned name: {0}. Use AddPoison()", name_id); + names_.push_back( {.name_id = name_id, .inst_id = inst_id, .access_kind = access_kind}); return {true, EntryId(names_.size() - 1)}; } +auto NameScope::AddPoison(SemIR::NameId name_id) -> void { + auto insert_result = name_map_.Insert(name_id, EntryId(names_.size())); + if (insert_result.is_inserted()) { + names_.push_back({.name_id = name_id, + .inst_id = InstId::PoisonedName, + .access_kind = AccessKind::Public}); + return; + } + + CARBON_CHECK(GetEntry(insert_result.value()).inst_id.is_poisoned(), + "Trying to poison an existing non poisoned name: {0}", name_id); +} + } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/name_scope.h b/toolchain/sem_ir/name_scope.h index 34373a4e245c9..b8b1f44ca4e11 100644 --- a/toolchain/sem_ir/name_scope.h +++ b/toolchain/sem_ir/name_scope.h @@ -50,6 +50,7 @@ class NameScope : public Printable { // Searches for the given name and returns an EntryId if found or nullopt if // not. + // If the name is found, it might be a poisoned name. auto Lookup(NameId name_id) const -> std::optional { auto lookup = name_map_.Lookup(name_id); if (!lookup) { @@ -59,14 +60,19 @@ class NameScope : public Printable { } // 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 name is found, it might be a poisoned name. + // If the name is not found, adds the name using inst_id and access_kind and + // returns false and an EntryId. auto LookupOrAdd(SemIR::NameId name_id, InstId inst_id, AccessKind access_kind) -> std::pair; + // Adds a new poisoned name. + auto AddPoison(SemIR::NameId name_id) -> void; + auto extended_scopes() const -> llvm::ArrayRef { return extended_scopes_; } @@ -110,7 +116,7 @@ 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,6 +178,7 @@ class NameScopeStore { // Adds a name that is required to exist in a name scope, such as `Self`. // These 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 c34aa1d8f010e..25fd6af0cee7b 100644 --- a/toolchain/sem_ir/name_scope_test.cpp +++ b/toolchain/sem_ir/name_scope_test.cpp @@ -145,6 +145,44 @@ 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); + name_scope.AddPoison(poison2); // Ignored. + 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;