Unify MergeRedecl functions (#7632)

As discussed in #7620.
This commit is contained in:
Özgür T. Önsoy
2026-08-25 18:43:52 +00:00
committed by GitHub
parent 1410795bf7
commit 2bdcb1fa62
17 changed files with 470 additions and 905 deletions
+8 -125
View File
@@ -26,6 +26,7 @@
#include "toolchain/check/type_completion.h"
#include "toolchain/diagnostics/emitter.h"
#include "toolchain/parse/node_ids.h"
#include "toolchain/sem_ir/class.h"
#include "toolchain/sem_ir/function.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/inst.h"
@@ -48,128 +49,6 @@ auto HandleParseNode(Context& context, Parse::ClassIntroducerId node_id)
return true;
}
// Tries to merge new_class into prev_class_id. Since new_class won't have a
// definition even if one is upcoming, set is_definition to indicate the planned
// result.
//
// If merging is successful, returns true and may update the previous class.
// Otherwise, returns false. Prints a diagnostic when appropriate.
static auto MergeClassRedecl(Context& context, Parse::AnyClassDeclId node_id,
SemIR::Class& new_class, bool new_is_definition,
SemIR::ClassId prev_class_id,
SemIR::ImportIRId prev_import_ir_id) -> bool {
auto& prev_class = context.classes().Get(prev_class_id);
SemIR::LocId prev_loc_id(prev_class.latest_decl_id());
// Check the generic parameters match, if they were specified.
if (!CheckRedeclParamsMatch(context, DeclParams(new_class),
DeclParams(prev_class))) {
return false;
}
DiagnoseIfInvalidRedecl(
context, Lex::TokenKind::Class, prev_class.name_id,
RedeclInfo(new_class, node_id, new_is_definition),
RedeclInfo(prev_class, prev_loc_id, prev_class.has_definition_started()),
prev_import_ir_id);
if (new_is_definition && prev_class.has_definition_started()) {
// Don't attempt to merge multiple definitions.
return false;
}
if (new_is_definition) {
prev_class.MergeDefinition(new_class);
}
if (prev_import_ir_id.has_value() ||
(prev_class.is_extern && !new_class.is_extern)) {
prev_class.first_owning_decl_id = new_class.first_owning_decl_id;
ReplacePrevInstForMerge(context, new_class.parent_scope_id,
prev_class.name_id, new_class.first_owning_decl_id);
}
return true;
}
// Adds the name to name lookup. If there's a conflict, tries to merge. May
// update class_decl and class_info when merging.
static auto MergeOrAddName(Context& context, Parse::AnyClassDeclId node_id,
const DeclNameStack::NameContext& name_context,
SemIR::InstId class_decl_id,
SemIR::ClassDecl& class_decl,
SemIR::Class& class_info, bool is_definition,
SemIR::AccessKind access_kind) -> void {
SemIR::ScopeLookupResult lookup_result =
context.decl_name_stack().LookupOrAddName(name_context, class_decl_id,
access_kind);
if (lookup_result.is_poisoned()) {
// This is a declaration of a poisoned name.
DiagnosePoisonedName(context, name_context.name_id_for_new_inst(),
lookup_result.poisoning_loc_id(), name_context.loc_id);
return;
}
if (!lookup_result.is_found()) {
return;
}
SemIR::InstId prev_id = lookup_result.target_inst_id();
auto prev_class_id = SemIR::ClassId::None;
auto prev_import_ir_id = SemIR::ImportIRId::None;
auto prev = context.insts().Get(prev_id);
CARBON_KIND_SWITCH(prev) {
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;
}
// Use the constant value to get the ID.
auto decl_value = context.insts().Get(
context.constant_values().GetConstantInstId(prev_id));
if (auto class_type = decl_value.TryAs<SemIR::ClassType>()) {
prev_class_id = class_type->class_id;
prev_import_ir_id = import_ir_inst.ir_id();
} else if (auto generic_class_type =
context.types().TryGetAs<SemIR::GenericClassType>(
decl_value.type_id())) {
prev_class_id = generic_class_type->class_id;
prev_import_ir_id = import_ir_inst.ir_id();
}
break;
}
default:
break;
}
if (!prev_class_id.has_value()) {
// This is a redeclaration of something other than a class.
DiagnoseDuplicateName(context, name_context.name_id, name_context.loc_id,
SemIR::LocId(prev_id));
return;
}
// TODO: Fix `extern` logic. It doesn't work correctly, but doesn't seem worth
// ripping out because existing code may incrementally help.
if (MergeClassRedecl(context, node_id, class_info, is_definition,
prev_class_id, prev_import_ir_id)) {
// When merging, use the existing entity rather than adding a new one.
class_decl.class_id = prev_class_id;
class_decl.type_id = prev.type_id();
// TODO: Validate that the redeclaration doesn't set an access modifier.
}
}
static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
bool is_definition)
-> std::tuple<SemIR::ClassId, SemIR::InstId> {
@@ -223,9 +102,13 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
DiagnoseIfGenericMissingExplicitParameters(context, class_info);
MergeOrAddName(context, node_id, name_context, class_decl_id, class_decl,
class_info, is_definition,
introducer.modifier_set.GetAccessKind());
SemIR::ScopeLookupResult lookup_result =
context.decl_name_stack().LookupOrAddName(
name_context, class_decl_id, introducer.modifier_set.GetAccessKind());
TryMergeRedecl(context, name_context, lookup_result,
MergeRedeclEntityInfo<SemIR::Class>{
.new_entity_decl = class_decl, .new_entity = class_info},
is_definition);
// Create a new class if this isn't a valid redeclaration.
bool is_new_class = !class_decl.class_id.has_value();
+5 -127
View File
@@ -141,131 +141,6 @@ static auto GetInterfaceModifier(const KeywordModifierSet& modifier_set)
.Default(None);
}
// Tries to merge new_function into prev_function_id. Since new_function won't
// have a definition even if one is upcoming, set is_definition to indicate the
// planned result.
//
// If merging is successful, returns true and may update the previous function.
// Otherwise, returns false. Prints a diagnostic when appropriate.
static auto MergeFunctionRedecl(Context& context,
Parse::AnyFunctionDeclId node_id,
SemIR::Function& new_function,
bool new_is_definition,
SemIR::FunctionId prev_function_id,
SemIR::ImportIRId prev_import_ir_id) -> bool {
auto& prev_function = context.functions().Get(prev_function_id);
if (!CheckFunctionTypeMatches(context, new_function, prev_function)) {
return false;
}
DiagnoseIfInvalidRedecl(
context, Lex::TokenKind::Fn, prev_function.name_id,
RedeclInfo(new_function, node_id, new_is_definition),
RedeclInfo(prev_function, SemIR::LocId(prev_function.latest_decl_id()),
prev_function.has_definition_started()),
prev_import_ir_id);
if (new_is_definition && prev_function.has_definition_started()) {
return false;
}
if (!prev_function.first_owning_decl_id.has_value()) {
prev_function.first_owning_decl_id = new_function.first_owning_decl_id;
}
if (new_is_definition) {
// Track the signature from the definition, so that IDs in the body
// match IDs in the signature.
prev_function.MergeDefinition(new_function);
}
if (prev_import_ir_id.has_value()) {
ReplacePrevInstForMerge(context, new_function.parent_scope_id,
prev_function.name_id,
new_function.first_owning_decl_id);
}
return true;
}
// Check whether this is a redeclaration, merging if needed.
static auto TryMergeRedecl(Context& context, Parse::AnyFunctionDeclId node_id,
const DeclNameStack::NameContext& name_context,
SemIR::FunctionDecl& function_decl,
SemIR::Function& function_info, bool is_definition)
-> void {
// Diagnose if we are declaring a poisoned name. However, don't diagnose at
// impl scope: if the name was referenced before being declared, we will have
// produced an error already.
if (name_context.state == DeclNameStack::NameContext::State::Poisoned) {
if (!context.name_scopes().InstIs<SemIR::ImplDecl>(
name_context.parent_scope_id)) {
DiagnosePoisonedName(context, name_context.name_id_for_new_inst(),
name_context.poisoning_loc_id, name_context.loc_id);
}
return;
}
auto prev_id = name_context.prev_inst_id();
if (!prev_id.has_value()) {
return;
}
auto prev_function_id = SemIR::FunctionId::None;
auto prev_type_id = SemIR::TypeId::None;
auto prev_import_ir_id = SemIR::ImportIRId::None;
CARBON_KIND_SWITCH(context.insts().Get(prev_id)) {
case CARBON_KIND(SemIR::AssociatedEntity assoc_entity): {
// This is a function in an interface definition scope.
auto function_decl =
context.insts().GetAs<SemIR::FunctionDecl>(assoc_entity.decl_id);
prev_function_id = function_decl.function_id;
prev_type_id = function_decl.type_id;
break;
}
case CARBON_KIND(SemIR::FunctionDecl function_decl): {
prev_function_id = function_decl.function_id;
prev_type_id = function_decl.type_id;
break;
}
case SemIR::ImportRefLoaded::Kind: {
auto import_ir_inst = GetCanonicalImportIRInst(context, prev_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().GetConstantInstId(prev_id))) {
if (auto function_type = context.types().TryGetAs<SemIR::FunctionType>(
struct_value->type_id)) {
prev_function_id = function_type->function_id;
prev_type_id = struct_value->type_id;
prev_import_ir_id = import_ir_inst.ir_id();
}
}
break;
}
default:
break;
}
if (!prev_function_id.has_value()) {
DiagnoseDuplicateName(context, name_context.name_id, name_context.loc_id,
SemIR::LocId(prev_id));
return;
}
if (MergeFunctionRedecl(context, node_id, function_info, is_definition,
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;
function_decl.type_id = prev_type_id;
}
}
// Adds the declaration to name lookup when appropriate.
static auto MaybeAddToNameLookup(Context& context,
const DeclNameStack::NameContext& name_context,
@@ -580,8 +455,11 @@ static auto BuildFunctionDecl(Context& context,
DiagnosePositionalParams(context, function_info);
TryMergeRedecl(context, node_id, name_context, function_decl, function_info,
is_definition);
TryMergeRedecl(
context, name_context, std::nullopt,
MergeRedeclEntityInfo<SemIR::Function>{.new_entity_decl = function_decl,
.new_entity = function_info},
is_definition);
// Create a new function if this isn't a valid redeclaration.
if (!function_decl.function_id.has_value()) {
+5 -6
View File
@@ -73,12 +73,11 @@ static auto BuildInterfaceDecl(Context& context,
SemIR::ScopeLookupResult lookup_result =
context.decl_name_stack().LookupOrAddName(
name_context, decl_inst_id, introducer.modifier_set.GetAccessKind());
if (auto existing_decl = TryGetExistingDecl(context, name, lookup_result,
interface_info, is_definition)) {
auto existing_interface_decl = existing_decl->As<SemIR::InterfaceDecl>();
interface_decl.interface_id = existing_interface_decl.interface_id;
interface_decl.type_id = existing_interface_decl.type_id;
if (TryMergeRedecl(
context, name_context, lookup_result,
MergeRedeclEntityInfo<SemIR::Interface>{
.new_entity_decl = interface_decl, .new_entity = interface_info},
is_definition)) {
auto prev_decl_generic_id =
context.interfaces().Get(interface_decl.interface_id).generic_id;
FinishGenericRedecl(context, prev_decl_generic_id);
+6 -11
View File
@@ -7,6 +7,7 @@
#include "toolchain/check/handle.h"
#include "toolchain/check/inst.h"
#include "toolchain/check/interface.h"
#include "toolchain/check/merge.h"
#include "toolchain/check/modifiers.h"
#include "toolchain/check/type.h"
#include "toolchain/sem_ir/ids.h"
@@ -69,17 +70,11 @@ static auto BuildNamedConstraintDecl(Context& context,
SemIR::ScopeLookupResult lookup_result =
context.decl_name_stack().LookupOrAddName(
name_context, decl_inst_id, introducer.modifier_set.GetAccessKind());
if (auto existing_decl = TryGetExistingDecl(context, name, lookup_result,
constraint_info, is_definition)) {
auto existing_constraint_decl =
existing_decl->As<SemIR::NamedConstraintDecl>();
constraint_decl.named_constraint_id =
existing_constraint_decl.named_constraint_id;
constraint_decl.type_id = existing_constraint_decl.type_id;
// TODO: If the new declaration is a definition, keep its parameter
// and implicit parameter lists rather than the ones from the
// previous declaration.
if (TryMergeRedecl(context, name_context, lookup_result,
MergeRedeclEntityInfo<SemIR::NamedConstraint>{
.new_entity_decl = constraint_decl,
.new_entity = constraint_info},
is_definition)) {
auto prev_decl_generic_id = context.named_constraints()
.Get(constraint_decl.named_constraint_id)
.generic_id;
-163
View File
@@ -156,167 +156,4 @@ auto AddSelfSymbolicBindingToScope(Context& context,
return self_param_inst_id;
}
template <typename EntityT>
requires std::same_as<EntityT, SemIR::Interface>
static constexpr auto DeclTokenKind() -> Lex::TokenKind {
return Lex::TokenKind::Interface;
}
template <typename EntityT>
requires std::same_as<EntityT, SemIR::NamedConstraint>
static constexpr auto DeclTokenKind() -> Lex::TokenKind {
return Lex::TokenKind::Constraint;
}
template <typename EntityT>
requires SameAsOneOf<EntityT, SemIR::Interface, SemIR::NamedConstraint>
auto TryGetExistingDecl(Context& context, const NameComponent& name,
SemIR::ScopeLookupResult lookup_result,
const EntityT& entity, bool is_definition)
-> std::optional<SemIR::Inst> {
using EntityIdT =
std::conditional_t<std::is_same_v<EntityT, SemIR::Interface>,
SemIR::InterfaceId, SemIR::NamedConstraintId>;
constexpr bool IsInterface = std::is_same_v<EntityIdT, SemIR::InterfaceId>;
if (lookup_result.is_poisoned()) {
// This is a declaration of a poisoned name.
DiagnosePoisonedName(context, name.name_id,
lookup_result.poisoning_loc_id(), name.name_loc_id);
return std::nullopt;
}
if (!lookup_result.is_found()) {
return std::nullopt;
}
auto prev_id = lookup_result.target_inst_id();
auto prev = context.insts().Get(prev_id);
auto prev_entity_id = EntityIdT::None;
auto prev_import_ir_id = SemIR::ImportIRId::None;
auto existing_decl_id = SemIR::InstId::None;
CARBON_KIND_SWITCH(prev) {
case CARBON_KIND(SemIR::InterfaceDecl interface_decl): {
if constexpr (IsInterface) {
prev_entity_id = interface_decl.interface_id;
existing_decl_id = prev_id;
}
break;
}
case CARBON_KIND(SemIR::NamedConstraintDecl named_constraint_decl): {
if constexpr (!IsInterface) {
prev_entity_id = named_constraint_decl.named_constraint_id;
existing_decl_id = prev_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 constexpr (IsInterface) {
if (!import_ir->insts().Is<SemIR::InterfaceDecl>(
import_ir_inst.inst_id())) {
break;
}
} else {
if (!import_ir->insts().Is<SemIR::NamedConstraintDecl>(
import_ir_inst.inst_id())) {
break;
}
}
// Use the constant value to get the ID.
auto decl_value = context.insts().Get(
context.constant_values().GetConstantInstId(prev_id));
if (auto facet_type = decl_value.TryAs<SemIR::FacetType>()) {
auto declared_facet_type = context.declared_facet_types().Get(
facet_type->declared_facet_type_id);
if constexpr (IsInterface) {
prev_entity_id =
declared_facet_type.extend_constraints[0].interface_id;
} else {
prev_entity_id = declared_facet_type.extend_named_constraints[0]
.named_constraint_id;
}
prev_import_ir_id = import_ir_inst.ir_id();
}
break;
}
default:
break;
}
if (!prev_entity_id.has_value()) {
// This is a redeclaration with a different entity kind.
DiagnoseDuplicateName(context, name.name_id, name.name_loc_id,
SemIR::LocId(prev_id));
return std::nullopt;
}
auto& prev_entity = [&]() -> EntityT& {
if constexpr (IsInterface) {
return context.interfaces().Get(prev_entity_id);
} else {
return context.named_constraints().Get(prev_entity_id);
}
}();
if (!CheckRedeclParamsMatch(
context,
DeclParams(SemIR::LocId(entity.latest_decl_id()),
name.first_param_node_id, name.last_param_node_id,
name.implicit_param_patterns_id, name.param_patterns_id),
DeclParams(prev_entity))) {
// Mismatch is diagnosed already if found.
return std::nullopt;
}
DiagnoseIfInvalidRedecl(
context, DeclTokenKind<EntityT>(), prev_entity.name_id,
RedeclInfo(entity, SemIR::LocId(entity.latest_decl_id()), is_definition),
RedeclInfo(prev_entity, SemIR::LocId(prev_entity.latest_decl_id()),
prev_entity.has_definition_started()),
prev_import_ir_id);
if (is_definition && prev_entity.has_definition_started()) {
// DiagnoseIfInvalidRedecl would diagnose an error in this case, since we'd
// have two definitions. Given the declaration parts of the definitions
// match, we would be able to use the prior declaration for error recovery,
// except that having two definitions causes larger problems for generics.
// All interfaces (and named constraints) are generic with an implicit Self
// compile time binding.
return std::nullopt;
}
if (is_definition) {
prev_entity.MergeDefinition(entity);
}
if (prev_import_ir_id.has_value()) {
prev_entity.first_owning_decl_id = entity.first_owning_decl_id;
ReplacePrevInstForMerge(context, entity.parent_scope_id,
prev_entity.name_id, entity.first_owning_decl_id);
}
// This is a matching redeclaration of an existing entity of the same type.
return existing_decl_id.has_value() ? std::optional<SemIR::Inst>(prev)
: std::nullopt;
}
template auto TryGetExistingDecl(Context& context, const NameComponent& name,
SemIR::ScopeLookupResult lookup_result,
const SemIR::Interface& entity,
bool is_definition)
-> std::optional<SemIR::Inst>;
template auto TryGetExistingDecl(Context& context, const NameComponent& name,
SemIR::ScopeLookupResult lookup_result,
const SemIR::NamedConstraint& entity,
bool is_definition)
-> std::optional<SemIR::Inst>;
} // namespace Carbon::Check
+325
View File
@@ -560,4 +560,329 @@ auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity,
return true;
}
// Fills the previous class id, type id, and import ir id.
static auto FillPrevEntityInfo(Context& context,
const SemIR::ImportIRInst& import_ir_inst,
SemIR::Inst decl_val,
SemIR::ClassId& prev_entity_id,
SemIR::TypeId& prev_type_id,
SemIR::ImportIRId& prev_import_ir_id) -> void {
// 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())) {
return;
}
if (auto class_type = decl_val.TryAs<SemIR::ClassType>()) {
prev_entity_id = class_type->class_id;
prev_type_id = SemIR::TypeId::None;
prev_import_ir_id = import_ir_inst.ir_id();
} else if (auto generic_class_type =
context.types().TryGetAs<SemIR::GenericClassType>(
decl_val.type_id())) {
prev_entity_id = generic_class_type->class_id;
prev_type_id = SemIR::TypeId::None;
prev_import_ir_id = import_ir_inst.ir_id();
}
}
// Fills the previous function id, type id, and import ir id.
static auto FillPrevEntityInfo(Context& context,
const SemIR::ImportIRInst& import_ir_inst,
SemIR::Inst decl_val,
SemIR::FunctionId& prev_entity_id,
SemIR::TypeId& prev_type_id,
SemIR::ImportIRId& prev_import_ir_id) -> void {
// 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())) {
return;
}
if (auto struct_value = decl_val.TryAs<SemIR::StructValue>()) {
if (auto function_type = context.types().TryGetAs<SemIR::FunctionType>(
struct_value->type_id)) {
prev_entity_id = function_type->function_id;
prev_type_id = struct_value->type_id;
prev_import_ir_id = import_ir_inst.ir_id();
}
}
}
// Fills the previous interface id, type id, and import ir id.
static auto FillPrevEntityInfo(Context& context,
const SemIR::ImportIRInst& import_ir_inst,
SemIR::Inst decl_val,
SemIR::InterfaceId& prev_entity_id,
SemIR::TypeId& prev_type_id,
SemIR::ImportIRId& prev_import_ir_id) -> void {
// 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::InterfaceDecl>(import_ir_inst.inst_id())) {
return;
}
if (auto facet_type = decl_val.TryAs<SemIR::FacetType>()) {
auto declared_facet_type =
context.declared_facet_types().Get(facet_type->declared_facet_type_id);
prev_entity_id = declared_facet_type.extend_constraints[0].interface_id;
prev_type_id = SemIR::TypeId::None;
prev_import_ir_id = import_ir_inst.ir_id();
}
}
// Fills the previous named constraint id, type id, and import ir id.
static auto FillPrevEntityInfo(Context& context,
const SemIR::ImportIRInst& import_ir_inst,
SemIR::Inst decl_val,
SemIR::NamedConstraintId& prev_entity_id,
SemIR::TypeId& prev_type_id,
SemIR::ImportIRId& prev_import_ir_id) -> void {
// 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::NamedConstraintDecl>(
import_ir_inst.inst_id())) {
return;
}
if (auto facet_type = decl_val.TryAs<SemIR::FacetType>()) {
auto declared_facet_type =
context.declared_facet_types().Get(facet_type->declared_facet_type_id);
prev_entity_id =
declared_facet_type.extend_named_constraints[0].named_constraint_id;
prev_type_id = SemIR::TypeId::None;
prev_import_ir_id = import_ir_inst.ir_id();
}
}
template <typename EntityT>
auto TryMergeRedecl(Context& context,
const DeclNameStack::NameContext& name_context,
std::optional<SemIR::ScopeLookupResult> lookup_result,
MergeRedeclEntityInfo<EntityT> entity_info,
bool is_definition) -> bool {
constexpr bool IsClass = std::is_same_v<EntityT, SemIR::Class>;
constexpr bool IsFunction = std::is_same_v<EntityT, SemIR::Function>;
constexpr bool IsInterface = std::is_same_v<EntityT, SemIR::Interface>;
constexpr bool IsNamedConstraint =
std::is_same_v<EntityT, SemIR::NamedConstraint>;
if constexpr (IsFunction) {
CARBON_CHECK(!lookup_result.has_value());
// Diagnose if we are declaring a poisoned name. However, don't diagnose
// at impl scope: if the name was referenced before being declared, we
// will have produced an error already.
if (name_context.state == DeclNameStack::NameContext::State::Poisoned) {
if (!context.name_scopes().InstIs<SemIR::ImplDecl>(
name_context.parent_scope_id)) {
DiagnosePoisonedName(context, name_context.name_id_for_new_inst(),
name_context.poisoning_loc_id,
name_context.loc_id);
}
return false;
}
} else if constexpr (IsClass || IsInterface || IsNamedConstraint) {
CARBON_CHECK(lookup_result.has_value());
if (lookup_result->is_poisoned()) {
DiagnosePoisonedName(context, name_context.name_id_for_new_inst(),
lookup_result->poisoning_loc_id(),
name_context.loc_id);
return false;
}
if (!lookup_result->is_found()) {
return false;
}
} else {
CARBON_FATAL("Unhandled entity type.");
}
auto prev_id = lookup_result ? lookup_result->target_inst_id()
: name_context.prev_inst_id();
if (!prev_id.has_value()) {
return false;
}
auto prev = context.insts().Get(prev_id);
auto prev_entity_id = MergeRedeclEntityInfo<EntityT>::EntityIdT::None;
auto prev_type_id = SemIR::TypeId::None;
auto prev_import_ir_id = SemIR::ImportIRId::None;
CARBON_KIND_SWITCH(prev) {
case CARBON_KIND(SemIR::AssociatedEntity assoc_entity): {
if constexpr (IsFunction) {
// This is a function in an interface definition scope.
auto function_decl =
context.insts().GetAs<SemIR::FunctionDecl>(assoc_entity.decl_id);
prev_entity_id = function_decl.function_id;
prev_type_id = function_decl.type_id;
}
break;
}
case CARBON_KIND(SemIR::ClassDecl class_decl): {
if constexpr (IsClass) {
prev_entity_id = class_decl.class_id;
}
break;
}
case CARBON_KIND(SemIR::FunctionDecl function_decl): {
if constexpr (IsFunction) {
prev_entity_id = function_decl.function_id;
prev_type_id = function_decl.type_id;
}
break;
}
case CARBON_KIND(SemIR::InterfaceDecl interface_decl): {
if constexpr (IsInterface) {
prev_entity_id = interface_decl.interface_id;
}
break;
}
case CARBON_KIND(SemIR::NamedConstraintDecl named_constraint_decl): {
if constexpr (IsNamedConstraint) {
prev_entity_id = named_constraint_decl.named_constraint_id;
}
break;
}
case CARBON_KIND(SemIR::ImportRefLoaded import_ref): {
// TODO: Should we get canonical inst for all entity types?
auto import_ir_inst = [&]() -> SemIR::ImportIRInst {
if constexpr (IsClass || IsInterface || IsNamedConstraint) {
return context.import_ir_insts().Get(import_ref.import_ir_inst_id);
} else if constexpr (IsFunction) {
return GetCanonicalImportIRInst(context, prev_id);
} else {
CARBON_FATAL("Unhandled entity type.");
}
}();
auto decl_val = context.insts().Get(
context.constant_values().GetConstantInstId(prev_id));
FillPrevEntityInfo(context, import_ir_inst, decl_val, prev_entity_id,
prev_type_id, prev_import_ir_id);
break;
}
default: {
break;
}
}
if (!prev_entity_id.has_value()) {
// This is a redeclaration with a different entity kind.
DiagnoseDuplicateName(context, name_context.name_id, name_context.loc_id,
SemIR::LocId(prev_id));
return false;
}
auto& prev_entity = [&]() -> EntityT& {
if constexpr (IsClass) {
return context.classes().Get(prev_entity_id);
} else if constexpr (IsFunction) {
return context.functions().Get(prev_entity_id);
} else if constexpr (IsInterface) {
return context.interfaces().Get(prev_entity_id);
} else if constexpr (IsNamedConstraint) {
return context.named_constraints().Get(prev_entity_id);
} else {
CARBON_FATAL("Unhandled entity type.");
}
}();
if constexpr (IsClass || IsInterface || IsNamedConstraint) {
if (!CheckRedeclParamsMatch(context, DeclParams(entity_info.new_entity),
DeclParams(prev_entity))) {
// Mismatch is diagnosed already if found.
return false;
}
} else if constexpr (IsFunction) {
if (!CheckFunctionTypeMatches(context, entity_info.new_entity,
prev_entity)) {
// Mismatch is diagnosed already if found.
return false;
}
} else {
CARBON_FATAL("Unhandled entity type.");
}
DiagnoseIfInvalidRedecl(
context, MergeRedeclEntityInfo<EntityT>::DeclTokenKind,
prev_entity.name_id,
RedeclInfo(entity_info.new_entity,
SemIR::LocId(entity_info.new_entity.latest_decl_id()),
is_definition),
RedeclInfo(prev_entity, SemIR::LocId(prev_entity.latest_decl_id()),
prev_entity.has_definition_started()),
prev_import_ir_id);
if (is_definition && prev_entity.has_definition_started()) {
// DiagnoseIfInvalidRedecl would diagnose an error in this case, since we'd
// have two definitions. Given the declaration parts of the definitions
// match, we would be able to use the prior declaration for error recovery,
// except that having two definitions causes larger problems for generics.
// All interfaces (and named constraints) are generic with an implicit Self
// compile time binding.
return false;
}
if (!prev_entity.first_owning_decl_id.has_value()) {
prev_entity.first_owning_decl_id =
entity_info.new_entity.first_owning_decl_id;
}
if (is_definition) {
prev_entity.MergeDefinition(entity_info.new_entity);
}
auto replace_prev_inst = prev_import_ir_id.has_value();
if constexpr (IsClass) {
replace_prev_inst |=
prev_entity.is_extern && !entity_info.new_entity.is_extern;
}
if (replace_prev_inst) {
ReplacePrevInstForMerge(context, entity_info.new_entity.parent_scope_id,
prev_entity.name_id,
entity_info.new_entity.first_owning_decl_id);
}
// When merging, use the existing entity rather than adding a new one.
if constexpr (IsClass) {
// TODO: Fix `extern` logic. It doesn't work correctly, but doesn't seem
// worth ripping out because existing code may incrementally help.
entity_info.new_entity_decl.class_id = prev_entity_id;
entity_info.new_entity_decl.type_id = prev.type_id();
// TODO: Validate that the redeclaration doesn't set an access modifier.
} else if constexpr (IsFunction) {
entity_info.new_entity_decl.function_id = prev_entity_id;
entity_info.new_entity_decl.type_id = prev_type_id;
} else if constexpr (IsInterface) {
entity_info.new_entity_decl.interface_id = prev_entity_id;
entity_info.new_entity_decl.type_id = prev.type_id();
} else if constexpr (IsNamedConstraint) {
entity_info.new_entity_decl.named_constraint_id = prev_entity_id;
entity_info.new_entity_decl.type_id = prev.type_id();
} else {
CARBON_FATAL("Unhandled entity type.");
}
return true;
}
template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&,
std::optional<SemIR::ScopeLookupResult>,
MergeRedeclEntityInfo<SemIR::Class>, bool) -> bool;
template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&,
std::optional<SemIR::ScopeLookupResult>,
MergeRedeclEntityInfo<SemIR::Function>, bool)
-> bool;
template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&,
std::optional<SemIR::ScopeLookupResult>,
MergeRedeclEntityInfo<SemIR::Interface>, bool)
-> bool;
template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&,
std::optional<SemIR::ScopeLookupResult>,
MergeRedeclEntityInfo<SemIR::NamedConstraint>,
bool) -> bool;
} // namespace Carbon::Check
+103
View File
@@ -5,9 +5,26 @@
#ifndef CARBON_TOOLCHAIN_CHECK_MERGE_H_
#define CARBON_TOOLCHAIN_CHECK_MERGE_H_
#include <optional>
#include <type_traits>
#include "common/check.h"
#include "common/concepts.h"
#include "toolchain/base/kind_switch.h"
#include "toolchain/check/context.h"
#include "toolchain/check/decl_name_stack.h"
#include "toolchain/check/function.h"
#include "toolchain/check/import_ref.h"
#include "toolchain/check/name_lookup.h"
#include "toolchain/check/subst.h"
#include "toolchain/lex/token_kind.h"
#include "toolchain/sem_ir/class.h"
#include "toolchain/sem_ir/function.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/import_ir.h"
#include "toolchain/sem_ir/interface.h"
#include "toolchain/sem_ir/named_constraint.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::Check {
@@ -109,6 +126,92 @@ inline auto CheckRedeclParamsMatch(Context& context,
/*check_syntax=*/true);
}
// Provides type traits and data for merging redeclarations.
template <typename EntityT>
struct MergeRedeclEntityInfo;
// Information for merging redeclarations of classes.
template <>
struct MergeRedeclEntityInfo<SemIR::Class> {
using EntityIdT = SemIR::ClassId;
using EntityT = SemIR::Class;
using EntityDeclT = SemIR::ClassDecl;
static constexpr auto DeclTokenKind = Lex::TokenKind::Class;
EntityDeclT& new_entity_decl;
const EntityT& new_entity;
};
// Information for merging redeclarations of functions.
template <>
struct MergeRedeclEntityInfo<SemIR::Function> {
using EntityIdT = SemIR::FunctionId;
using EntityT = SemIR::Function;
using EntityDeclT = SemIR::FunctionDecl;
static constexpr auto DeclTokenKind = Lex::TokenKind::Fn;
EntityDeclT& new_entity_decl;
const EntityT& new_entity;
};
// Information for merging redeclarations of interfaces.
template <>
struct MergeRedeclEntityInfo<SemIR::Interface> {
using EntityIdT = SemIR::InterfaceId;
using EntityT = SemIR::Interface;
using EntityDeclT = SemIR::InterfaceDecl;
static constexpr auto DeclTokenKind = Lex::TokenKind::Interface;
EntityDeclT& new_entity_decl;
const EntityT& new_entity;
};
// Information for merging redeclarations of named constraints.
template <>
struct MergeRedeclEntityInfo<SemIR::NamedConstraint> {
using EntityIdT = SemIR::NamedConstraintId;
using EntityT = SemIR::NamedConstraint;
using EntityDeclT = SemIR::NamedConstraintDecl;
static constexpr auto DeclTokenKind = Lex::TokenKind::Constraint;
EntityDeclT& new_entity_decl;
const EntityT& new_entity;
};
// Tries to merge new_entity into prev_entity_id. Since new_entity won't have a
// definition even if one is upcoming, set is_definition to indicate the planned
// result.
//
// If merging is successful, returns the previous declaration.
// Otherwise, returns nullopt. Prints a diagnostic when appropriate.
template <typename EntityT>
auto TryMergeRedecl(Context& context,
const DeclNameStack::NameContext& name_context,
std::optional<SemIR::ScopeLookupResult> lookup_result,
MergeRedeclEntityInfo<EntityT> entity_info,
bool is_definition) -> bool;
extern template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&,
std::optional<SemIR::ScopeLookupResult>,
MergeRedeclEntityInfo<SemIR::Class>, bool)
-> bool;
extern template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&,
std::optional<SemIR::ScopeLookupResult>,
MergeRedeclEntityInfo<SemIR::Function>,
bool) -> bool;
extern template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&,
std::optional<SemIR::ScopeLookupResult>,
MergeRedeclEntityInfo<SemIR::Interface>,
bool) -> bool;
extern template auto TryMergeRedecl(
Context&, const DeclNameStack::NameContext&,
std::optional<SemIR::ScopeLookupResult>,
MergeRedeclEntityInfo<SemIR::NamedConstraint>, bool) -> bool;
} // namespace Carbon::Check
#endif // CARBON_TOOLCHAIN_CHECK_MERGE_H_
+1 -1
View File
@@ -479,7 +479,7 @@ private class Redecl {}
// CHECK:STDOUT: %Forward.decl: type = class_decl @Forward [concrete = constants.%Forward] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @Forward {
// CHECK:STDOUT: class @Forward [from "forward.carbon"] {
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT:
+4 -4
View File
@@ -576,7 +576,7 @@ extern class C;
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C;
// CHECK:STDOUT: class @C [from "extern_decl.carbon"];
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_decl_after_import_extern_decl.carbon
// CHECK:STDOUT:
@@ -592,7 +592,7 @@ extern class C;
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C;
// CHECK:STDOUT: class @C [from "decl.carbon"];
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_def_after_import_extern_decl.carbon
// CHECK:STDOUT:
@@ -615,7 +615,7 @@ extern class C;
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT: class @C [from "def.carbon"] {
// CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
@@ -643,7 +643,7 @@ extern class C;
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT: class @C [from "def.carbon"] {
// CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
+1 -1
View File
@@ -385,7 +385,7 @@ class Class(U: type) {
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic class @Class(imports.%Main.import_ref.b3bc94.1: type) {
// CHECK:STDOUT: generic class @Class(imports.%Main.import_ref.b3bc94.1: type) [from "foo.carbon"] {
// CHECK:STDOUT: %T.patt.1: %pattern_type.98f = symbolic_binding_pattern T, 0 [symbolic = %T.patt.1 (constants.%T.patt)]
// CHECK:STDOUT: %T.1: type = symbolic_binding T, 0 [symbolic = %T.1 (constants.%T)]
// CHECK:STDOUT:
+2 -2
View File
@@ -117,7 +117,7 @@ class B {}
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT: class @C [from "basic.carbon"] {
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT:
@@ -170,7 +170,7 @@ class B {}
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C {
// CHECK:STDOUT: class @C [from "redecl_after_def.carbon"] {
// CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
+1 -1
View File
@@ -75,7 +75,7 @@ class ForwardDecl {
// CHECK:STDOUT: %ForwardDecl.decl: type = class_decl @ForwardDecl [concrete = constants.%ForwardDecl] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @ForwardDecl {
// CHECK:STDOUT: class @ForwardDecl [from "a.carbon"] {
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT:
@@ -3,8 +3,6 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon
// TODO: Add ranges and switch to "--dump-sem-ir-ranges=only".
// EXTRA-ARGS: --dump-sem-ir-ranges=if-present
//
// AUTOUPDATE
// TIP: To test this file alone, run:
@@ -76,149 +74,3 @@ impl library "[[@TEST_NAME]]";
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
class D;
// CHECK:STDOUT: --- decl_in_api_definition_in_impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %A: type = class_type @A [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .A = %A.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @A;
// CHECK:STDOUT:
// CHECK:STDOUT: --- todo_fail_decl_in_api_definition_in_impl.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %A: type = class_type @A [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .A = %A.decl.loc5
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_46.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_46.2 = import <none>
// CHECK:STDOUT: %A.decl.loc5: type = class_decl @A [concrete = constants.%A] {} {}
// CHECK:STDOUT: %A.decl.loc7: type = class_decl @A [concrete = constants.%A] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @A {
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type]
// CHECK:STDOUT: complete_type_witness = %complete_type
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%A
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- use_decl_in_api.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- use_decl_in_api.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Main.A = import_ref Main//decl_in_api_definition_in_impl, A, unloaded
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .A = imports.%Main.A
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_31.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_31.2 = import <none>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- decl_only_in_api.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %B: type = class_type @B [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .B = %B.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @B;
// CHECK:STDOUT:
// CHECK:STDOUT: --- decl_only_in_api.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Main.B = import_ref Main//decl_only_in_api, B, unloaded
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .B = imports.%Main.B
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_32.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_32.2 = import <none>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- decl_in_api_decl_in_impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C;
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_decl_in_api_decl_in_impl.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_40.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_40.2 = import <none>
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @C;
// CHECK:STDOUT:
// CHECK:STDOUT: --- decl_only_in_impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_decl_only_in_impl.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %D: type = class_type @D [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .D = %D.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_33.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_33.2 = import <none>
// CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: class @D;
// CHECK:STDOUT:
+3 -3
View File
@@ -687,7 +687,7 @@ fn Base.F(ref self: Base) {
// CHECK:STDOUT: .Self = imports.%Main.import_ref.e47
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic class @Foo(imports.%Main.import_ref.99696d.1: %C) {
// CHECK:STDOUT: generic class @Foo(imports.%Main.import_ref.99696d.1: %C) [from "two_file.carbon"] {
// CHECK:STDOUT: %a.patt.1: %pattern_type = symbolic_binding_pattern a, 0 [symbolic = %a.patt.1 (constants.%a.patt)]
// CHECK:STDOUT: %a.1: %C = symbolic_binding a, 0 [symbolic = %a.1 (constants.%a)]
// CHECK:STDOUT:
@@ -702,7 +702,7 @@ fn Base.F(ref self: Base) {
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic class @Bar(imports.%Main.import_ref.99696d.2: %C) {
// CHECK:STDOUT: generic class @Bar(imports.%Main.import_ref.99696d.2: %C) [from "two_file.carbon"] {
// CHECK:STDOUT: %a.patt.1: %pattern_type = symbolic_binding_pattern a, 0 [symbolic = %a.patt.1 (constants.%a.patt)]
// CHECK:STDOUT: %a.1: %C = symbolic_binding a, 0 [symbolic = %a.1 (constants.%a)]
// CHECK:STDOUT:
@@ -1093,7 +1093,7 @@ fn Base.F(ref self: Base) {
// CHECK:STDOUT: .Self = imports.%Main.import_ref.e47
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic class @Foo(imports.%Main.import_ref.996: %C) {
// CHECK:STDOUT: generic class @Foo(imports.%Main.import_ref.996: %C) [from "alias_two_file.carbon"] {
// CHECK:STDOUT: %a.patt.1: %pattern_type = symbolic_binding_pattern a, 0 [symbolic = %a.patt.1 (constants.%a.patt)]
// CHECK:STDOUT: %a.1: %C = symbolic_binding a, 0 [symbolic = %a.1 (constants.%a)]
// CHECK:STDOUT:
@@ -3,8 +3,6 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon
// TODO: Add ranges and switch to "--dump-sem-ir-ranges=only".
// EXTRA-ARGS: --dump-sem-ir-ranges=if-present
//
// AUTOUPDATE
// TIP: To test this file alone, run:
@@ -76,150 +74,3 @@ impl library "[[@TEST_NAME]]";
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
fn D();
// CHECK:STDOUT: --- decl_in_api_definition_in_impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %A.type: type = fn_type @A [concrete]
// CHECK:STDOUT: %A: %A.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .A = %A.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @A();
// CHECK:STDOUT:
// CHECK:STDOUT: --- todo_fail_decl_in_api_definition_in_impl.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %A.type: type = fn_type @A [concrete]
// CHECK:STDOUT: %A: %A.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .A = %A.decl.loc5
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_46.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_46.2 = import <none>
// CHECK:STDOUT: %A.decl.loc5: %A.type = fn_decl @A [concrete = constants.%A] {} {}
// CHECK:STDOUT: %A.decl.loc7: %A.type = fn_decl @A [concrete = constants.%A] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @A() [from "decl_in_api_definition_in_impl.carbon"] {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- use_decl_in_api.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- use_decl_in_api.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Main.A = import_ref Main//decl_in_api_definition_in_impl, A, unloaded
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .A = imports.%Main.A
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_31.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_31.2 = import <none>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- decl_only_in_api.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %B.type: type = fn_type @B [concrete]
// CHECK:STDOUT: %B: %B.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .B = %B.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @B();
// CHECK:STDOUT:
// CHECK:STDOUT: --- decl_only_in_api.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Main.B = import_ref Main//decl_only_in_api, B, unloaded
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .B = imports.%Main.B
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_32.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_32.2 = import <none>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- decl_in_api_decl_in_impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C.type: type = fn_type @C [concrete]
// CHECK:STDOUT: %C: %C.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @C();
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_decl_in_api_decl_in_impl.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C.type: type = fn_type @C [concrete]
// CHECK:STDOUT: %C: %C.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_40.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_40.2 = import <none>
// CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @C [from "decl_in_api_decl_in_impl.carbon"];
// CHECK:STDOUT:
// CHECK:STDOUT: --- decl_only_in_impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_decl_only_in_impl.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %D.type: type = fn_type @D [concrete]
// CHECK:STDOUT: %D: %D.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .D = %D.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_33.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_33.2 = import <none>
// CHECK:STDOUT: %D.decl: %D.type = fn_decl @D [concrete = constants.%D] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @D();
// CHECK:STDOUT:
@@ -52,8 +52,8 @@ interface I {}
// CHECK:STDOUT: --- fail_b.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %I.type.8e70aa.2: type = facet_type <@I.loc14_13.2> [concrete]
// CHECK:STDOUT: %Self: %I.type.8e70aa.2 = symbolic_binding Self, 0 [symbolic]
// CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete]
// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
@@ -61,14 +61,12 @@ interface I {}
// CHECK:STDOUT: .I = %I.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import = import <none>
// CHECK:STDOUT: %I.decl: type = interface_decl @I.loc14_13.2 [concrete = constants.%I.type.8e70aa.2] {} {}
// CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @I.loc14_13.1;
// CHECK:STDOUT:
// CHECK:STDOUT: interface @I.loc14_13.2 {
// CHECK:STDOUT: %Self: %I.type.8e70aa.2 = symbolic_binding Self, 0 [symbolic = constants.%Self]
// CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I.loc14_13.2 [concrete]
// CHECK:STDOUT: interface @I [from "a.carbon"] {
// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self]
// CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = %Self
@@ -3,8 +3,6 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon
// TODO: Add ranges and switch to "--dump-sem-ir-ranges=only".
// EXTRA-ARGS: --dump-sem-ir-ranges=if-present
//
// AUTOUPDATE
// TIP: To test this file alone, run:
@@ -76,157 +74,3 @@ impl library "[[@TEST_NAME]]";
// CHECK:STDERR: ^~~~~~~~~~~~
// CHECK:STDERR:
interface D;
// CHECK:STDOUT: --- decl_in_api_definition_in_impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .A = %A.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @A;
// CHECK:STDOUT:
// CHECK:STDOUT: --- todo_fail_decl_in_api_definition_in_impl.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %A.type.3138d6.2: type = facet_type <@A.loc7> [concrete]
// CHECK:STDOUT: %Self: %A.type.3138d6.2 = symbolic_binding Self, 0 [symbolic]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .A = %A.decl.loc5
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_46.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_46.2 = import <none>
// CHECK:STDOUT: %A.decl.loc5: type = interface_decl @A.loc7 [concrete = constants.%A.type.3138d6.2] {} {}
// CHECK:STDOUT: %A.decl.loc7: type = interface_decl @A.loc7 [concrete = constants.%A.type.3138d6.2] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @A.loc5;
// CHECK:STDOUT:
// CHECK:STDOUT: interface @A.loc7 {
// CHECK:STDOUT: %Self: %A.type.3138d6.2 = symbolic_binding Self, 0 [symbolic = constants.%Self]
// CHECK:STDOUT: %A.WithSelf.decl = interface_with_self_decl @A.loc7 [concrete]
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = %Self
// CHECK:STDOUT: witness = ()
// CHECK:STDOUT:
// CHECK:STDOUT: !requires:
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: specific @A.WithSelf(constants.%Self) {}
// CHECK:STDOUT:
// CHECK:STDOUT: --- use_decl_in_api.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- use_decl_in_api.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Main.A = import_ref Main//decl_in_api_definition_in_impl, A, unloaded
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .A = imports.%Main.A
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_31.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_31.2 = import <none>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- decl_only_in_api.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .B = %B.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @B;
// CHECK:STDOUT:
// CHECK:STDOUT: --- decl_only_in_api.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Main.B = import_ref Main//decl_only_in_api, B, unloaded
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .B = imports.%Main.B
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_32.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_32.2 = import <none>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- decl_in_api_decl_in_impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C.type: type = facet_type <@C> [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %C.decl: type = interface_decl @C [concrete = constants.%C.type] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @C;
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_decl_in_api_decl_in_impl.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %C.type.709119.2: type = facet_type <@C.loc8_12.2> [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .C = %C.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_40.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_40.2 = import <none>
// CHECK:STDOUT: %C.decl: type = interface_decl @C.loc8_12.2 [concrete = constants.%C.type.709119.2] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @C.loc8_12.1;
// CHECK:STDOUT:
// CHECK:STDOUT: interface @C.loc8_12.2;
// CHECK:STDOUT:
// CHECK:STDOUT: --- decl_only_in_impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_decl_only_in_impl.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %D.type: type = facet_type <@D> [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .D = %D.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %default.import.loc2_33.1 = import <none>
// CHECK:STDOUT: %default.import.loc2_33.2 = import <none>
// CHECK:STDOUT: %D.decl: type = interface_decl @D [concrete = constants.%D.type] {} {}
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: interface @D;
// CHECK:STDOUT: