|
|
@@ -6,12 +6,14 @@
|
|
|
|
|
|
#include "common/map.h"
|
|
|
#include "toolchain/base/kind_switch.h"
|
|
|
+#include "toolchain/check/diagnostic_helpers.h"
|
|
|
#include "toolchain/check/eval.h"
|
|
|
#include "toolchain/check/generic_region_stack.h"
|
|
|
#include "toolchain/check/inst.h"
|
|
|
#include "toolchain/check/subst.h"
|
|
|
#include "toolchain/check/type.h"
|
|
|
#include "toolchain/check/type_completion.h"
|
|
|
+#include "toolchain/diagnostics/diagnostic.h"
|
|
|
#include "toolchain/sem_ir/constant.h"
|
|
|
#include "toolchain/sem_ir/generic.h"
|
|
|
#include "toolchain/sem_ir/ids.h"
|
|
|
@@ -74,12 +76,13 @@ class RebuildGenericConstantInEvalBlockCallbacks : public SubstInstCallbacks {
|
|
|
RebuildGenericConstantInEvalBlockCallbacks(
|
|
|
Context& context, SemIR::GenericId generic_id,
|
|
|
SemIR::GenericInstIndex::Region region, SemIR::LocId loc_id,
|
|
|
- ConstantsInGenericMap& constants_in_generic)
|
|
|
+ ConstantsInGenericMap& constants_in_generic, bool inside_redeclaration)
|
|
|
: context_(context),
|
|
|
generic_id_(generic_id),
|
|
|
region_(region),
|
|
|
loc_id_(loc_id),
|
|
|
- constants_in_generic_(constants_in_generic) {}
|
|
|
+ constants_in_generic_(constants_in_generic),
|
|
|
+ inside_redeclaration_(inside_redeclaration) {}
|
|
|
|
|
|
auto context() const -> Context& { return context_; }
|
|
|
|
|
|
@@ -149,6 +152,16 @@ class RebuildGenericConstantInEvalBlockCallbacks : public SubstInstCallbacks {
|
|
|
// We might already have an instruction in the eval block if a transitive
|
|
|
// operand of this instruction has the same constant value.
|
|
|
auto result = constants_in_generic_.Insert(const_inst_id, [&] {
|
|
|
+ if (inside_redeclaration_) {
|
|
|
+ // Adding instructions to a redeclaration causes crashes later since it
|
|
|
+ // causes us to produce invalid indices into the original declaration's
|
|
|
+ // set of instructions. So we terminate now and avoid adding a new
|
|
|
+ // instruction and new index. It should not be possible to create this
|
|
|
+ // situation where a generic redeclaration introduces new instructions
|
|
|
+ // to the eval block.
|
|
|
+ CARBON_FATAL("generic redeclaration differs from previous declaration");
|
|
|
+ }
|
|
|
+
|
|
|
// TODO: Add a function on `Context` to add the instruction without
|
|
|
// inserting it into the dependent instructions list or computing a
|
|
|
// constant value for it.
|
|
|
@@ -170,6 +183,7 @@ class RebuildGenericConstantInEvalBlockCallbacks : public SubstInstCallbacks {
|
|
|
SemIR::GenericInstIndex::Region region_;
|
|
|
SemIR::LocId loc_id_;
|
|
|
ConstantsInGenericMap& constants_in_generic_;
|
|
|
+ bool inside_redeclaration_;
|
|
|
};
|
|
|
|
|
|
// Substitution callbacks to rebuild a template action. This rebuilds the action
|
|
|
@@ -180,9 +194,11 @@ class RebuildTemplateActionInEvalBlockCallbacks final
|
|
|
RebuildTemplateActionInEvalBlockCallbacks(
|
|
|
Context& context, SemIR::GenericId generic_id,
|
|
|
SemIR::GenericInstIndex::Region region, SemIR::LocId loc_id,
|
|
|
- ConstantsInGenericMap& constants_in_generic, SemIR::InstId action_inst_id)
|
|
|
- : RebuildGenericConstantInEvalBlockCallbacks(
|
|
|
- context, generic_id, region, loc_id, constants_in_generic),
|
|
|
+ ConstantsInGenericMap& constants_in_generic, bool inside_redeclaration,
|
|
|
+ SemIR::InstId action_inst_id)
|
|
|
+ : RebuildGenericConstantInEvalBlockCallbacks(context, generic_id, region,
|
|
|
+ loc_id, constants_in_generic,
|
|
|
+ inside_redeclaration),
|
|
|
action_inst_id_(action_inst_id) {}
|
|
|
|
|
|
auto Rebuild(SemIR::InstId orig_inst_id, SemIR::Inst new_inst) const
|
|
|
@@ -209,14 +225,15 @@ class RebuildTemplateActionInEvalBlockCallbacks final
|
|
|
static auto AddGenericTypeToEvalBlock(
|
|
|
Context& context, SemIR::GenericId generic_id,
|
|
|
SemIR::GenericInstIndex::Region region, SemIR::LocId loc_id,
|
|
|
- ConstantsInGenericMap& constants_in_generic, SemIR::TypeId type_id)
|
|
|
- -> SemIR::TypeId {
|
|
|
+ ConstantsInGenericMap& constants_in_generic, bool inside_redeclaration,
|
|
|
+ SemIR::TypeId type_id) -> SemIR::TypeId {
|
|
|
// Substitute into the type's constant instruction and rebuild it in the eval
|
|
|
// block.
|
|
|
auto type_inst_id =
|
|
|
SubstInst(context, context.types().GetInstId(type_id),
|
|
|
RebuildGenericConstantInEvalBlockCallbacks(
|
|
|
- context, generic_id, region, loc_id, constants_in_generic));
|
|
|
+ context, generic_id, region, loc_id, constants_in_generic,
|
|
|
+ inside_redeclaration));
|
|
|
return context.types().GetTypeIdForTypeInstId(type_inst_id);
|
|
|
}
|
|
|
|
|
|
@@ -227,14 +244,14 @@ static auto AddGenericTypeToEvalBlock(
|
|
|
static auto AddGenericConstantToEvalBlock(
|
|
|
Context& context, SemIR::GenericId generic_id,
|
|
|
SemIR::GenericInstIndex::Region region,
|
|
|
- ConstantsInGenericMap& constants_in_generic, SemIR::InstId inst_id)
|
|
|
- -> SemIR::ConstantId {
|
|
|
+ ConstantsInGenericMap& constants_in_generic, bool inside_redeclaration,
|
|
|
+ SemIR::InstId inst_id) -> SemIR::ConstantId {
|
|
|
// Substitute into the constant value and rebuild it in the eval block if
|
|
|
// we've not encountered it before.
|
|
|
auto const_inst_id = context.constant_values().GetConstantInstId(inst_id);
|
|
|
auto callbacks = RebuildGenericConstantInEvalBlockCallbacks(
|
|
|
context, generic_id, region, context.insts().GetLocId(inst_id),
|
|
|
- constants_in_generic);
|
|
|
+ constants_in_generic, inside_redeclaration);
|
|
|
auto new_inst_id = SubstInst(context, const_inst_id, callbacks);
|
|
|
if (new_inst_id == const_inst_id) {
|
|
|
// It's possible that no substitutions were necessary in this instruction.
|
|
|
@@ -255,14 +272,14 @@ static auto AddGenericConstantToEvalBlock(
|
|
|
static auto AddTemplateActionToEvalBlock(
|
|
|
Context& context, SemIR::GenericId generic_id,
|
|
|
SemIR::GenericInstIndex::Region region,
|
|
|
- ConstantsInGenericMap& constants_in_generic, SemIR::InstId inst_id)
|
|
|
- -> void {
|
|
|
+ ConstantsInGenericMap& constants_in_generic, bool inside_redeclaration,
|
|
|
+ SemIR::InstId inst_id) -> void {
|
|
|
// Substitute into the constant value and rebuild it in the eval block.
|
|
|
auto new_inst_id = SubstInst(
|
|
|
context, inst_id,
|
|
|
RebuildTemplateActionInEvalBlockCallbacks(
|
|
|
context, generic_id, region, context.insts().GetLocId(inst_id),
|
|
|
- constants_in_generic, inst_id));
|
|
|
+ constants_in_generic, inside_redeclaration, inst_id));
|
|
|
CARBON_CHECK(new_inst_id == inst_id,
|
|
|
"Substitution changed InstId of template action");
|
|
|
constants_in_generic.Insert(inst_id, inst_id);
|
|
|
@@ -298,14 +315,15 @@ static auto PopulateConstantsFromDeclaration(
|
|
|
// Builds and returns a block of instructions whose constant values need to be
|
|
|
// evaluated in order to resolve a generic to a specific.
|
|
|
static auto MakeGenericEvalBlock(Context& context, SemIR::GenericId generic_id,
|
|
|
- SemIR::GenericInstIndex::Region region)
|
|
|
+ SemIR::GenericInstIndex::Region region,
|
|
|
+ bool inside_redeclaration)
|
|
|
-> SemIR::InstBlockId {
|
|
|
context.inst_block_stack().Push();
|
|
|
|
|
|
ConstantsInGenericMap constants_in_generic;
|
|
|
|
|
|
- // For the definition region, populate constants from the declaration.
|
|
|
- if (region == SemIR::GenericInstIndex::Region::Definition) {
|
|
|
+ if (region == SemIR::GenericInstIndex::Region::Definition ||
|
|
|
+ inside_redeclaration) {
|
|
|
PopulateConstantsFromDeclaration(context, generic_id, constants_in_generic);
|
|
|
}
|
|
|
|
|
|
@@ -324,7 +342,11 @@ static auto MakeGenericEvalBlock(Context& context, SemIR::GenericId generic_id,
|
|
|
auto inst = context.insts().Get(inst_id);
|
|
|
auto type_id = AddGenericTypeToEvalBlock(
|
|
|
context, generic_id, region, context.insts().GetLocId(inst_id),
|
|
|
- constants_in_generic, inst.type_id());
|
|
|
+ constants_in_generic, inside_redeclaration, inst.type_id());
|
|
|
+ // If the generic declaration is invalid, it can result in an error.
|
|
|
+ if (type_id == SemIR::ErrorInst::SingletonTypeId) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
// TODO: Eventually, completeness requirements should be modeled as
|
|
|
// constraints on the generic rather than properties of the type. For now,
|
|
|
// require the transformed type to be complete if the original was.
|
|
|
@@ -343,9 +365,9 @@ static auto MakeGenericEvalBlock(Context& context, SemIR::GenericId generic_id,
|
|
|
GenericRegionStack::DependencyKind::None) {
|
|
|
// Update the constant value to refer to this generic.
|
|
|
context.constant_values().Set(
|
|
|
- inst_id,
|
|
|
- AddGenericConstantToEvalBlock(context, generic_id, region,
|
|
|
- constants_in_generic, inst_id));
|
|
|
+ inst_id, AddGenericConstantToEvalBlock(
|
|
|
+ context, generic_id, region, constants_in_generic,
|
|
|
+ inside_redeclaration, inst_id));
|
|
|
}
|
|
|
|
|
|
// If the instruction is a template action, add it directly to this position
|
|
|
@@ -353,7 +375,8 @@ static auto MakeGenericEvalBlock(Context& context, SemIR::GenericId generic_id,
|
|
|
if ((dep_kind & GenericRegionStack::DependencyKind::Template) !=
|
|
|
GenericRegionStack::DependencyKind::None) {
|
|
|
AddTemplateActionToEvalBlock(context, generic_id, region,
|
|
|
- constants_in_generic, inst_id);
|
|
|
+ constants_in_generic, inside_redeclaration,
|
|
|
+ inst_id);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -377,10 +400,15 @@ auto RebuildGenericEvalBlock(Context& context, SemIR::GenericId generic_id,
|
|
|
-> SemIR::InstBlockId {
|
|
|
context.inst_block_stack().Push();
|
|
|
|
|
|
+ // We say we are not inside a redeclaration since this function is used for
|
|
|
+ // import and there's no redeclaration there.
|
|
|
+ bool inside_redeclaration = false;
|
|
|
+
|
|
|
ConstantsInGenericMap constants_in_generic;
|
|
|
|
|
|
// For the definition region, populate constants from the declaration.
|
|
|
- if (region == SemIR::GenericInstIndex::Region::Definition) {
|
|
|
+ if (inside_redeclaration ||
|
|
|
+ region == SemIR::GenericInstIndex::Region::Definition) {
|
|
|
PopulateConstantsFromDeclaration(context, generic_id, constants_in_generic);
|
|
|
}
|
|
|
|
|
|
@@ -388,7 +416,8 @@ auto RebuildGenericEvalBlock(Context& context, SemIR::GenericId generic_id,
|
|
|
for (auto [i, inst_id] : llvm::enumerate(const_ids)) {
|
|
|
// Build a constant in the inst block.
|
|
|
AddGenericConstantToEvalBlock(context, generic_id, region,
|
|
|
- constants_in_generic, inst_id);
|
|
|
+ constants_in_generic, inside_redeclaration,
|
|
|
+ inst_id);
|
|
|
CARBON_CHECK(
|
|
|
context.inst_block_stack().PeekCurrentBlockContents().size() == i + 1,
|
|
|
"Produced {0} instructions when importing {1}",
|
|
|
@@ -443,7 +472,8 @@ auto FinishGenericDecl(Context& context, SemIRLoc loc,
|
|
|
return;
|
|
|
}
|
|
|
auto decl_block_id = MakeGenericEvalBlock(
|
|
|
- context, generic_id, SemIR::GenericInstIndex::Region::Declaration);
|
|
|
+ context, generic_id, SemIR::GenericInstIndex::Region::Declaration,
|
|
|
+ /*inside_redeclaration=*/false);
|
|
|
context.generic_region_stack().Pop();
|
|
|
context.generics().Get(generic_id).decl_block_id = decl_block_id;
|
|
|
|
|
|
@@ -460,10 +490,18 @@ auto BuildGenericDecl(Context& context, SemIR::InstId decl_id)
|
|
|
return generic_id;
|
|
|
}
|
|
|
|
|
|
-auto FinishGenericRedecl(Context& context, SemIR::InstId /*decl_id*/,
|
|
|
- SemIR::GenericId /*generic_id*/) -> void {
|
|
|
- // TODO: Compare contents of this declaration with the existing one on the
|
|
|
- // generic.
|
|
|
+auto FinishGenericRedecl(Context& context, SemIR::GenericId generic_id)
|
|
|
+ -> void {
|
|
|
+ if (!generic_id.has_value()) {
|
|
|
+ context.generic_region_stack().Pop();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto definition_block_id = MakeGenericEvalBlock(
|
|
|
+ context, generic_id, SemIR::GenericInstIndex::Region::Declaration,
|
|
|
+ /*inside_redeclaration=*/true);
|
|
|
+ CARBON_CHECK(definition_block_id == SemIR::InstBlockId::Empty);
|
|
|
+
|
|
|
context.generic_region_stack().Pop();
|
|
|
}
|
|
|
|
|
|
@@ -479,7 +517,8 @@ auto FinishGenericDefinition(Context& context, SemIR::GenericId generic_id)
|
|
|
}
|
|
|
|
|
|
auto definition_block_id = MakeGenericEvalBlock(
|
|
|
- context, generic_id, SemIR::GenericInstIndex::Region::Definition);
|
|
|
+ context, generic_id, SemIR::GenericInstIndex::Region::Definition,
|
|
|
+ /*inside_redeclaration=*/false);
|
|
|
context.generics().Get(generic_id).definition_block_id = definition_block_id;
|
|
|
|
|
|
context.generic_region_stack().Pop();
|