Skip to content

Commit

Permalink
Import enclosing namespaces when importing an entity.
Browse files Browse the repository at this point in the history
Ensures that we can properly name entities imported indirectly.
  • Loading branch information
zygoloid committed Dec 10, 2024
1 parent 8167d6c commit 0b886b2
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 19 deletions.
46 changes: 34 additions & 12 deletions toolchain/check/import_ref.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1082,18 +1082,7 @@ static auto GetLocalNameScopeId(ImportRefResolver& resolver,
}

// Get the constant value for the scope.
auto const_id = SemIR::ConstantId::Invalid;
CARBON_KIND_SWITCH(*inst) {
case SemIR::Namespace::Kind:
// If the namespace has already been imported, we can use its constant.
// However, if it hasn't, we use Invalid instead of adding it to the
// work stack. That's expected to be okay when resolving references.
const_id = resolver.local_constant_values_for_import_insts().Get(inst_id);
break;

default:
const_id = GetLocalConstantId(resolver, inst_id);
}
auto const_id = GetLocalConstantId(resolver, inst_id);
if (!const_id.is_valid()) {
return SemIR::NameScopeId::Invalid;
}
Expand Down Expand Up @@ -2280,6 +2269,36 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
.bit_width_id = bit_width_id});
}

static auto TryResolveTypedInst(ImportRefResolver& resolver,
SemIR::Namespace inst,
SemIR::InstId import_inst_id) -> ResolveResult {
const auto& name_scope =
resolver.import_name_scopes().Get(inst.name_scope_id);
auto parent_scope_id =
GetLocalNameScopeId(resolver, name_scope.parent_scope_id());

if (resolver.HasNewWork()) {
return ResolveResult::Retry();
}

auto namespace_type_id = resolver.local_context().GetSingletonType(
SemIR::NamespaceType::SingletonInstId);
auto namespace_decl =
SemIR::Namespace{.type_id = namespace_type_id,
.name_scope_id = SemIR::NameScopeId::Invalid,
.import_id = SemIR::AbsoluteInstId::Invalid};
auto inst_id = resolver.local_context().AddPlaceholderInstInNoBlock(
resolver.local_context().MakeImportedLocAndInst(
AddImportIRInst(resolver, import_inst_id), namespace_decl));

auto name_id = GetLocalNameId(resolver, name_scope.name_id());
namespace_decl.name_scope_id =
resolver.local_name_scopes().Add(inst_id, name_id, parent_scope_id);
resolver.local_context().ReplaceInstBeforeConstantUse(inst_id,
namespace_decl);
return {.const_id = resolver.local_constant_values().Get(inst_id)};
}

static auto TryResolveTypedInst(ImportRefResolver& resolver,
SemIR::PointerType inst) -> ResolveResult {
CARBON_CHECK(inst.type_id == SemIR::TypeType::SingletonTypeId);
Expand Down Expand Up @@ -2519,6 +2538,9 @@ static auto TryResolveInstCanonical(ImportRefResolver& resolver,
case CARBON_KIND(SemIR::IntType inst): {
return TryResolveTypedInst(resolver, inst);
}
case CARBON_KIND(SemIR::Namespace inst): {
return TryResolveTypedInst(resolver, inst, inst_id);
}
case CARBON_KIND(SemIR::PointerType inst): {
return TryResolveTypedInst(resolver, inst);
}
Expand Down
174 changes: 171 additions & 3 deletions toolchain/check/testdata/namespace/imported_indirect.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@ package Same library "[[@TEST_NAME]]";

namespace A;

class A.C;

// --- b.carbon

package Same library "[[@TEST_NAME]]";
import library "a";

namespace A.B;

fn F() -> A.C;

// --- c.carbon

package Same library "[[@TEST_NAME]]";
Expand All @@ -42,8 +46,53 @@ import library "d";

var e: () = A.B.C.D();

// --- fail_named_indirectly_same_package.carbon

package Same library "[[@TEST_NAME]]";

import library "b";

// CHECK:STDERR: fail_named_indirectly_same_package.carbon:[[@LINE+13]]:10: error: function returns incomplete type `A.C` [IncompleteTypeInFunctionReturnType]
// CHECK:STDERR: fn G() { F(); }
// CHECK:STDERR: ^
// CHECK:STDERR: fail_named_indirectly_same_package.carbon:[[@LINE-5]]:1: in import [InImport]
// CHECK:STDERR: b.carbon:3:1: in import [InImport]
// CHECK:STDERR: a.carbon:6:1: note: class was forward declared here [ClassForwardDeclaredHere]
// CHECK:STDERR: class A.C;
// CHECK:STDERR: ^~~~~~~~~~
// CHECK:STDERR: fail_named_indirectly_same_package.carbon:[[@LINE-10]]:1: in import [InImport]
// CHECK:STDERR: b.carbon:7:8: note: return type declared here [IncompleteReturnTypeHere]
// CHECK:STDERR: fn F() -> A.C;
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR:
fn G() { F(); }

// --- fail_named_indirectly_different_package.carbon

package Other library "[[@TEST_NAME]]";

import Same library "b";

// CHECK:STDERR: fail_named_indirectly_different_package.carbon:[[@LINE+12]]:10: error: function returns incomplete type `Same.A.C` [IncompleteTypeInFunctionReturnType]
// CHECK:STDERR: fn G() { Same.F(); }
// CHECK:STDERR: ^~~~~~
// CHECK:STDERR: fail_named_indirectly_different_package.carbon:[[@LINE-5]]:1: in import [InImport]
// CHECK:STDERR: b.carbon:3:1: in import [InImport]
// CHECK:STDERR: a.carbon:6:1: note: class was forward declared here [ClassForwardDeclaredHere]
// CHECK:STDERR: class A.C;
// CHECK:STDERR: ^~~~~~~~~~
// CHECK:STDERR: fail_named_indirectly_different_package.carbon:[[@LINE-10]]:1: in import [InImport]
// CHECK:STDERR: b.carbon:7:8: note: return type declared here [IncompleteReturnTypeHere]
// CHECK:STDERR: fn F() -> A.C;
// CHECK:STDERR: ^~~~~~
fn G() { Same.F(); }

// CHECK:STDOUT: --- a.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C: type = class_type @C [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [template] {
// CHECK:STDOUT: import Core//prelude
Expand All @@ -57,16 +106,29 @@ var e: () = A.B.C.D();
// CHECK:STDOUT: .A = %A
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %A: <namespace> = namespace [template] {}
// CHECK:STDOUT: %A: <namespace> = namespace [template] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C;
// CHECK:STDOUT:
// CHECK:STDOUT: --- b.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C: type = class_type @C [template]
// CHECK:STDOUT: %F.type: type = fn_type @F [template]
// CHECK:STDOUT: %F: %F.type = struct_value () [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %import_ref: <namespace> = import_ref Same//a, A, loaded
// CHECK:STDOUT: %A: <namespace> = namespace %import_ref, [template] {
// CHECK:STDOUT: %import_ref.1: <namespace> = import_ref Same//a, A, loaded
// CHECK:STDOUT: %A: <namespace> = namespace %import_ref.1, [template] {
// CHECK:STDOUT: .C = %import_ref.2
// CHECK:STDOUT: .B = file.%B
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref.2: type = import_ref Same//a, C, loaded [template = constants.%C]
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [template] {
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
Expand All @@ -77,19 +139,34 @@ var e: () = A.B.C.D();
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .A = imports.%A
// CHECK:STDOUT: .Core = imports.%Core
// CHECK:STDOUT: .F = %F.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %default.import = import <invalid>
// CHECK:STDOUT: %B: <namespace> = namespace [template] {}
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [template = constants.%F] {
// CHECK:STDOUT: %return.patt: %C = return_slot_pattern
// CHECK:STDOUT: %return.param_patt: %C = out_param_pattern %return.patt, runtime_param0
// CHECK:STDOUT: } {
// CHECK:STDOUT: %A.ref: <namespace> = name_ref A, imports.%A [template = imports.%A]
// CHECK:STDOUT: %C.ref: type = name_ref C, imports.%import_ref.2 [template = constants.%C]
// CHECK:STDOUT: %return.param: ref %C = out_param runtime_param0
// CHECK:STDOUT: %return: ref %C = return_slot %return.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C [from "a.carbon"];
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() -> %C;
// CHECK:STDOUT:
// CHECK:STDOUT: --- c.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %import_ref.1: <namespace> = import_ref Same//b, A, loaded
// CHECK:STDOUT: %A: <namespace> = namespace %import_ref.1, [template] {
// CHECK:STDOUT: .B = %B
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref.3 = import_ref Same//b, F, unloaded
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [template] {
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
Expand All @@ -99,6 +176,7 @@ var e: () = A.B.C.D();
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .A = imports.%A
// CHECK:STDOUT: .F = imports.%import_ref.3
// CHECK:STDOUT: .Core = imports.%Core
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
Expand Down Expand Up @@ -194,3 +272,93 @@ var e: () = A.B.C.D();
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_named_indirectly_same_package.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %G.type: type = fn_type @G [template]
// CHECK:STDOUT: %G: %G.type = struct_value () [template]
// CHECK:STDOUT: %F.type: type = fn_type @F [template]
// CHECK:STDOUT: %F: %F.type = struct_value () [template]
// CHECK:STDOUT: %C: type = class_type @C [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %import_ref.1: <namespace> = import_ref Same//b, A, loaded
// CHECK:STDOUT: %A: <namespace> = namespace %import_ref.1, [template] {
// CHECK:STDOUT: .B = %B
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref.3: %F.type = import_ref Same//b, F, loaded [template = constants.%F]
// CHECK:STDOUT: %Core: <namespace> = 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> = namespace [template] {
// CHECK:STDOUT: .A = imports.%A
// CHECK:STDOUT: .F = imports.%import_ref.3
// CHECK:STDOUT: .Core = imports.%Core
// CHECK:STDOUT: .G = %G.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %default.import = import <invalid>
// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [template = constants.%G] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C [from "b.carbon"];
// CHECK:STDOUT:
// CHECK:STDOUT: fn @G() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%import_ref.3 [template = constants.%F]
// CHECK:STDOUT: %F.call: init <error> = call %F.ref()
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() -> %C [from "b.carbon"];
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_named_indirectly_different_package.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %G.type: type = fn_type @G [template]
// CHECK:STDOUT: %G: %G.type = struct_value () [template]
// CHECK:STDOUT: %F.type: type = fn_type @F [template]
// CHECK:STDOUT: %F: %F.type = struct_value () [template]
// CHECK:STDOUT: %C: type = class_type @C [template]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [template] {
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: %Same: <namespace> = namespace file.%Same.import, [template] {
// CHECK:STDOUT: .F = %import_ref
// CHECK:STDOUT: import Same//b
// CHECK:STDOUT: }
// CHECK:STDOUT: %import_ref: %F.type = import_ref Same//b, F, loaded [template = constants.%F]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [template] {
// CHECK:STDOUT: .Core = imports.%Core
// CHECK:STDOUT: .Same = imports.%Same
// CHECK:STDOUT: .G = %G.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %Same.import = import Same
// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [template = constants.%G] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C [from "b.carbon"];
// CHECK:STDOUT:
// CHECK:STDOUT: fn @G() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Same.ref: <namespace> = name_ref Same, imports.%Same [template = imports.%Same]
// CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%import_ref [template = constants.%F]
// CHECK:STDOUT: %F.call: init <error> = call %F.ref()
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F() -> %C [from "b.carbon"];
// CHECK:STDOUT:
4 changes: 2 additions & 2 deletions toolchain/check/testdata/struct/import.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ var c_bad: C({.c = 1, .d = 2}) = F();
// --- fail_bad_value.impl.carbon

impl package Implicit;
// CHECK:STDERR: fail_bad_value.impl.carbon:[[@LINE+6]]:1: error: cannot implicitly convert from `C(<cannot stringify inst357 kind StructValue>)` to `C(<cannot stringify inst345 kind StructValue>)` [ImplicitAsConversionFailure]
// CHECK:STDERR: fail_bad_value.impl.carbon:[[@LINE+6]]:1: error: cannot implicitly convert from `C(<cannot stringify inst358 kind StructValue>)` to `C(<cannot stringify inst346 kind StructValue>)` [ImplicitAsConversionFailure]
// CHECK:STDERR: var c_bad: C({.a = 3, .b = 4}) = F();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_bad_value.impl.carbon:[[@LINE+3]]:1: note: type `C(<cannot stringify inst357 kind StructValue>)` does not implement interface `ImplicitAs(C(<cannot stringify inst345 kind StructValue>))` [MissingImplInMemberAccessNote]
// CHECK:STDERR: fail_bad_value.impl.carbon:[[@LINE+3]]:1: note: type `C(<cannot stringify inst358 kind StructValue>)` does not implement interface `Core.ImplicitAs(C(<cannot stringify inst346 kind StructValue>))` [MissingImplInMemberAccessNote]
// CHECK:STDERR: var c_bad: C({.a = 3, .b = 4}) = F();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var c_bad: C({.a = 3, .b = 4}) = F();
Expand Down
4 changes: 2 additions & 2 deletions toolchain/check/testdata/tuple/import.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ var c_bad: C((1, 2, 3)) = F();

impl package Implicit;

// CHECK:STDERR: fail_bad_value.impl.carbon:[[@LINE+6]]:1: error: cannot implicitly convert from `C(<cannot stringify inst357 kind TupleValue>)` to `C(<cannot stringify inst345 kind TupleValue>)` [ImplicitAsConversionFailure]
// CHECK:STDERR: fail_bad_value.impl.carbon:[[@LINE+6]]:1: error: cannot implicitly convert from `C(<cannot stringify inst358 kind TupleValue>)` to `C(<cannot stringify inst346 kind TupleValue>)` [ImplicitAsConversionFailure]
// CHECK:STDERR: var c_bad: C((3, 4)) = F();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
// CHECK:STDERR: fail_bad_value.impl.carbon:[[@LINE+3]]:1: note: type `C(<cannot stringify inst357 kind TupleValue>)` does not implement interface `ImplicitAs(C(<cannot stringify inst345 kind TupleValue>))` [MissingImplInMemberAccessNote]
// CHECK:STDERR: fail_bad_value.impl.carbon:[[@LINE+3]]:1: note: type `C(<cannot stringify inst358 kind TupleValue>)` does not implement interface `Core.ImplicitAs(C(<cannot stringify inst346 kind TupleValue>))` [MissingImplInMemberAccessNote]
// CHECK:STDERR: var c_bad: C((3, 4)) = F();
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~
var c_bad: C((3, 4)) = F();
Expand Down

0 comments on commit 0b886b2

Please sign in to comment.