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: