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>
This commit is contained in:
Jon Ross-Perkins
2024-05-06 21:27:21 +00:00
committed by GitHub
co-authored by Richard Smith
parent d5c0c9cfe3
commit 292a9d3cb4
10 changed files with 400 additions and 97 deletions
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+42 -27
View File
@@ -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;
break;
}
default:
// This is a redeclaration of something other than a class.
context.DiagnoseDuplicateName(class_decl_id, prev_id);
break;
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);
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;
// 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;
}
} else {
// This is a redeclaration of something other than a class.
context.DiagnoseDuplicateName(class_decl_id, prev_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;
}
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
View File
@@ -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
View File
@@ -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
View File
@@ -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
@@ -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:
@@ -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: