Просмотр исходного кода

Replace ResolvePrevInstForMerge with decl logic. (#3936)

This addresses the const reliance of ResolvePrevInstForMerge by checking
the imported instruction, testing with `alias` name conflicts. Given the
need to examine structure, the helper function feels like it now gets in
the way, so I'm just removing it.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Jon Ross-Perkins 2 лет назад
Родитель
Сommit
292a9d3cb4

+ 3 - 3
toolchain/check/class.cpp

@@ -12,7 +12,7 @@ auto MergeClassRedecl(Context& context, SemIRLoc new_loc,
                       SemIR::Class& new_class, bool new_is_import,
                       bool new_is_definition, bool new_is_extern,
                       SemIR::ClassId prev_class_id, bool prev_is_extern,
-                      SemIR::ImportIRInstId prev_import_ir_inst_id) -> bool {
+                      SemIR::ImportIRId prev_import_ir_id) -> bool {
   auto& prev_class = context.classes().Get(prev_class_id);
   SemIRLoc prev_loc =
       prev_class.is_defined() ? prev_class.definition_id : prev_class.decl_id;
@@ -30,7 +30,7 @@ auto MergeClassRedecl(Context& context, SemIRLoc new_loc,
                        {.loc = prev_loc,
                         .is_definition = prev_class.is_defined(),
                         .is_extern = prev_is_extern},
-                       prev_import_ir_inst_id);
+                       prev_import_ir_id);
 
   // The introducer kind must match the previous declaration.
   // TODO: The rule here is not yet decided. See #3384.
@@ -56,7 +56,7 @@ auto MergeClassRedecl(Context& context, SemIRLoc new_loc,
     prev_class.object_repr_id = new_class.object_repr_id;
   }
 
-  if ((prev_import_ir_inst_id.is_valid() && !new_is_import) ||
+  if ((prev_import_ir_id.is_valid() && !new_is_import) ||
       (prev_is_extern && !new_is_extern)) {
     prev_class.decl_id = new_class.decl_id;
     ReplacePrevInstForMerge(

+ 1 - 1
toolchain/check/class.h

@@ -21,7 +21,7 @@ auto MergeClassRedecl(Context& context, SemIRLoc new_loc,
                       SemIR::Class& new_class, bool new_is_import,
                       bool new_is_definition, bool new_is_extern,
                       SemIR::ClassId prev_class_id, bool prev_is_extern,
-                      SemIR::ImportIRInstId prev_import_ir_inst_id) -> bool;
+                      SemIR::ImportIRId prev_import_ir_id) -> bool;
 
 }  // namespace Carbon::Check
 

+ 3 - 3
toolchain/check/function.cpp

@@ -95,7 +95,7 @@ auto MergeFunctionRedecl(Context& context, SemIRLoc new_loc,
                          SemIR::Function& new_function, bool new_is_import,
                          bool new_is_definition,
                          SemIR::FunctionId prev_function_id,
-                         SemIR::ImportIRInstId prev_import_ir_inst_id) -> bool {
+                         SemIR::ImportIRId prev_import_ir_id) -> bool {
   auto& prev_function = context.functions().Get(prev_function_id);
 
   if (!CheckRedecl(context, new_function, prev_function, {})) {
@@ -111,7 +111,7 @@ auto MergeFunctionRedecl(Context& context, SemIRLoc new_loc,
                                    : prev_function.decl_id,
                         .is_definition = prev_function.definition_id.is_valid(),
                         .is_extern = prev_function.is_extern},
-                       prev_import_ir_inst_id);
+                       prev_import_ir_id);
 
   if (new_is_definition) {
     // Track the signature from the definition, so that IDs in the body
@@ -125,7 +125,7 @@ auto MergeFunctionRedecl(Context& context, SemIRLoc new_loc,
   // The new function might have return slot information if it was imported.
   prev_function.return_slot =
       MergeReturnSlot(prev_function.return_slot, new_function.return_slot);
-  if ((prev_import_ir_inst_id.is_valid() && !new_is_import) ||
+  if ((prev_import_ir_id.is_valid() && !new_is_import) ||
       (prev_function.is_extern && !new_function.is_extern)) {
     prev_function.is_extern = new_function.is_extern;
     prev_function.decl_id = new_function.decl_id;

+ 1 - 1
toolchain/check/function.h

@@ -46,7 +46,7 @@ auto MergeFunctionRedecl(Context& context, SemIRLoc new_loc,
                          SemIR::Function& new_function, bool new_is_import,
                          bool new_is_definition,
                          SemIR::FunctionId prev_function_id,
-                         SemIR::ImportIRInstId prev_import_ir_inst_id) -> bool;
+                         SemIR::ImportIRId prev_import_ir_id) -> bool;
 
 // Checks that the return type of the specified function is complete, issuing an
 // error if not. This computes the return slot usage for the function if

+ 41 - 26
toolchain/check/handle_class.cpp

@@ -48,38 +48,53 @@ static auto MergeOrAddName(Context& context, Parse::AnyClassDeclId node_id,
                            bool is_extern) -> void {
   auto prev_id =
       context.decl_name_stack().LookupOrAddName(name_context, class_decl_id);
-  if (prev_id.is_valid()) {
-    auto prev_inst_for_merge = ResolvePrevInstForMerge(context, prev_id);
+  if (!prev_id.is_valid()) {
+    return;
+  }
 
-    auto prev_class_id = SemIR::ClassId::Invalid;
-    CARBON_KIND_SWITCH(prev_inst_for_merge.inst) {
-      case CARBON_KIND(SemIR::ClassDecl class_decl): {
-        prev_class_id = class_decl.class_id;
-        break;
-      }
-      case CARBON_KIND(SemIR::ClassType class_type): {
-        prev_class_id = class_type.class_id;
+  auto prev_class_id = SemIR::ClassId::Invalid;
+  auto prev_import_ir_id = SemIR::ImportIRId::Invalid;
+  CARBON_KIND_SWITCH(context.insts().Get(prev_id)) {
+    case CARBON_KIND(SemIR::ClassDecl class_decl): {
+      prev_class_id = class_decl.class_id;
+      break;
+    }
+    case CARBON_KIND(SemIR::ImportRefLoaded import_ref): {
+      auto import_ir_inst =
+          context.import_ir_insts().Get(import_ref.import_ir_inst_id);
+
+      // Verify the decl so that things like aliases are name conflicts.
+      const auto* import_ir =
+          context.import_irs().Get(import_ir_inst.ir_id).sem_ir;
+      if (!import_ir->insts().Is<SemIR::ClassDecl>(import_ir_inst.inst_id)) {
         break;
       }
-      default:
-        // This is a redeclaration of something other than a class.
-        context.DiagnoseDuplicateName(class_decl_id, prev_id);
-        break;
-    }
 
-    if (prev_class_id.is_valid()) {
-      // TODO: Fix prev_is_extern logic.
-      if (MergeClassRedecl(context, node_id, class_info,
-                           /*new_is_import=*/false, is_definition, is_extern,
-                           prev_class_id, /*prev_is_extern=*/false,
-                           prev_inst_for_merge.import_ir_inst_id)) {
-        // When merging, use the existing entity rather than adding a new one.
-        class_decl.class_id = prev_class_id;
+      // Use the type to get the ID.
+      if (auto class_type = context.insts().TryGetAs<SemIR::ClassType>(
+              context.constant_values().Get(prev_id).inst_id())) {
+        prev_class_id = class_type->class_id;
+        prev_import_ir_id = import_ir_inst.ir_id;
       }
-    } else {
-      // This is a redeclaration of something other than a class.
-      context.DiagnoseDuplicateName(class_decl_id, prev_id);
+      break;
     }
+    default:
+      break;
+  }
+
+  if (!prev_class_id.is_valid()) {
+    // This is a redeclaration of something other than a class.
+    context.DiagnoseDuplicateName(class_decl_id, prev_id);
+    return;
+  }
+
+  // TODO: Fix prev_is_extern logic.
+  if (MergeClassRedecl(context, node_id, class_info,
+                       /*new_is_import=*/false, is_definition, is_extern,
+                       prev_class_id, /*prev_is_extern=*/false,
+                       prev_import_ir_id)) {
+    // When merging, use the existing entity rather than adding a new one.
+    class_decl.class_id = prev_class_id;
   }
 }
 

+ 34 - 27
toolchain/check/handle_function.cpp

@@ -61,28 +61,6 @@ static auto DiagnoseModifiers(Context& context, bool is_definition,
   return context.decl_state_stack().innermost().modifier_set;
 }
 
-// Returns the function ID for the instruction, or invalid.
-static auto GetRedeclFunctionId(Context& context, SemIR::Inst prev_inst)
-    -> SemIR::FunctionId {
-  CARBON_KIND_SWITCH(prev_inst) {
-    case CARBON_KIND(SemIR::StructValue struct_value): {
-      if (auto fn_type = context.types().TryGetAs<SemIR::FunctionType>(
-              struct_value.type_id)) {
-        return fn_type->function_id;
-      }
-      return SemIR::FunctionId::Invalid;
-    }
-    case CARBON_KIND(SemIR::FunctionDecl fn_decl): {
-      return fn_decl.function_id;
-    }
-    default:
-      // This is a redeclaration of something other than a function. This
-      // includes the case where an associated function redeclares another
-      // associated function.
-      return SemIR::FunctionId::Invalid;
-  }
-}
-
 // Check whether this is a redeclaration, merging if needed.
 static auto TryMergeRedecl(Context& context, Parse::AnyFunctionDeclId node_id,
                            SemIR::InstId prev_id,
@@ -93,9 +71,39 @@ static auto TryMergeRedecl(Context& context, Parse::AnyFunctionDeclId node_id,
     return;
   }
 
-  auto prev_inst_for_merge = ResolvePrevInstForMerge(context, prev_id);
-  auto prev_function_id =
-      GetRedeclFunctionId(context, prev_inst_for_merge.inst);
+  auto prev_function_id = SemIR::FunctionId::Invalid;
+  auto prev_import_ir_id = SemIR::ImportIRId::Invalid;
+  CARBON_KIND_SWITCH(context.insts().Get(prev_id)) {
+    case CARBON_KIND(SemIR::FunctionDecl function_decl): {
+      prev_function_id = function_decl.function_id;
+      break;
+    }
+    case CARBON_KIND(SemIR::ImportRefLoaded import_ref): {
+      auto import_ir_inst =
+          context.import_ir_insts().Get(import_ref.import_ir_inst_id);
+
+      // Verify the decl so that things like aliases are name conflicts.
+      const auto* import_ir =
+          context.import_irs().Get(import_ir_inst.ir_id).sem_ir;
+      if (!import_ir->insts().Is<SemIR::FunctionDecl>(import_ir_inst.inst_id)) {
+        break;
+      }
+
+      // Use the type to get the ID.
+      if (auto struct_value = context.insts().TryGetAs<SemIR::StructValue>(
+              context.constant_values().Get(prev_id).inst_id())) {
+        if (auto function_type = context.types().TryGetAs<SemIR::FunctionType>(
+                struct_value->type_id)) {
+          prev_function_id = function_type->function_id;
+          prev_import_ir_id = import_ir_inst.ir_id;
+        }
+      }
+      break;
+    }
+    default:
+      break;
+  }
+
   if (!prev_function_id.is_valid()) {
     context.DiagnoseDuplicateName(function_info.decl_id, prev_id);
     return;
@@ -103,8 +111,7 @@ static auto TryMergeRedecl(Context& context, Parse::AnyFunctionDeclId node_id,
 
   if (MergeFunctionRedecl(context, node_id, function_info,
                           /*new_is_import=*/false, is_definition,
-                          prev_function_id,
-                          prev_inst_for_merge.import_ir_inst_id)) {
+                          prev_function_id, prev_import_ir_id)) {
     // When merging, use the existing function rather than adding a new one.
     function_decl.function_id = prev_function_id;
   }

+ 2 - 18
toolchain/check/merge.cpp

@@ -72,10 +72,9 @@ static auto DiagnoseNonExtern(Context& context, Lex::TokenKind decl_kind,
 // These all still merge.
 auto CheckIsAllowedRedecl(Context& context, Lex::TokenKind decl_kind,
                           SemIR::NameId name_id, RedeclInfo new_decl,
-                          RedeclInfo prev_decl,
-                          SemIR::ImportIRInstId prev_import_ir_inst_id)
+                          RedeclInfo prev_decl, SemIR::ImportIRId import_ir_id)
     -> void {
-  if (!prev_import_ir_inst_id.is_valid()) {
+  if (!import_ir_id.is_valid()) {
     // Check for disallowed redeclarations in the same file.
     if (!new_decl.is_definition) {
       DiagnoseRedundant(context, decl_kind, name_id, new_decl.loc,
@@ -96,8 +95,6 @@ auto CheckIsAllowedRedecl(Context& context, Lex::TokenKind decl_kind,
     return;
   }
 
-  auto import_ir_id =
-      context.import_ir_insts().Get(prev_import_ir_inst_id).ir_id;
   if (import_ir_id == SemIR::ImportIRId::ApiForImpl) {
     // Check for disallowed redeclarations in the same library. Note that a
     // forward declaration in the impl is allowed.
@@ -125,19 +122,6 @@ auto CheckIsAllowedRedecl(Context& context, Lex::TokenKind decl_kind,
   }
 }
 
-auto ResolvePrevInstForMerge(Context& context, SemIR::InstId prev_inst_id)
-    -> InstForMerge {
-  InstForMerge result = {.inst = context.insts().Get(prev_inst_id),
-                         .import_ir_inst_id = SemIR::ImportIRInstId::Invalid};
-  if (auto import_ref = result.inst.TryAs<SemIR::ImportRefLoaded>()) {
-    // Follow the import ref.
-    result.import_ir_inst_id = import_ref->import_ir_inst_id;
-    result.inst = context.insts().Get(
-        context.constant_values().Get(prev_inst_id).inst_id());
-  }
-  return result;
-}
-
 auto ReplacePrevInstForMerge(Context& context, SemIR::NameScopeId scope_id,
                              SemIR::NameId name_id, SemIR::InstId new_inst_id)
     -> void {

+ 1 - 16
toolchain/check/merge.h

@@ -32,22 +32,7 @@ struct RedeclInfo {
 auto CheckIsAllowedRedecl(Context& context, Lex::TokenKind decl_kind,
                           SemIR::NameId name_id, RedeclInfo new_decl,
                           RedeclInfo prev_decl,
-                          SemIR::ImportIRInstId prev_import_ir_inst_id) -> void;
-
-struct InstForMerge {
-  // The resolved instruction.
-  SemIR::Inst inst;
-  // The imported instruction, or invalid if not an import. This should
-  // typically only be used for the ImportIRId, but we only load it if needed.
-  SemIR::ImportIRInstId import_ir_inst_id;
-};
-
-// Resolves prev_inst_id for merging (or name conflicts). This handles imports
-// to return the instruction relevant for a merge. If an import is found and was
-// previously used, it notes it, although an invalid redeclaration may diagnose
-// for other reasons too.
-auto ResolvePrevInstForMerge(Context& context, SemIR::InstId prev_inst_id)
-    -> InstForMerge;
+                          SemIR::ImportIRId prev_import_ir_id) -> void;
 
 // When the prior name lookup result is an import and we are successfully
 // merging, replace the name lookup result with the reference in the current

+ 242 - 0
toolchain/check/testdata/class/no_prelude/implicit_import.carbon

@@ -0,0 +1,242 @@
+// 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
+
+// --- basic.carbon
+
+library "basic" api;
+
+class C;
+
+// --- basic.impl.carbon
+
+library "basic" impl;
+
+class C {}
+
+// --- redecl_after_def.carbon
+
+library "redecl_after_def" api;
+
+class C {}
+
+// --- fail_redecl_after_def.impl.carbon
+
+library "redecl_after_def" impl;
+
+// CHECK:STDERR: fail_redecl_after_def.impl.carbon:[[@LINE+10]]:1: ERROR: Redeclaration of `class C` is redundant.
+// CHECK:STDERR: class C;
+// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR: fail_redecl_after_def.impl.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: library "redecl_after_def" impl;
+// CHECK:STDERR: ^~~~~~~
+// CHECK:STDERR: redecl_after_def.carbon:4:1: Previously declared here.
+// CHECK:STDERR: class C {}
+// CHECK:STDERR: ^~~~~~~~~
+// CHECK:STDERR:
+class C;
+
+// --- redef_after_def.carbon
+
+library "redef_after_def" api;
+
+class C {}
+
+// --- fail_redef_after_def.impl.carbon
+
+library "redef_after_def" impl;
+
+// CHECK:STDERR: fail_redef_after_def.impl.carbon:[[@LINE+10]]:1: ERROR: Redefinition of `class C`.
+// CHECK:STDERR: class C {}
+// CHECK:STDERR: ^~~~~~~~~
+// CHECK:STDERR: fail_redef_after_def.impl.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: library "redef_after_def" impl;
+// CHECK:STDERR: ^~~~~~~
+// CHECK:STDERR: redef_after_def.carbon:4:1: Previously defined here.
+// CHECK:STDERR: class C {}
+// CHECK:STDERR: ^~~~~~~~~
+// CHECK:STDERR:
+class C {}
+
+// --- def_alias.carbon
+
+library "def_alias" api;
+
+class C;
+alias B = C;
+
+// --- fail_def_alias.impl.carbon
+
+library "def_alias" impl;
+
+// CHECK:STDERR: fail_def_alias.impl.carbon:[[@LINE+9]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: class B {}
+// CHECK:STDERR: ^~~~~~~~~
+// CHECK:STDERR: fail_def_alias.impl.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: library "def_alias" impl;
+// CHECK:STDERR: ^~~~~~~
+// CHECK:STDERR: def_alias.carbon:5:7: Name is previously declared here.
+// CHECK:STDERR: alias B = C;
+// CHECK:STDERR:       ^
+class B {}
+
+// CHECK:STDOUT: --- basic.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <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: --- basic.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref: type = import_ref ir0, inst+1, loaded [template = constants.%C]
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- redecl_after_def.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <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: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_redecl_after_def.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir0, inst+1, loaded [template = constants.%C]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir0, inst+2, unloaded
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = file.%import_ref.2
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- redef_after_def.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <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: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_redef_after_def.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1: type = import_ref ir0, inst+1, loaded [template = constants.%C]
+// CHECK:STDOUT:   %import_ref.2 = import_ref ir0, inst+2, unloaded
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%C
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- def_alias.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %C.decl
+// CHECK:STDOUT:     .B = %B
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [template = constants.%C] {}
+// CHECK:STDOUT:   %C.ref: type = name_ref C, %C.decl [template = constants.%C]
+// CHECK:STDOUT:   %B: type = bind_alias B, %C.decl [template = constants.%C]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C;
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_def_alias.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %C: type = class_type @C [template]
+// CHECK:STDOUT:   %.1: type = class_type @.1 [template]
+// CHECK:STDOUT:   %.2: type = struct_type {} [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .C = %import_ref.1
+// CHECK:STDOUT:     .B = %import_ref.2
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref ir0, inst+1, unloaded
+// CHECK:STDOUT:   %import_ref.2: type = import_ref ir0, inst+4, loaded [template = constants.%C]
+// CHECK:STDOUT:   %.decl: type = class_decl @.1 [template = constants.%.1] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @C;
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @.1 {
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%.1
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 71 - 1
toolchain/check/testdata/function/definition/no_prelude/implicit_import.carbon

@@ -86,7 +86,7 @@ fn A() {}
 
 library "redef_after_def" impl;
 
-// CHECK:STDERR: fail_redef_after_def.impl.carbon:[[@LINE+9]]:1: ERROR: Redefinition of `fn A`.
+// CHECK:STDERR: fail_redef_after_def.impl.carbon:[[@LINE+10]]:1: ERROR: Redefinition of `fn A`.
 // CHECK:STDERR: fn A() {}
 // CHECK:STDERR: ^~~~~~~~
 // CHECK:STDERR: fail_redef_after_def.impl.carbon:[[@LINE-5]]:1: In import.
@@ -95,8 +95,31 @@ library "redef_after_def" impl;
 // CHECK:STDERR: redef_after_def.carbon:4:1: Previously defined here.
 // CHECK:STDERR: fn A() {}
 // CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR:
 fn A() {}
 
+// --- def_alias.carbon
+
+library "def_alias" api;
+
+fn A();
+alias B = A;
+
+// --- fail_def_alias.impl.carbon
+
+library "def_alias" impl;
+
+// CHECK:STDERR: fail_def_alias.impl.carbon:[[@LINE+9]]:1: ERROR: Duplicate name being declared in the same scope.
+// CHECK:STDERR: fn B() {}
+// CHECK:STDERR: ^~~~~~~~
+// CHECK:STDERR: fail_def_alias.impl.carbon:[[@LINE-5]]:1: In import.
+// CHECK:STDERR: library "def_alias" impl;
+// CHECK:STDERR: ^~~~~~~
+// CHECK:STDERR: def_alias.carbon:5:7: Name is previously declared here.
+// CHECK:STDERR: alias B = A;
+// CHECK:STDERR:       ^
+fn B() {}
+
 // CHECK:STDOUT: --- basic.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -290,3 +313,50 @@ fn A() {}
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- def_alias.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %A: type = fn_type @A [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %struct: A = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .A = %A.decl
+// CHECK:STDOUT:     .B = %B
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %A.decl: A = fn_decl @A [template = constants.%struct] {}
+// CHECK:STDOUT:   %A.ref: A = name_ref A, %A.decl [template = constants.%struct]
+// CHECK:STDOUT:   %B: A = bind_alias B, %A.decl [template = constants.%struct]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @A();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_def_alias.impl.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %A: type = fn_type @A [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %struct.1: A = struct_value () [template]
+// CHECK:STDOUT:   %.2: type = fn_type @.1 [template]
+// CHECK:STDOUT:   %struct.2: <invalid> = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .A = %import_ref.1
+// CHECK:STDOUT:     .B = %import_ref.2
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref.1 = import_ref ir0, inst+1, unloaded
+// CHECK:STDOUT:   %import_ref.2: A = import_ref ir0, inst+6, loaded [template = constants.%struct.1]
+// CHECK:STDOUT:   %.decl: <invalid> = fn_decl @.1 [template = constants.%struct.2] {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @A();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @.1() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT: