Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ImplWitness #4679

Draft
wants to merge 39 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
af4dd98
Checkpoint progress.
josh11b Dec 11, 2024
cb6b124
Merge remote-tracking branch 'upstream/trunk' into witness
josh11b Dec 11, 2024
21382af
Checkpoint progress.
josh11b Dec 12, 2024
bf409c7
Checkpoint progress.
josh11b Dec 12, 2024
e5b8232
Checkpoint progress.
josh11b Dec 13, 2024
5d11fd9
Back to having a specific_id in the ImplWitness instruction
josh11b Dec 13, 2024
dee9ba4
Merge remote-tracking branch 'upstream/trunk' into witness
josh11b Dec 13, 2024
086ea22
Take a stab at eval
josh11b Dec 13, 2024
97eca32
Add error to eval
josh11b Dec 13, 2024
e67afce
Instruction in CHECK
josh11b Dec 13, 2024
56b7202
Dependent instruction in CHECK
josh11b Dec 13, 2024
b7c60d7
Autoupdate what doesn't crash
josh11b Dec 13, 2024
297cb82
More autoupdate
josh11b Dec 13, 2024
de8bc24
Seem to have made things worse
josh11b Dec 14, 2024
fabcbde
Identify broken check tests, update non-broken
josh11b Dec 14, 2024
3cde8ea
UNDO debug prints
josh11b Dec 14, 2024
79b5558
Merge remote-tracking branch 'upstream/trunk' into witness
josh11b Dec 14, 2024
e5d552b
Checkpoint progress.
josh11b Dec 17, 2024
a402c97
Some FIXMEs
josh11b Dec 17, 2024
ad495c6
Merge remote-tracking branch 'upstream/trunk' into witness
josh11b Dec 17, 2024
28807fc
Checkpoint progress.
josh11b Dec 17, 2024
5bc875f
Checkpoint progress.
josh11b Dec 17, 2024
20a843f
Test updates
josh11b Dec 17, 2024
8c2f6be
Merge remote-tracking branch 'upstream/trunk' into witness
josh11b Dec 17, 2024
4961945
Fix in eval
josh11b Dec 17, 2024
fc280cd
Fix importing
josh11b Dec 17, 2024
6f8099d
autoupdate
josh11b Dec 17, 2024
6fa1e74
Improve diagnostics
josh11b Dec 17, 2024
bf6f4ab
Checkpoint progress.
josh11b Dec 18, 2024
1cbd7a0
Better missing impl definition diagnostic
josh11b Dec 18, 2024
0d98714
Fewer diagnostics on cross import redeclaration
josh11b Dec 18, 2024
b1c9359
Relax lowering check
josh11b Dec 18, 2024
f8419c5
Checkpoint progress.
josh11b Dec 18, 2024
1701022
Checkpoint progress.
josh11b Dec 18, 2024
51f9826
Middle of debugging
josh11b Dec 19, 2024
085c7b2
Checkpoint progress.
josh11b Dec 20, 2024
9dbb3c9
Merge remote-tracking branch 'upstream/trunk' into witness
josh11b Dec 20, 2024
9890606
Workaround for bug
josh11b Dec 21, 2024
141f337
Checkpoint progress.
josh11b Dec 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 2 additions & 0 deletions toolchain/check/check_unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "toolchain/base/pretty_stack_trace_function.h"
#include "toolchain/check/generic.h"
#include "toolchain/check/handle.h"
#include "toolchain/check/impl.h"
#include "toolchain/check/import.h"
#include "toolchain/check/import_ref.h"
#include "toolchain/check/node_id_traversal.h"
Expand Down Expand Up @@ -397,6 +398,7 @@ auto CheckUnit::CheckRequiredDefinitions() -> void {
case CARBON_KIND(SemIR::ImplDecl impl_decl): {
auto& impl = context_.impls().Get(impl_decl.impl_id);
if (!impl.is_defined()) {
FillImplWitnessWithErrors(context_, impl);
CARBON_DIAGNOSTIC(MissingImplDefinition, Error,
"impl declared but not defined");
emitter_.Emit(decl_inst_id, MissingImplDefinition);
Expand Down
2 changes: 1 addition & 1 deletion toolchain/check/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,7 @@ class TypeCompleter {

template <typename InstT>
requires(InstT::Kind.template IsAnyOf<SemIR::BindSymbolicName,
SemIR::InterfaceWitnessAccess>())
SemIR::ImplWitnessAccess>())
auto BuildValueReprForInst(SemIR::TypeId type_id, InstT /*inst*/) const
-> SemIR::ValueRepr {
// For symbolic types, we arbitrarily pick a copy representation.
Expand Down
48 changes: 45 additions & 3 deletions toolchain/check/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "toolchain/base/kind_switch.h"
#include "toolchain/check/diagnostic_helpers.h"
#include "toolchain/check/generic.h"
#include "toolchain/check/import_ref.h"
#include "toolchain/diagnostics/diagnostic_emitter.h"
#include "toolchain/diagnostics/format_providers.h"
#include "toolchain/sem_ir/builtin_function_kind.h"
Expand Down Expand Up @@ -1431,9 +1432,13 @@ static auto TryEvalInstInContext(EvalContext& eval_context,
return RebuildIfFieldsAreConstant(
eval_context, inst,
&SemIR::GenericInterfaceType::enclosing_specific_id);
case SemIR::InterfaceWitness::Kind:
case SemIR::ImplWitness::Kind:
// We intentionally don't replace the `elements_id` field here. We want to
// track that specific InstBlock in particular, not coalesce blocks with
// the same members. That block may get updated, and we want to pick up
// those changes.
return RebuildIfFieldsAreConstant(eval_context, inst,
&SemIR::InterfaceWitness::elements_id);
&SemIR::ImplWitness::specific_id);
case CARBON_KIND(SemIR::IntType int_type): {
return RebuildAndValidateIfFieldsAreConstant(
eval_context, inst,
Expand Down Expand Up @@ -1608,11 +1613,48 @@ static auto TryEvalInstInContext(EvalContext& eval_context,

// The elements of a constant aggregate can be accessed.
case SemIR::ClassElementAccess::Kind:
case SemIR::InterfaceWitnessAccess::Kind:
case SemIR::StructAccess::Kind:
case SemIR::TupleAccess::Kind:
return PerformAggregateAccess(eval_context, inst);

case CARBON_KIND(SemIR::ImplWitnessAccess access_inst): {
// This is PerformAggregateAccess followed by GetConstantInSpecific.
Phase phase = Phase::Template;
if (ReplaceFieldWithConstantValue(eval_context, &access_inst,
&SemIR::ImplWitnessAccess::witness_id,
&phase)) {
if (auto witness = eval_context.insts().TryGetAs<SemIR::ImplWitness>(
access_inst.witness_id)) {
auto elements = eval_context.inst_blocks().Get(witness->elements_id);
auto index = static_cast<size_t>(access_inst.index.index);
CARBON_CHECK(index < elements.size(), "Access out of bounds.");
// `Phase` is not used here. If this element is a template constant,
// then so is the result of indexing, even if the aggregate also
// contains a symbolic context.

auto element = elements[index];
if (!element.is_valid()) {
// TODO: Perhaps this should be a `{}` value with incomplete type?
CARBON_DIAGNOSTIC(ImplAccessMemberBeforeComplete, Error,
"accessing member from impl before the end of "
"its definition");
// TODO: Add note pointing to the impl declaration.
eval_context.emitter().Emit(eval_context.GetDiagnosticLoc(inst_id),
ImplAccessMemberBeforeComplete);
return SemIR::ErrorInst::SingletonConstantId;
}
LoadImportRef(eval_context.context(), element);
return GetConstantValueInSpecific(eval_context.sem_ir(),
witness->specific_id, element);
} else {
CARBON_CHECK(phase != Phase::Template,
"Failed to evaluate template constant {0} arg0: {1}",
inst, eval_context.insts().Get(access_inst.witness_id));
}
return MakeConstantResult(eval_context.context(), access_inst, phase);
}
return MakeNonConstantResult(phase);
}
case CARBON_KIND(SemIR::ArrayIndex index): {
return PerformArrayIndex(eval_context, index);
}
Expand Down
71 changes: 57 additions & 14 deletions toolchain/check/generic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@

namespace Carbon::Check {

static auto MakeSelfSpecificId(Context& context, SemIR::GenericId generic_id)
-> SemIR::SpecificId;
static auto ResolveSpecificDeclaration(Context& context, SemIRLoc loc,
SemIR::SpecificId specific_id) -> void;

auto StartGenericDecl(Context& context) -> void {
context.generic_region_stack().Push();
}
Expand Down Expand Up @@ -315,15 +320,19 @@ auto DiscardGenericDecl(Context& context) -> void {
context.generic_region_stack().Pop();
}

auto FinishGenericDecl(Context& context, SemIR::InstId decl_id)
-> SemIR::GenericId {
auto BuildGeneric(Context& context, SemIR::InstId decl_id) -> SemIR::GenericId {
auto all_bindings =
context.scope_stack().compile_time_bindings_stack().PeekAllValues();

if (all_bindings.empty()) {
CARBON_CHECK(context.generic_region_stack().PeekDependentInsts().empty(),
"Have dependent instructions but no compile time bindings are "
"in scope.");
"Have dependent instruction {0} in declaration {1} but no "
"compile time bindings are in scope.",
context.insts().Get(context.generic_region_stack()
.PeekDependentInsts()
.front()
.inst_id),
context.insts().Get(decl_id));
context.generic_region_stack().Pop();
return SemIR::GenericId::Invalid;
}
Expand All @@ -333,18 +342,38 @@ auto FinishGenericDecl(Context& context, SemIR::InstId decl_id)
// collection can have items added to it by import resolution while we are
// building this generic.
auto bindings_id = context.inst_blocks().Add(all_bindings);
auto generic_id = context.generics().Add(

SemIR::GenericId generic_id = context.generics().Add(
SemIR::Generic{.decl_id = decl_id,
.bindings_id = bindings_id,
.self_specific_id = SemIR::SpecificId::Invalid});
// MakeSelfSpecificId could cause something to be imported, which would
// invalidate the return value of `context.generics().Get(generic_id)`.
auto self_specific_id = MakeSelfSpecificId(context, generic_id);
context.generics().Get(generic_id).self_specific_id = self_specific_id;
return generic_id;
}

auto FinishGenericDecl(Context& context, SemIRLoc loc,
SemIR::GenericId generic_id) -> void {
if (!generic_id.is_valid()) {
return;
}
auto decl_block_id = MakeGenericEvalBlock(
context, generic_id, SemIR::GenericInstIndex::Region::Declaration);
context.generic_region_stack().Pop();
context.generics().Get(generic_id).decl_block_id = decl_block_id;

auto self_specific_id = MakeSelfSpecific(context, decl_id, generic_id);
context.generics().Get(generic_id).self_specific_id = self_specific_id;
ResolveSpecificDeclaration(context, loc,
context.generics().GetSelfSpecific(generic_id));
}

auto BuildGenericDecl(Context& context, SemIR::InstId decl_id)
-> SemIR::GenericId {
SemIR::GenericId generic_id = BuildGeneric(context, decl_id);
if (generic_id.is_valid()) {
FinishGenericDecl(context, decl_id, generic_id);
}
return generic_id;
}

Expand Down Expand Up @@ -373,26 +402,34 @@ auto FinishGenericDefinition(Context& context, SemIR::GenericId generic_id)
context.generic_region_stack().Pop();
}

auto MakeSpecific(Context& context, SemIRLoc loc, SemIR::GenericId generic_id,
SemIR::InstBlockId args_id) -> SemIR::SpecificId {
auto specific_id = context.specifics().GetOrAdd(generic_id, args_id);

static auto ResolveSpecificDeclaration(Context& context, SemIRLoc loc,
SemIR::SpecificId specific_id) -> void {
// If this is the first time we've formed this specific, evaluate its decl
// block to form information about the specific.
if (!context.specifics().Get(specific_id).decl_block_id.is_valid()) {
// Put a placeholder value in the decl block so we won't attempt to
// recursively resolve the same specific.
context.specifics().Get(specific_id).decl_block_id =
SemIR::InstBlockId::Empty;

auto decl_block_id =
TryEvalBlockForSpecific(context, loc, specific_id,
SemIR::GenericInstIndex::Region::Declaration);
// Note that TryEvalBlockForSpecific may reallocate the list of specifics,
// so re-lookup the specific here.
context.specifics().Get(specific_id).decl_block_id = decl_block_id;
}
}

auto MakeSpecific(Context& context, SemIRLoc loc, SemIR::GenericId generic_id,
SemIR::InstBlockId args_id) -> SemIR::SpecificId {
auto specific_id = context.specifics().GetOrAdd(generic_id, args_id);
ResolveSpecificDeclaration(context, loc, specific_id);
return specific_id;
}

auto MakeSelfSpecific(Context& context, SemIRLoc loc,
SemIR::GenericId generic_id) -> SemIR::SpecificId {
static auto MakeSelfSpecificId(Context& context, SemIR::GenericId generic_id)
-> SemIR::SpecificId {
if (!generic_id.is_valid()) {
return SemIR::SpecificId::Invalid;
}
Expand All @@ -407,12 +444,18 @@ auto MakeSelfSpecific(Context& context, SemIRLoc loc,
arg_ids.push_back(context.constant_values().GetConstantInstId(arg_id));
}
auto args_id = context.inst_blocks().AddCanonical(arg_ids);
return context.specifics().GetOrAdd(generic_id, args_id);
}

auto MakeSelfSpecific(Context& context, SemIRLoc loc,
SemIR::GenericId generic_id) -> SemIR::SpecificId {
// Build a corresponding specific.
SemIR::SpecificId specific_id = MakeSelfSpecificId(context, generic_id);
// TODO: This could be made more efficient. We don't need to perform
// substitution here; we know we want identity mappings for all constants and
// types. We could also consider not storing the mapping at all in this case.
return MakeSpecific(context, loc, generic_id, args_id);
ResolveSpecificDeclaration(context, loc, specific_id);
return specific_id;
}

auto ResolveSpecificDefinition(Context& context, SemIRLoc loc,
Expand Down
10 changes: 9 additions & 1 deletion toolchain/check/generic.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ auto DiscardGenericDecl(Context& context) -> void;
// Finish processing a potentially generic declaration and produce a
// corresponding generic object. Returns SemIR::GenericId::Invalid if this
// declaration is not actually generic.
auto FinishGenericDecl(Context& context, SemIR::InstId decl_id)
auto BuildGeneric(Context& context, SemIR::InstId decl_id) -> SemIR::GenericId;

// Builds eval block for the declaration.
auto FinishGenericDecl(Context& context, SemIRLoc loc,
SemIR::GenericId generic_id) -> void;

// BuildGeneric() and FinishGenericDecl() combined. Normally you would call this
// function unless the caller has work to do between the two steps.
auto BuildGenericDecl(Context& context, SemIR::InstId decl_id)
-> SemIR::GenericId;

// Merge a redeclaration of an entity that might be a generic into the original
Expand Down
2 changes: 1 addition & 1 deletion toolchain/check/handle_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
// TODO: If this is an invalid redeclaration of a non-class entity or there
// was an error in the qualifier, we will have lost track of the class name
// here. We should keep track of it even if the name is invalid.
class_info.generic_id = FinishGenericDecl(context, class_decl_id);
class_info.generic_id = BuildGenericDecl(context, class_decl_id);
class_decl.class_id = context.classes().Add(class_info);
if (class_info.has_parameters()) {
class_decl.type_id = context.GetGenericClassType(
Expand Down
2 changes: 1 addition & 1 deletion toolchain/check/handle_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ static auto BuildFunctionDecl(Context& context,
if (function_info.is_extern && context.IsImplFile()) {
DiagnoseExternRequiresDeclInApiFile(context, node_id);
}
function_info.generic_id = FinishGenericDecl(context, decl_id);
function_info.generic_id = BuildGenericDecl(context, decl_id);
function_decl.function_id = context.functions().Add(function_info);
} else {
FinishGenericRedecl(context, decl_id, function_info.generic_id);
Expand Down
10 changes: 6 additions & 4 deletions toolchain/check/handle_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,6 @@ static auto MergeImplRedecl(Context& context, SemIR::Impl& new_impl,
SemIR::ImplId prev_impl_id) -> bool {
auto& prev_impl = context.impls().Get(prev_impl_id);

// TODO: Following #3763, disallow redeclarations in different scopes.

// If the parameters aren't the same, then this is not a redeclaration of this
// `impl`. Keep looking for a prior declaration without issuing a diagnostic.
if (!CheckRedeclParamsMatch(context, DeclParams(new_impl),
Expand Down Expand Up @@ -340,8 +338,9 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id,

// Create a new impl if this isn't a valid redeclaration.
if (!impl_decl.impl_id.is_valid()) {
impl_info.generic_id = FinishGenericDecl(context, impl_decl_id);
impl_info.generic_id = BuildGeneric(context, impl_decl_id);
impl_info.witness_id = ImplWitnessForDeclaration(context, impl_info);
FinishGenericDecl(context, impl_decl_id, impl_info.generic_id);
impl_decl.impl_id = context.impls().Add(impl_info);
lookup_bucket_ref.push_back(impl_decl.impl_id);
} else {
Expand Down Expand Up @@ -412,6 +411,9 @@ auto HandleParseNode(Context& context, Parse::ImplDefinitionStartId node_id)
context.generics().GetSelfSpecific(impl_info.generic_id));
StartGenericDefinition(context);

if (!impl_info.is_defined()) {
ImplWitnessStartDefinition(context, impl_info);
}
context.inst_block_stack().Push();
context.node_stack().Push(node_id, impl_id);

Expand All @@ -435,7 +437,7 @@ auto HandleParseNode(Context& context, Parse::ImplDefinitionId /*node_id*/)

auto& impl_info = context.impls().Get(impl_id);
if (!impl_info.is_defined()) {
impl_info.witness_id = BuildImplWitness(context, impl_info);
FinishImplWitness(context, impl_info);
impl_info.defined = true;
}

Expand Down
2 changes: 1 addition & 1 deletion toolchain/check/handle_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ static auto BuildInterfaceDecl(Context& context,
// there was an error in the qualifier, we will have lost track of the
// interface name here. We should keep track of it even if the name is
// invalid.
interface_info.generic_id = FinishGenericDecl(context, interface_decl_id);
interface_info.generic_id = BuildGenericDecl(context, interface_decl_id);
interface_decl.interface_id = context.interfaces().Add(interface_info);
if (interface_info.has_parameters()) {
interface_decl.type_id = context.GetGenericInterfaceType(
Expand Down
Loading
Loading