From 0d70091bdab7dd945966da0691de107dbff7c7da Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 13 Jan 2025 10:55:26 -0800 Subject: [PATCH] Fix introduction of class and interface names in local scopes. (#4793) When declaring a class (or interface), we create a scope that covers the entire class declaration. If the class was declared in a lexical scope, we would declare the class name in the innermost scope, which was the class's own scope instead of the enclosing lexical scope. Fix this by instead adding the name to the lexical scope at the start of the class declaration, not the lexical scope created to hold the class. For now, we reject if the class name would have been shadowed by a name that has already been declared within its scope, such as a generic parameter, so we only ever need to modify the end of the list of lexical lookup results for the class name. This appears to be sufficient to make local declarations and definitions of classes and interfaces work properly throughout check, though testing is pretty minimal so far. --- toolchain/check/context.cpp | 28 +- toolchain/check/context.h | 17 +- toolchain/check/decl_name_stack.cpp | 6 +- toolchain/check/scope_index.h | 2 + toolchain/check/scope_stack.cpp | 55 ++-- toolchain/check/scope_stack.h | 17 +- ...l_todo_local_class.carbon => local.carbon} | 23 +- toolchain/check/testdata/generic/local.carbon | 279 ++++++++++++++++++ .../interface/no_prelude/local.carbon | 95 ++++++ .../namespace/fail_not_top_level.carbon | 28 +- 10 files changed, 485 insertions(+), 65 deletions(-) rename toolchain/check/testdata/class/{fail_todo_local_class.carbon => local.carbon} (89%) create mode 100644 toolchain/check/testdata/generic/local.carbon create mode 100644 toolchain/check/testdata/interface/no_prelude/local.carbon diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index 331b9b55d172..eace61d26fae 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -304,20 +304,22 @@ auto Context::NoteUndefinedInterface(SemIR::InterfaceId interface_id, } } -auto Context::AddNameToLookup(SemIR::NameId name_id, SemIR::InstId target_id) - -> void { - if (auto existing = scope_stack().LookupOrAddName(name_id, target_id); +auto Context::AddNameToLookup(SemIR::NameId name_id, SemIR::InstId target_id, + ScopeIndex scope_index) -> void { + if (auto existing = + scope_stack().LookupOrAddName(name_id, target_id, scope_index); existing.is_valid()) { DiagnoseDuplicateName(target_id, existing); } } auto Context::LookupNameInDecl(SemIR::LocId loc_id, SemIR::NameId name_id, - SemIR::NameScopeId scope_id) + SemIR::NameScopeId scope_id, + ScopeIndex scope_index) -> std::pair { if (!scope_id.is_valid()) { - // Look for a name in the current scope only. There are two cases where the - // name would be in an outer scope: + // Look for a name in the specified scope or a scope nested within it only. + // There are two cases where the name would be in an outer scope: // // - The name is the sole component of the declared name: // @@ -339,7 +341,19 @@ auto Context::LookupNameInDecl(SemIR::LocId loc_id, SemIR::NameId name_id, // In this case, we're not in the correct scope to define a member of // class A, so we should reject, and we achieve this by not finding the // name A from the outer scope. - return {scope_stack().LookupInCurrentScope(name_id), false}; + // + // There is also one case where the name would be in an inner scope: + // + // - The name is redeclared by a parameter of the same entity: + // + // fn F() { + // class C(C:! type); + // } + // + // In this case, the class C is not a redeclaration of its parameter, but + // we find the parameter in order to diagnose a redeclaration error. + return {scope_stack().LookupInLexicalScopesWithin(name_id, scope_index), + false}; } else { // We do not look into `extend`ed scopes here. A qualified name in a // declaration must specify the exact scope in which the name was originally diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 0412b1a5c53b..ce7fb86ebb92 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -16,6 +16,7 @@ #include "toolchain/check/inst_block_stack.h" #include "toolchain/check/node_stack.h" #include "toolchain/check/param_and_arg_refs_stack.h" +#include "toolchain/check/scope_index.h" #include "toolchain/check/scope_stack.h" #include "toolchain/parse/node_ids.h" #include "toolchain/parse/tree.h" @@ -219,16 +220,20 @@ class Context { sem_ir().insts().SetLocId(inst_id, SemIR::LocId(node_id)); } - // Adds a name to name lookup. Prints a diagnostic for name conflicts. - auto AddNameToLookup(SemIR::NameId name_id, SemIR::InstId target_id) -> void; + // Adds a name to name lookup. Prints a diagnostic for name conflicts. If + // specified, `scope_index` specifies which lexical scope the name is inserted + // into, otherwise the name is inserted into the current scope. + auto AddNameToLookup(SemIR::NameId name_id, SemIR::InstId target_id, + ScopeIndex scope_index = ScopeIndex::Invalid) -> void; // Performs name lookup in a specified scope for a name appearing in a - // declaration. If scope_id is invalid, uses the current contextual scope. If - // found, returns the referenced instruction and false. If poisoned, returns - // an invalid instruction and true. + // declaration. If scope_id is invalid, performs lookup into the lexical scope + // specified by scope_index instead. If found, returns the referenced + // instruction and false. If poisoned, returns an invalid instruction and + // true. // TODO: For poisoned names, return the poisoning instruction. auto LookupNameInDecl(SemIR::LocId loc_id, SemIR::NameId name_id, - SemIR::NameScopeId scope_id) + SemIR::NameScopeId scope_id, ScopeIndex scope_index) -> std::pair; // Performs an unqualified name lookup, returning the referenced instruction. diff --git a/toolchain/check/decl_name_stack.cpp b/toolchain/check/decl_name_stack.cpp index 6aaa5f58297a..6d948b2ebfe1 100644 --- a/toolchain/check/decl_name_stack.cpp +++ b/toolchain/check/decl_name_stack.cpp @@ -134,7 +134,8 @@ auto DeclNameStack::AddName(NameContext name_context, SemIR::InstId target_id, case NameContext::State::Unresolved: if (!name_context.parent_scope_id.is_valid()) { - context_->AddNameToLookup(name_context.unresolved_name_id, target_id); + context_->AddNameToLookup(name_context.unresolved_name_id, target_id, + name_context.initial_scope_index); } else { auto& name_scope = context_->name_scopes().Get(name_context.parent_scope_id); @@ -262,7 +263,8 @@ auto DeclNameStack::ApplyAndLookupName(NameContext& name_context, // For identifier nodes, we need to perform a lookup on the identifier. auto [resolved_inst_id, is_poisoned] = context_->LookupNameInDecl( - name_context.loc_id, name_id, name_context.parent_scope_id); + name_context.loc_id, name_id, name_context.parent_scope_id, + name_context.initial_scope_index); if (is_poisoned) { name_context.unresolved_name_id = name_id; name_context.state = NameContext::State::Poisoned; diff --git a/toolchain/check/scope_index.h b/toolchain/check/scope_index.h index d8d71b5cd370..395fafff583c 100644 --- a/toolchain/check/scope_index.h +++ b/toolchain/check/scope_index.h @@ -20,11 +20,13 @@ namespace Carbon::Check { struct ScopeIndex : public IndexBase { static constexpr llvm::StringLiteral Label = "scope"; static const ScopeIndex Package; + static const ScopeIndex Invalid; using IndexBase::IndexBase; }; constexpr ScopeIndex ScopeIndex::Package = ScopeIndex(0); +constexpr ScopeIndex ScopeIndex::Invalid = ScopeIndex(InvalidIndex); } // namespace Carbon::Check diff --git a/toolchain/check/scope_stack.cpp b/toolchain/check/scope_stack.cpp index 019c30714c8c..1b1dca8e38ed 100644 --- a/toolchain/check/scope_stack.cpp +++ b/toolchain/check/scope_stack.cpp @@ -99,14 +99,16 @@ auto ScopeStack::PopTo(ScopeIndex index) -> void { PeekIndex()); } -auto ScopeStack::LookupInCurrentScope(SemIR::NameId name_id) -> SemIR::InstId { +auto ScopeStack::LookupInLexicalScopesWithin(SemIR::NameId name_id, + ScopeIndex scope_index) + -> SemIR::InstId { auto& lexical_results = lexical_lookup_.Get(name_id); if (lexical_results.empty()) { return SemIR::InstId::Invalid; } auto result = lexical_results.back(); - if (result.scope_index != PeekIndex()) { + if (result.scope_index < scope_index) { return SemIR::InstId::Invalid; } @@ -139,24 +141,43 @@ auto ScopeStack::LookupInLexicalScopes(SemIR::NameId name_id) llvm::ArrayRef(first_non_lexical_scope, non_lexical_scope_stack_.end())}; } -auto ScopeStack::LookupOrAddName(SemIR::NameId name_id, SemIR::InstId target_id) - -> SemIR::InstId { - if (!scope_stack_.back().names.Insert(name_id).is_inserted()) { - auto existing = lexical_lookup_.Get(name_id).back().inst_id; - CARBON_CHECK(existing.is_valid(), - "Name in scope but not in lexical lookups"); - return existing; +auto ScopeStack::LookupOrAddName(SemIR::NameId name_id, SemIR::InstId target_id, + ScopeIndex scope_index) -> SemIR::InstId { + // Find the corresponding scope depth. + // + // TODO: Consider passing in the depth rather than performing a scan for it. + // We only do this scan when declaring an entity such as a class within a + // function, so it should be relatively rare, but it's still not necesasry to + // recompute this. + int scope_depth = scope_stack_.size() - 1; + if (scope_index.is_valid()) { + scope_depth = + std::lower_bound(scope_stack_.begin(), scope_stack_.end(), scope_index, + [](const ScopeStackEntry& entry, ScopeIndex index) { + return entry.index < index; + }) - + scope_stack_.begin(); + CARBON_CHECK(scope_stack_[scope_depth].index == scope_index, + "Declaring name in scope that has already ended"); + } else { + scope_index = scope_stack_[scope_depth].index; } - ++scope_stack_.back().num_names; - // TODO: Reject if we previously performed a failed lookup for this name - // in this scope or a scope nested within it. + // If this name has already been declared in this scope or an inner scope, + // return the existing result. auto& lexical_results = lexical_lookup_.Get(name_id); - CARBON_CHECK( - lexical_results.empty() || - lexical_results.back().scope_index < PeekIndex(), - "Failed to clean up after scope nested within the current scope"); - lexical_results.push_back({.inst_id = target_id, .scope_index = PeekIndex()}); + if (!lexical_results.empty() && + lexical_results.back().scope_index >= scope_index) { + return lexical_results.back().inst_id; + } + + // Add the name into the scope. + bool inserted = scope_stack_[scope_depth].names.Insert(name_id).is_inserted(); + CARBON_CHECK(inserted, "Name in scope but not in lexical lookups"); + ++scope_stack_[scope_depth].num_names; + + // Add a corresponding lexical lookup result. + lexical_results.push_back({.inst_id = target_id, .scope_index = scope_index}); return SemIR::InstId::Invalid; } diff --git a/toolchain/check/scope_stack.h b/toolchain/check/scope_stack.h index 60d2339c5d4e..a1ba60c4d14d 100644 --- a/toolchain/check/scope_stack.h +++ b/toolchain/check/scope_stack.h @@ -111,9 +111,10 @@ class ScopeStack { // is already a `returned var`, returns it instead. auto SetReturnedVarOrGetExisting(SemIR::InstId inst_id) -> SemIR::InstId; - // Looks up the name `name_id` in the current scope. Returns the existing - // lookup result, if any. - auto LookupInCurrentScope(SemIR::NameId name_id) -> SemIR::InstId; + // Looks up the name `name_id` in the current scope and enclosing scopes, but + // do not look past `scope_index`. Returns the existing lookup result, if any. + auto LookupInLexicalScopesWithin(SemIR::NameId name_id, + ScopeIndex scope_index) -> SemIR::InstId; // Looks up the name `name_id` in the current scope and related lexical // scopes. Returns the innermost lexical lookup result, if any, along with a @@ -122,10 +123,12 @@ class ScopeStack { auto LookupInLexicalScopes(SemIR::NameId name_id) -> std::pair>; - // Looks up the name `name_id` in the current scope. Returns the existing - // instruction if any, and otherwise adds the name with the value `target_id` - // and returns Invalid. - auto LookupOrAddName(SemIR::NameId name_id, SemIR::InstId target_id) + // Looks up the name `name_id` in the current scope, or in `scope_index` if + // specified. Returns the existing instruction if the name is already declared + // in that scope or any unfinished scope within it, and otherwise adds the + // name with the value `target_id` and returns Invalid. + auto LookupOrAddName(SemIR::NameId name_id, SemIR::InstId target_id, + ScopeIndex scope_index = ScopeIndex::Invalid) -> SemIR::InstId; // Prepares to add a compile-time binding in the current scope, and returns diff --git a/toolchain/check/testdata/class/fail_todo_local_class.carbon b/toolchain/check/testdata/class/local.carbon similarity index 89% rename from toolchain/check/testdata/class/fail_todo_local_class.carbon rename to toolchain/check/testdata/class/local.carbon index 069cae960c22..0573739585e1 100644 --- a/toolchain/check/testdata/class/fail_todo_local_class.carbon +++ b/toolchain/check/testdata/class/local.carbon @@ -4,9 +4,9 @@ // // AUTOUPDATE // TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/fail_todo_local_class.carbon +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/local.carbon // TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/fail_todo_local_class.carbon +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/local.carbon class A { fn F() -> i32 { @@ -19,15 +19,11 @@ class A { var n: i32; } - // TODO: Add the name `B` to the lexical scope. - // CHECK:STDERR: fail_todo_local_class.carbon:[[@LINE+3]]:12: error: name `B` not found [NameNotFound] - // CHECK:STDERR: return B.Make().n; - // CHECK:STDERR: ^ return B.Make().n; } } -// CHECK:STDOUT: --- fail_todo_local_class.carbon +// CHECK:STDOUT: --- local.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %A: type = class_type @A [template] @@ -113,10 +109,15 @@ class A { // CHECK:STDOUT: fn @F() -> %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %B.decl: type = class_decl @B [template = constants.%B] {} {} -// CHECK:STDOUT: %B.ref: = name_ref B, [template = ] -// CHECK:STDOUT: %Make.ref: = name_ref Make, [template = ] -// CHECK:STDOUT: %n.ref: = name_ref n, [template = ] -// CHECK:STDOUT: return +// CHECK:STDOUT: %B.ref: type = name_ref B, %B.decl [template = constants.%B] +// CHECK:STDOUT: %Make.ref: %Make.type = name_ref Make, @B.%Make.decl [template = constants.%Make] +// CHECK:STDOUT: %.loc22_19.1: ref %B = temporary_storage +// CHECK:STDOUT: %Make.call: init %B = call %Make.ref() to %.loc22_19.1 +// CHECK:STDOUT: %.loc22_19.2: ref %B = temporary %.loc22_19.1, %Make.call +// CHECK:STDOUT: %n.ref: %B.elem = name_ref n, @B.%.loc19 [template = @B.%.loc19] +// CHECK:STDOUT: %.loc22_20.1: ref %i32 = class_element_access %.loc22_19.2, element0 +// CHECK:STDOUT: %.loc22_20.2: %i32 = bind_value %.loc22_20.1 +// CHECK:STDOUT: return %.loc22_20.2 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Make() -> %return.param_patt: %B { diff --git a/toolchain/check/testdata/generic/local.carbon b/toolchain/check/testdata/generic/local.carbon new file mode 100644 index 000000000000..ffb165e0a539 --- /dev/null +++ b/toolchain/check/testdata/generic/local.carbon @@ -0,0 +1,279 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/generic/local.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/generic/local.carbon + +// --- class.carbon + +library "[[@TEST_NAME]]"; + +fn F() { + class C(T:! type) { + var x: T; + } + var v: C(i32) = {.x = 1}; +} + +// --- fail_param_shadows_class.carbon + +library "[[@TEST_NAME]]"; + +fn F() { + // TODO: Decide on what behavior we want here. We don't reject the corresponding case outside of a function. + // CHECK:STDERR: fail_param_shadows_class.carbon:[[@LINE+6]]:3: error: duplicate name being declared in the same scope [NameDeclDuplicate] + // CHECK:STDERR: class C(C:! type) { + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_param_shadows_class.carbon:[[@LINE+3]]:11: note: name is previously declared here [NameDeclPrevious] + // CHECK:STDERR: class C(C:! type) { + // CHECK:STDERR: ^ + class C(C:! type) { + } +} + +// --- nonlocal_param_shadows_class.carbon + +library "[[@TEST_NAME]]"; + +class C(C:! type) { +} + +// CHECK:STDOUT: --- class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [template] +// CHECK:STDOUT: %F: %F.type = struct_value () [template] +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %T.patt: type = symbolic_binding_pattern T, 0 [symbolic] +// CHECK:STDOUT: %C.type: type = generic_class_type @C [template] +// CHECK:STDOUT: %C.generic: %C.type = struct_value () [template] +// CHECK:STDOUT: %C.f06: type = class_type @C, @C(%T) [symbolic] +// CHECK:STDOUT: %require_complete.4ae: = require_complete_type %T [symbolic] +// CHECK:STDOUT: %C.elem.cca: type = unbound_element_type %C.f06, %T [symbolic] +// CHECK:STDOUT: %struct_type.x.2ac: type = struct_type {.x: %T} [symbolic] +// CHECK:STDOUT: %complete_type.4339: = complete_type_witness %struct_type.x.2ac [symbolic] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [template] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [template] +// CHECK:STDOUT: %C.d45: type = class_type @C, @C(%i32) [template] +// CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [template] +// CHECK:STDOUT: %complete_type.f8a: = complete_type_witness %i32.builtin [template] +// CHECK:STDOUT: %C.elem.f74: type = unbound_element_type %C.d45, %i32 [template] +// CHECK:STDOUT: %struct_type.x.ed6: type = struct_type {.x: %i32} [template] +// CHECK:STDOUT: %complete_type.1ec: = complete_type_witness %struct_type.x.ed6 [template] +// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [template] +// CHECK:STDOUT: %struct_type.x.c96: type = struct_type {.x: Core.IntLiteral} [template] +// CHECK:STDOUT: %Convert.type.1b6: type = fn_type @Convert.1, @ImplicitAs(%i32) [template] +// CHECK:STDOUT: %impl_witness.d39: = impl_witness (imports.%import_ref.a5b), @impl.1(%int_32) [template] +// CHECK:STDOUT: %Convert.type.035: type = fn_type @Convert.2, @impl.1(%int_32) [template] +// CHECK:STDOUT: %Convert.956: %Convert.type.035 = struct_value () [template] +// CHECK:STDOUT: %Convert.bound: = bound_method %int_1.5b8, %Convert.956 [template] +// CHECK:STDOUT: %Convert.specific_fn: = specific_function %Convert.bound, @Convert.2(%int_32) [template] +// CHECK:STDOUT: %int_1.5d2: %i32 = int_value 1 [template] +// CHECK:STDOUT: %C.val: %C.d45 = struct_value (%int_1.5d2) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [template] { +// CHECK:STDOUT: .Int = %import_ref.485 +// CHECK:STDOUT: .ImplicitAs = %import_ref.d44 +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @C(%T.loc5_11.1: type) { +// CHECK:STDOUT: %T.loc5_11.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc5_11.2 (constants.%T)] +// CHECK:STDOUT: %T.patt.loc5_11.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc5_11.2 (constants.%T.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete: = require_complete_type @C.%T.loc5_11.2 (%T) [symbolic = %require_complete (constants.%require_complete.4ae)] +// CHECK:STDOUT: %C: type = class_type @C, @C(%T.loc5_11.2) [symbolic = %C (constants.%C.f06)] +// CHECK:STDOUT: %C.elem: type = unbound_element_type @C.%C (%C.f06), @C.%T.loc5_11.2 (%T) [symbolic = %C.elem (constants.%C.elem.cca)] +// CHECK:STDOUT: %struct_type.x: type = struct_type {.x: @C.%T.loc5_11.2 (%T)} [symbolic = %struct_type.x (constants.%struct_type.x.2ac)] +// CHECK:STDOUT: %complete_type.loc7_3.2: = complete_type_witness @C.%struct_type.x (%struct_type.x.2ac) [symbolic = %complete_type.loc7_3.2 (constants.%complete_type.4339)] +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %.loc6: @C.%C.elem (%C.elem.cca) = field_decl x, element0 [template] +// CHECK:STDOUT: %complete_type.loc7_3.1: = complete_type_witness %struct_type.x.2ac [symbolic = %complete_type.loc7_3.2 (constants.%complete_type.4339)] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C.f06 +// CHECK:STDOUT: .x = %.loc6 +// CHECK:STDOUT: complete_type_witness = %complete_type.loc7_3.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %C.decl: %C.type = class_decl @C [template = constants.%C.generic] { +// CHECK:STDOUT: %T.patt.loc5_11.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc5_11.2 (constants.%T.patt)] +// CHECK:STDOUT: %T.param_patt: type = value_param_pattern %T.patt.loc5_11.1, runtime_param [symbolic = %T.patt.loc5_11.2 (constants.%T.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %T.param: type = value_param runtime_param +// CHECK:STDOUT: %T.loc5_11.1: type = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc5_11.2 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %v.var: ref %C.d45 = var v +// CHECK:STDOUT: %v: ref %C.d45 = bind_name v, %v.var +// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [template = constants.%int_1.5b8] +// CHECK:STDOUT: %.loc8_26.1: %struct_type.x.c96 = struct_literal (%int_1) +// CHECK:STDOUT: %impl.elem0: %Convert.type.1b6 = impl_witness_access constants.%impl_witness.d39, element0 [template = constants.%Convert.956] +// CHECK:STDOUT: %Convert.bound: = bound_method %int_1, %impl.elem0 [template = constants.%Convert.bound] +// CHECK:STDOUT: %Convert.specific_fn: = specific_function %Convert.bound, @Convert.2(constants.%int_32) [template = constants.%Convert.specific_fn] +// CHECK:STDOUT: %int.convert_checked: init %i32 = call %Convert.specific_fn(%int_1) [template = constants.%int_1.5d2] +// CHECK:STDOUT: %.loc8_26.2: init %i32 = converted %int_1, %int.convert_checked [template = constants.%int_1.5d2] +// CHECK:STDOUT: %.loc8_26.3: ref %i32 = class_element_access %v.var, element0 +// CHECK:STDOUT: %.loc8_26.4: init %i32 = initialize_from %.loc8_26.2 to %.loc8_26.3 [template = constants.%int_1.5d2] +// CHECK:STDOUT: %.loc8_26.5: init %C.d45 = class_init (%.loc8_26.4), %v.var [template = constants.%C.val] +// CHECK:STDOUT: %.loc8_27: init %C.d45 = converted %.loc8_26.1, %.loc8_26.5 [template = constants.%C.val] +// CHECK:STDOUT: assign %v.var, %.loc8_27 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%T) { +// CHECK:STDOUT: %T.loc5_11.2 => constants.%T +// CHECK:STDOUT: %T.patt.loc5_11.2 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(%T.loc5_11.2) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%i32) { +// CHECK:STDOUT: %T.loc5_11.2 => constants.%i32 +// CHECK:STDOUT: %T.patt.loc5_11.2 => constants.%i32 +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %require_complete => constants.%complete_type.f8a +// CHECK:STDOUT: %C => constants.%C.d45 +// CHECK:STDOUT: %C.elem => constants.%C.elem.f74 +// CHECK:STDOUT: %struct_type.x => constants.%struct_type.x.ed6 +// CHECK:STDOUT: %complete_type.loc7_3.2 => constants.%complete_type.1ec +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_param_shadows_class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [template] +// CHECK:STDOUT: %F: %F.type = struct_value () [template] +// CHECK:STDOUT: %C: type = bind_symbolic_name C, 0 [symbolic] +// CHECK:STDOUT: %C.patt: type = symbolic_binding_pattern C, 0 [symbolic] +// CHECK:STDOUT: %.type: type = generic_class_type @.1 [template] +// CHECK:STDOUT: %.generic: %.type = struct_value () [template] +// CHECK:STDOUT: %.de1: type = class_type @.1, @.1(%C) [symbolic] +// 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: %Core: = namespace file.%Core.import, [template] { +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @.1(%C.loc12_11.1: type) { +// CHECK:STDOUT: %C.loc12_11.2: type = bind_symbolic_name C, 0 [symbolic = %C.loc12_11.2 (constants.%C)] +// CHECK:STDOUT: %C.patt.loc12_11.2: type = symbolic_binding_pattern C, 0 [symbolic = %C.patt.loc12_11.2 (constants.%C.patt)] +// CHECK:STDOUT: +// 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.%.de1 +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.decl: %.type = class_decl @.1 [template = constants.%.generic] { +// CHECK:STDOUT: %C.patt.loc12_11.1: type = symbolic_binding_pattern C, 0 [symbolic = %C.patt.loc12_11.2 (constants.%C.patt)] +// CHECK:STDOUT: %C.param_patt: type = value_param_pattern %C.patt.loc12_11.1, runtime_param [symbolic = %C.patt.loc12_11.2 (constants.%C.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.param: type = value_param runtime_param +// CHECK:STDOUT: %C.loc12_11.1: type = bind_symbolic_name C, 0, %C.param [symbolic = %C.loc12_11.2 (constants.%C)] +// CHECK:STDOUT: } +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @.1(constants.%C) { +// CHECK:STDOUT: %C.loc12_11.2 => constants.%C +// CHECK:STDOUT: %C.patt.loc12_11.2 => constants.%C +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- nonlocal_param_shadows_class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C.8b3: type = bind_symbolic_name C, 0 [symbolic] +// CHECK:STDOUT: %C.patt: type = symbolic_binding_pattern C, 0 [symbolic] +// CHECK:STDOUT: %C.type: type = generic_class_type @C [template] +// CHECK:STDOUT: %C.generic: %C.type = struct_value () [template] +// CHECK:STDOUT: %C.f2e: type = class_type @C, @C(%C.8b3) [symbolic] +// 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: %Core: = namespace file.%Core.import, [template] { +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %C.decl: %C.type = class_decl @C [template = constants.%C.generic] { +// CHECK:STDOUT: %C.patt.loc4_9.1: type = symbolic_binding_pattern C, 0 [symbolic = %C.patt.loc4_9.2 (constants.%C.patt)] +// CHECK:STDOUT: %C.param_patt: type = value_param_pattern %C.patt.loc4_9.1, runtime_param [symbolic = %C.patt.loc4_9.2 (constants.%C.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %C.param: type = value_param runtime_param +// CHECK:STDOUT: %C.loc4_9.1: type = bind_symbolic_name C, 0, %C.param [symbolic = %C.loc4_9.2 (constants.%C.8b3)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @C(%C.loc4_9.1: type) { +// CHECK:STDOUT: %C.loc4_9.2: type = bind_symbolic_name C, 0 [symbolic = %C.loc4_9.2 (constants.%C.8b3)] +// CHECK:STDOUT: %C.patt.loc4_9.2: type = symbolic_binding_pattern C, 0 [symbolic = %C.patt.loc4_9.2 (constants.%C.patt)] +// CHECK:STDOUT: +// 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.%C.f2e +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @C(constants.%C.8b3) { +// CHECK:STDOUT: %C.loc4_9.2 => constants.%C.8b3 +// CHECK:STDOUT: %C.patt.loc4_9.2 => constants.%C.8b3 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/no_prelude/local.carbon b/toolchain/check/testdata/interface/no_prelude/local.carbon new file mode 100644 index 000000000000..7bd9b04d5abb --- /dev/null +++ b/toolchain/check/testdata/interface/no_prelude/local.carbon @@ -0,0 +1,95 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interface/no_prelude/local.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interface/no_prelude/local.carbon + +fn F() { + interface I { + fn G(); + } + impl () as I { + fn G() {} + } + ().(I.G)(); +} + +// CHECK:STDOUT: --- local.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [template] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [template] +// CHECK:STDOUT: %F: %F.type = struct_value () [template] +// CHECK:STDOUT: %I.type: type = facet_type <@I> [template] +// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: %G.type.bff: type = fn_type @G.1 [template] +// CHECK:STDOUT: %G.f0a: %G.type.bff = struct_value () [template] +// CHECK:STDOUT: %G.assoc_type: type = assoc_entity_type %I.type, %G.type.bff [template] +// CHECK:STDOUT: %assoc0: %G.assoc_type = assoc_entity element0, @I.%G.decl [template] +// CHECK:STDOUT: %impl_witness: = impl_witness (@impl.%G.decl) [template] +// CHECK:STDOUT: %G.type.c84: type = fn_type @G.2 [template] +// CHECK:STDOUT: %G.5a2: %G.type.c84 = struct_value () [template] +// CHECK:STDOUT: %I.facet: %I.type = facet_value %empty_tuple.type, %impl_witness [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I { +// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: %G.decl: %G.type.bff = fn_decl @G.1 [template = constants.%G.f0a] {} {} +// CHECK:STDOUT: %assoc0: %G.assoc_type = assoc_entity element0, %G.decl [template = constants.%assoc0] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .G = %assoc0 +// CHECK:STDOUT: witness = (%G.decl) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: %.loc15_9.2 as %I.ref { +// CHECK:STDOUT: %G.decl: %G.type.c84 = fn_decl @G.2 [template = constants.%G.5a2] {} {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .G = %G.decl +// CHECK:STDOUT: witness = @F.%impl_witness +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%I.type] {} {} +// CHECK:STDOUT: impl_decl @impl [template] {} { +// CHECK:STDOUT: %.loc15_9.1: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc15_9.2: type = converted %.loc15_9.1, constants.%empty_tuple.type [template = constants.%empty_tuple.type] +// CHECK:STDOUT: %I.ref: type = name_ref I, @F.%I.decl [template = constants.%I.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %impl_witness: = impl_witness (@impl.%G.decl) [template = constants.%impl_witness] +// CHECK:STDOUT: %.loc18: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %I.ref: type = name_ref I, %I.decl [template = constants.%I.type] +// CHECK:STDOUT: %G.ref: %G.assoc_type = name_ref G, @I.%assoc0 [template = constants.%assoc0] +// CHECK:STDOUT: %impl.elem0: %G.type.bff = impl_witness_access constants.%impl_witness, element0 [template = constants.%G.5a2] +// CHECK:STDOUT: %G.call: init %empty_tuple.type = call %impl.elem0() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @G.1(@I.%Self: %I.type) { +// CHECK:STDOUT: +// CHECK:STDOUT: fn(); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G.2() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @G.1(constants.%Self) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @G.1(constants.%I.facet) {} +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/namespace/fail_not_top_level.carbon b/toolchain/check/testdata/namespace/fail_not_top_level.carbon index a534dcba497b..22981017079a 100644 --- a/toolchain/check/testdata/namespace/fail_not_top_level.carbon +++ b/toolchain/check/testdata/namespace/fail_not_top_level.carbon @@ -14,10 +14,6 @@ fn F() { // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: namespace N; - // CHECK:STDERR: fail_not_top_level.carbon:[[@LINE+4]]:6: error: name `N` not found [NameNotFound] - // CHECK:STDERR: fn N.F() {} - // CHECK:STDERR: ^ - // CHECK:STDERR: fn N.F() {} } @@ -43,10 +39,10 @@ interface I { // CHECK:STDOUT: constants { // CHECK:STDOUT: %F.type.b25: type = fn_type @F.1 [template] // CHECK:STDOUT: %F.c41: %F.type.b25 = struct_value () [template] -// CHECK:STDOUT: %.type: type = fn_type @.1 [template] -// CHECK:STDOUT: %.3ad: %.type = struct_value () [template] +// CHECK:STDOUT: %F.type.bee: type = fn_type @F.2 [template] +// CHECK:STDOUT: %F.39e: %F.type.bee = struct_value () [template] // CHECK:STDOUT: %C: type = class_type @C [template] -// CHECK:STDOUT: %F.type.1cc: type = fn_type @F.2 [template] +// CHECK:STDOUT: %F.type.1cc: type = fn_type @F.3 [template] // CHECK:STDOUT: %F.f49: %F.type.1cc = struct_value () [template] // CHECK:STDOUT: %empty_struct_type: type = struct_type {} [template] // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template] @@ -93,7 +89,7 @@ interface I { // CHECK:STDOUT: %N: = namespace [template] { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } -// CHECK:STDOUT: %F.decl: %F.type.1cc = fn_decl @F.2 [template = constants.%F.f49] {} {} +// CHECK:STDOUT: %F.decl: %F.type.1cc = fn_decl @F.3 [template = constants.%F.f49] {} {} // CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [template = constants.%complete_type] // CHECK:STDOUT: // CHECK:STDOUT: !members: @@ -104,13 +100,10 @@ interface I { // CHECK:STDOUT: // CHECK:STDOUT: fn @F.1() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %N: = namespace [template] {} -// CHECK:STDOUT: %.decl: %.type = fn_decl @.1 [template = constants.%.3ad] {} {} -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @.1() { -// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %N: = namespace [template] { +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type.bee = fn_decl @F.2 [template = constants.%F.39e] {} {} // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: @@ -119,6 +112,11 @@ interface I { // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: fn @F.3() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: generic fn @I.1(@I.2.%Self: %I.type.733) { // CHECK:STDOUT: !definition: // CHECK:STDOUT: