|
|
@@ -7,6 +7,7 @@
|
|
|
#include <compare>
|
|
|
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
|
+#include "llvm/ADT/STLExtras.h"
|
|
|
#include "toolchain/check/convert.h"
|
|
|
#include "toolchain/check/diagnostic_helpers.h"
|
|
|
#include "toolchain/check/generic.h"
|
|
|
@@ -256,6 +257,47 @@ auto AllocateFacetTypeImplWitness(Context& context,
|
|
|
context.inst_blocks().ReplacePlaceholder(witness_id, empty_table);
|
|
|
}
|
|
|
|
|
|
+namespace {
|
|
|
+struct FacetTypeConstraintValue {
|
|
|
+ SemIR::EntityNameId entity_name_id;
|
|
|
+ SemIR::ElementIndex access_index;
|
|
|
+ SemIR::SpecificInterfaceId specific_interface_id;
|
|
|
+
|
|
|
+ friend auto operator<=>(const FacetTypeConstraintValue& lhs,
|
|
|
+ const FacetTypeConstraintValue& rhs)
|
|
|
+ -> std::weak_ordering {
|
|
|
+ if (lhs.entity_name_id != rhs.entity_name_id) {
|
|
|
+ return lhs.entity_name_id.index <=> rhs.entity_name_id.index;
|
|
|
+ }
|
|
|
+ if (lhs.access_index != rhs.access_index) {
|
|
|
+ return lhs.access_index.index <=> rhs.access_index.index;
|
|
|
+ }
|
|
|
+ return lhs.specific_interface_id.index <=> rhs.specific_interface_id.index;
|
|
|
+ }
|
|
|
+
|
|
|
+ friend auto operator==(const FacetTypeConstraintValue& lhs,
|
|
|
+ const FacetTypeConstraintValue& rhs) -> bool = default;
|
|
|
+};
|
|
|
+} // namespace
|
|
|
+
|
|
|
+static auto GetFacetTypeConstraintValue(Context& context,
|
|
|
+ SemIR::ImplWitnessAccess access)
|
|
|
+ -> std::optional<FacetTypeConstraintValue> {
|
|
|
+ auto lookup =
|
|
|
+ context.insts().TryGetAs<SemIR::LookupImplWitness>(access.witness_id);
|
|
|
+ if (lookup) {
|
|
|
+ auto self = context.insts().TryGetAs<SemIR::BindSymbolicName>(
|
|
|
+ context.constant_values().GetConstantInstId(
|
|
|
+ lookup->query_self_inst_id));
|
|
|
+ if (self) {
|
|
|
+ return {{.entity_name_id = self->entity_name_id,
|
|
|
+ .access_index = access.index,
|
|
|
+ .specific_interface_id = lookup->query_specific_interface_id}};
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return std::nullopt;
|
|
|
+}
|
|
|
+
|
|
|
// Returns an ordering between two values in a rewrite constraint. Two
|
|
|
// `ImplWitnessAccess` instructions that refer to the same associated constant
|
|
|
// through the same facet value are treated as equivalent. Otherwise, the
|
|
|
@@ -272,28 +314,10 @@ static auto CompareFacetTypeConstraintValues(Context& context,
|
|
|
auto lhs_access = context.insts().TryGetAs<SemIR::ImplWitnessAccess>(lhs_id);
|
|
|
auto rhs_access = context.insts().TryGetAs<SemIR::ImplWitnessAccess>(rhs_id);
|
|
|
if (lhs_access && rhs_access) {
|
|
|
- auto lhs_lookup = context.insts().TryGetAs<SemIR::LookupImplWitness>(
|
|
|
- lhs_access->witness_id);
|
|
|
- auto rhs_lookup = context.insts().TryGetAs<SemIR::LookupImplWitness>(
|
|
|
- rhs_access->witness_id);
|
|
|
- if (lhs_lookup && rhs_lookup) {
|
|
|
- auto lhs_self = context.insts().TryGetAs<SemIR::BindSymbolicName>(
|
|
|
- context.constant_values().GetConstantInstId(
|
|
|
- lhs_lookup->query_self_inst_id));
|
|
|
- auto rhs_self = context.insts().TryGetAs<SemIR::BindSymbolicName>(
|
|
|
- context.constant_values().GetConstantInstId(
|
|
|
- rhs_lookup->query_self_inst_id));
|
|
|
- if (lhs_self && rhs_self) {
|
|
|
- if (lhs_self->entity_name_id != rhs_self->entity_name_id) {
|
|
|
- return lhs_self->entity_name_id.index <=>
|
|
|
- rhs_self->entity_name_id.index;
|
|
|
- }
|
|
|
- if (lhs_access->index != rhs_access->index) {
|
|
|
- return lhs_access->index <=> rhs_access->index;
|
|
|
- }
|
|
|
- return lhs_lookup->query_specific_interface_id.index <=>
|
|
|
- rhs_lookup->query_specific_interface_id.index;
|
|
|
- }
|
|
|
+ auto lhs_access_value = GetFacetTypeConstraintValue(context, *lhs_access);
|
|
|
+ auto rhs_access_value = GetFacetTypeConstraintValue(context, *rhs_access);
|
|
|
+ if (lhs_access_value && rhs_access_value) {
|
|
|
+ return *lhs_access_value <=> *rhs_access_value;
|
|
|
}
|
|
|
|
|
|
// We do *not* want to get the evaluated result of `ImplWitnessAccess` here,
|
|
|
@@ -342,6 +366,105 @@ static auto SortAndDedupeRewriteConstraints(
|
|
|
rewrites.erase(llvm::unique(rewrites, eq), rewrites.end());
|
|
|
}
|
|
|
|
|
|
+// A mapping of each associated constant (represented as `ImplWitnessAccess`) to
|
|
|
+// its value (represented as an `InstId`). Used to track rewrite constraints,
|
|
|
+// with the LHS mapping to the resolved value of the RHS.
|
|
|
+class AccessRewriteValues {
|
|
|
+ public:
|
|
|
+ enum State {
|
|
|
+ NotRewritten,
|
|
|
+ BeingRewritten,
|
|
|
+ FullyRewritten,
|
|
|
+ };
|
|
|
+ struct Value {
|
|
|
+ State state;
|
|
|
+ SemIR::InstId inst_id;
|
|
|
+ };
|
|
|
+
|
|
|
+ auto InsertNotRewritten(Context& context, SemIR::ImplWitnessAccess access,
|
|
|
+ SemIR::InstId inst_id) -> void {
|
|
|
+ map_.insert({GetKey(context, access), {NotRewritten, inst_id}});
|
|
|
+ }
|
|
|
+
|
|
|
+ // Finds and returns a pointer into the cache for a given ImplWitnessAccess.
|
|
|
+ // The pointer will be invalidated by mutating the cache. Returns `nullptr`
|
|
|
+ // if `access` is not found.
|
|
|
+ auto FindRef(Context& context, SemIR::ImplWitnessAccess access) -> Value* {
|
|
|
+ auto it = map_.find(GetKey(context, access));
|
|
|
+ if (it == map_.end()) {
|
|
|
+ return nullptr;
|
|
|
+ }
|
|
|
+ return &it->second;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto SetBeingRewritten(Value& value) -> void {
|
|
|
+ if (value.state == NotRewritten) {
|
|
|
+ value.state = BeingRewritten;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ auto SetFullyRewritten(Value& value, SemIR::InstId rewritten_to_inst_id)
|
|
|
+ -> void {
|
|
|
+ // TODO: If state == FullyRewrtten and the inst id is different (according
|
|
|
+ // to `CompareFacetTypeConstraintValues`), we can diagnose writing two
|
|
|
+ // different values for the same associated constant immediately?
|
|
|
+ //
|
|
|
+ // TODO: Once the above is done, we don't need to do the SortAndDedupe
|
|
|
+ // step in ResolveFacetTypeRewriteConstraints()? We can just convert this
|
|
|
+ // `map_` into a new set of `RewriteConstraint`s which will already be
|
|
|
+ // deduped.
|
|
|
+ if (value.state == BeingRewritten) {
|
|
|
+ value = {FullyRewritten, rewritten_to_inst_id};
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private:
|
|
|
+ using Key = FacetTypeConstraintValue;
|
|
|
+ struct KeyInfo {
|
|
|
+ static auto getEmptyKey() -> Key {
|
|
|
+ return {
|
|
|
+ .entity_name_id = SemIR::EntityNameId::None,
|
|
|
+ .access_index = SemIR::ElementIndex(-1),
|
|
|
+ .specific_interface_id = SemIR::SpecificInterfaceId::None,
|
|
|
+ };
|
|
|
+ }
|
|
|
+ static auto getTombstoneKey() -> Key {
|
|
|
+ return {
|
|
|
+ .entity_name_id = SemIR::EntityNameId::None,
|
|
|
+ .access_index = SemIR::ElementIndex(-2),
|
|
|
+ .specific_interface_id = SemIR::SpecificInterfaceId::None,
|
|
|
+ };
|
|
|
+ }
|
|
|
+ static auto getHashValue(Key key) -> unsigned {
|
|
|
+ // This hash produces the same value if two ImplWitnessAccess are
|
|
|
+ // pointing to the same associated constant, even if they are different
|
|
|
+ // instruction ids.
|
|
|
+ //
|
|
|
+ // TODO: This truncates the high bits of the hash code which does not
|
|
|
+ // make for a good hash function.
|
|
|
+ return static_cast<unsigned>(static_cast<uint64_t>(HashValue(key)));
|
|
|
+ }
|
|
|
+ static auto isEqual(Key lhs, Key rhs) -> bool {
|
|
|
+ // This comparison is true if the two ImplWitnessAccess are pointing to
|
|
|
+ // the same associated constant, even if they are different instruction
|
|
|
+ // ids.
|
|
|
+ return lhs == rhs;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ auto GetKey(Context& context, SemIR::ImplWitnessAccess access) -> Key {
|
|
|
+ return {*GetFacetTypeConstraintValue(context, access)};
|
|
|
+ }
|
|
|
+
|
|
|
+ // Try avoid heap allocations in the common case where there are a small
|
|
|
+ // number of rewrite rules referring to each other by keeping up to 16 on
|
|
|
+ // the stack.
|
|
|
+ //
|
|
|
+ // TODO: Revisit if 16 is an appropriate number when we can measure how deep
|
|
|
+ // rewrite constraint chains go in practice.
|
|
|
+ llvm::SmallDenseMap<Key, Value, 16, KeyInfo> map_;
|
|
|
+};
|
|
|
+
|
|
|
// To be used for substituting into the RHS of a rewrite constraint.
|
|
|
//
|
|
|
// It will substitute any `ImplWitnessAccess` into `.Self` (a reference to an
|
|
|
@@ -359,119 +482,189 @@ static auto SortAndDedupeRewriteConstraints(
|
|
|
//
|
|
|
// This additionally diagnoses cycles when the `ImplWitnessAccess` is reading
|
|
|
// from the same rewrite constraint, and is thus assigning to the associated
|
|
|
-// constant a value that refers to the same associated constant, such as with
|
|
|
-// `Z where .X = C(.X)`. In the event of a cycle, the `ImplWitnessAccess` is
|
|
|
+// constant a value that refers to the same associated constant, such as with `Z
|
|
|
+// where .X = C(.X)`. In the event of a cycle, the `ImplWitnessAccess` is
|
|
|
// replaced with `ErrorInst` so that further evaluation of the
|
|
|
// `ImplWitnessAccess` will not loop infinitely.
|
|
|
+//
|
|
|
+// The `rewrite_values` given to the constructor must be set up initially with
|
|
|
+// each rewrite rule of an associated constant inserted with its unresolved
|
|
|
+// value via `InsertNotRewritten`. Then for each rewrite rule of an associated
|
|
|
+// constant, the LHS access should be set as being rewritten with its state
|
|
|
+// changed to `BeingRewritten` in order to detect cycles before performing
|
|
|
+// SubstInst. The result of SubstInst should be preserved afterward by changing
|
|
|
+// the state and value for the LHS to `FullyRewritten` and the subst output
|
|
|
+// instruction, respectively, to avoid duplicating work.
|
|
|
class SubstImplWitnessAccessCallbacks : public SubstInstCallbacks {
|
|
|
public:
|
|
|
- // The `rewrites` is the set of rewrite constraints that are being
|
|
|
- // substituted, and where it looks for rewritten values to substitute from.
|
|
|
- //
|
|
|
- // The `substituting_constraint` is the rewrite constraint for which the RHS
|
|
|
- // is being substituted with the value from another rewrite constraint, if
|
|
|
- // possible. That is, `.Y = .X` in the example in the class docs.
|
|
|
- explicit SubstImplWitnessAccessCallbacks(
|
|
|
- Context* context, SemIR::LocId loc_id,
|
|
|
- llvm::ArrayRef<SemIR::FacetTypeInfo::RewriteConstraint> rewrites,
|
|
|
- const SemIR::FacetTypeInfo::RewriteConstraint* substituting_constraint)
|
|
|
+ explicit SubstImplWitnessAccessCallbacks(Context* context,
|
|
|
+ SemIR::LocId loc_id,
|
|
|
+ AccessRewriteValues* rewrite_values)
|
|
|
: SubstInstCallbacks(context),
|
|
|
loc_id_(loc_id),
|
|
|
- rewrites_(rewrites),
|
|
|
- substituting_constraint_(substituting_constraint) {}
|
|
|
+ rewrite_values_(rewrite_values) {}
|
|
|
+
|
|
|
+ auto Subst(SemIR::InstId& rhs_inst_id) -> SubstResult override {
|
|
|
+ auto rhs_access =
|
|
|
+ context().insts().TryGetAs<SemIR::ImplWitnessAccess>(rhs_inst_id);
|
|
|
+ if (!rhs_access) {
|
|
|
+ // We only want to substitute ImplWitnessAccesses written directly on the
|
|
|
+ // RHS of the rewrite constraint, not when they are nested inside facet
|
|
|
+ // types that are part of the RHS, like `.X = C as (I where .Y = {})`.
|
|
|
+ if (context().insts().Is<SemIR::FacetType>(rhs_inst_id)) {
|
|
|
+ return SubstResult::FullySubstituted;
|
|
|
+ }
|
|
|
+ if (context().constant_values().Get(rhs_inst_id).is_concrete()) {
|
|
|
+ // There's no ImplWitnessAccess that we care about inside this
|
|
|
+ // instruction.
|
|
|
+ return SubstResult::FullySubstituted;
|
|
|
+ } else {
|
|
|
+ // SubstOperands will result in a Rebuild or ReuseUnchanged callback, so
|
|
|
+ // push the non-ImplWitnessAccess to get proper bracketing, allowing us
|
|
|
+ // to pop it in the paired callback.
|
|
|
+ substs_in_progress_.push_back(rhs_inst_id);
|
|
|
+ return SubstResult::SubstOperands;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- auto Subst(SemIR::InstId& rhs_inst_id) const -> bool override {
|
|
|
- if (!context().insts().Is<SemIR::ImplWitnessAccess>(rhs_inst_id)) {
|
|
|
- return context().constant_values().Get(rhs_inst_id).is_concrete();
|
|
|
+ auto* rewrite_value = rewrite_values_->FindRef(context(), *rhs_access);
|
|
|
+ if (!rewrite_value) {
|
|
|
+ // The RHS refers to an associated constant for which there is no rewrite
|
|
|
+ // rule.
|
|
|
+ return SubstResult::FullySubstituted;
|
|
|
}
|
|
|
|
|
|
- // TODO: We could consider something better than linear search here, such as
|
|
|
- // a map. However that would probably require heap allocations which may be
|
|
|
- // worse overall since the number of rewrite constraints is generally low.
|
|
|
- for (const auto& search_constraint : rewrites_) {
|
|
|
- if (CompareFacetTypeConstraintValues(context(), search_constraint.lhs_id,
|
|
|
- rhs_inst_id) ==
|
|
|
- std::weak_ordering::equivalent) {
|
|
|
- if (&search_constraint == substituting_constraint_) {
|
|
|
- if (search_constraint.rhs_id != SemIR::ErrorInst::InstId) {
|
|
|
- CARBON_DIAGNOSTIC(FacetTypeConstraintCycle, Error,
|
|
|
- "found cycle in facet type constraint for {0}",
|
|
|
- InstIdAsConstant);
|
|
|
- // TODO: It would be nice to note the places where the values are
|
|
|
- // assigned but rewrite constraint instructions are from canonical
|
|
|
- // constant values, and have no locations. We'd need to store a
|
|
|
- // location along with them in the rewrite constraints, and track
|
|
|
- // propagation of locations here, which may imply heap allocations.
|
|
|
- context().emitter().Emit(loc_id_, FacetTypeConstraintCycle,
|
|
|
- substituting_constraint_->lhs_id);
|
|
|
- rhs_inst_id = SemIR::ErrorInst::InstId;
|
|
|
- }
|
|
|
- } else {
|
|
|
- rhs_inst_id = search_constraint.rhs_id;
|
|
|
- }
|
|
|
- }
|
|
|
+ // Diagnose a cycle if the RHS refers to something that depends on the value
|
|
|
+ // of the RHS.
|
|
|
+ if (rewrite_value->state == AccessRewriteValues::BeingRewritten) {
|
|
|
+ CARBON_DIAGNOSTIC(FacetTypeConstraintCycle, Error,
|
|
|
+ "found cycle in facet type constraint for {0}",
|
|
|
+ InstIdAsConstant);
|
|
|
+ // TODO: It would be nice to note the places where the values are
|
|
|
+ // assigned but rewrite constraint instructions are from canonical
|
|
|
+ // constant values, and have no locations. We'd need to store a location
|
|
|
+ // along with them in the rewrite constraints, and track propagation of
|
|
|
+ // locations here, which may imply heap allocations.
|
|
|
+ context().emitter().Emit(loc_id_, FacetTypeConstraintCycle, rhs_inst_id);
|
|
|
+ rhs_inst_id = SemIR::ErrorInst::InstId;
|
|
|
+ return SubstResult::FullySubstituted;
|
|
|
+ } else if (rewrite_value->state == AccessRewriteValues::FullyRewritten) {
|
|
|
+ rhs_inst_id = rewrite_value->inst_id;
|
|
|
+ return SubstResult::FullySubstituted;
|
|
|
}
|
|
|
|
|
|
- // Never recurse into ImplWitnessAccess, we don't want to substitute into
|
|
|
- // FacetTypes found within. We only substitute ImplWitnessAccesses that
|
|
|
- // appear directly on the RHS.
|
|
|
- return true;
|
|
|
+ // We have a non-rewritten RHS. We need to recurse on rewriting it. Reuse
|
|
|
+ // the previous lookup by mutating it in place.
|
|
|
+ rewrite_values_->SetBeingRewritten(*rewrite_value);
|
|
|
+
|
|
|
+ // The ImplWitnessAccess was replaced with some other instruction, which may
|
|
|
+ // contain or be another ImplWitnessAccess. Keep track of the associated
|
|
|
+ // constant we are now computing the value of.
|
|
|
+ substs_in_progress_.push_back(rhs_inst_id);
|
|
|
+ rhs_inst_id = rewrite_value->inst_id;
|
|
|
+ return SubstResult::SubstAgain;
|
|
|
}
|
|
|
|
|
|
- auto Rebuild(SemIR::InstId /*orig_inst_id*/, SemIR::Inst new_inst) const
|
|
|
+ auto Rebuild(SemIR::InstId /*orig_inst_id*/, SemIR::Inst new_inst)
|
|
|
-> SemIR::InstId override {
|
|
|
- return RebuildNewInst(loc_id_, new_inst);
|
|
|
+ auto inst_id = RebuildNewInst(loc_id_, new_inst);
|
|
|
+ auto subst_inst_id = substs_in_progress_.pop_back_val();
|
|
|
+ if (auto access = context().insts().TryGetAs<SemIR::ImplWitnessAccess>(
|
|
|
+ subst_inst_id)) {
|
|
|
+ auto* rewrite_value = rewrite_values_->FindRef(context(), *access);
|
|
|
+ CARBON_CHECK(rewrite_value);
|
|
|
+ rewrite_values_->SetFullyRewritten(*rewrite_value, inst_id);
|
|
|
+ }
|
|
|
+ return inst_id;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto ReuseUnchanged(SemIR::InstId orig_inst_id) -> SemIR::InstId override {
|
|
|
+ auto subst_inst_id = substs_in_progress_.pop_back_val();
|
|
|
+ if (auto access = context().insts().TryGetAs<SemIR::ImplWitnessAccess>(
|
|
|
+ subst_inst_id)) {
|
|
|
+ auto* rewrite_value = rewrite_values_->FindRef(context(), *access);
|
|
|
+ CARBON_CHECK(rewrite_value);
|
|
|
+ rewrite_values_->SetFullyRewritten(*rewrite_value, orig_inst_id);
|
|
|
+ }
|
|
|
+ return orig_inst_id;
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
+ struct SubstInProgress {
|
|
|
+ // The associated constant whose value is being determined, represented as
|
|
|
+ // an ImplWitnessAccess. Or another instruction that we are recursing
|
|
|
+ // through.
|
|
|
+ SemIR::InstId inst_id;
|
|
|
+ };
|
|
|
+
|
|
|
+ // The location of the rewrite constraints as a whole.
|
|
|
SemIR::LocId loc_id_;
|
|
|
- llvm::ArrayRef<SemIR::FacetTypeInfo::RewriteConstraint> rewrites_;
|
|
|
- const SemIR::FacetTypeInfo::RewriteConstraint* substituting_constraint_;
|
|
|
+ // Tracks the resolved value of each rewrite constraint, keyed by the
|
|
|
+ // `ImplWitnessAccess` of the associated constant on the LHS of the
|
|
|
+ // constraint. The value of each associated constant may be changed during
|
|
|
+ // substitution, replaced with a fully resolved value for the RHS. This allows
|
|
|
+ // us to cache work; when a value for an associated constant is found once it
|
|
|
+ // can be reused cheaply, avoiding exponential runtime when rewrite rules
|
|
|
+ // refer to each other in ways that create exponential references.
|
|
|
+ AccessRewriteValues* rewrite_values_;
|
|
|
+ // A stack of instructions being replaced in Subst(). When it's an associated
|
|
|
+ // constant, then it represents the constant value is being determined,
|
|
|
+ // represented as an ImplWitnessAccess.
|
|
|
+ //
|
|
|
+ // Avoid heap allocations in common cases, if there are chains of instructions
|
|
|
+ // in associated constants with a depth at most 16.
|
|
|
+ llvm::SmallVector<SemIR::InstId, 16> substs_in_progress_;
|
|
|
};
|
|
|
|
|
|
auto ResolveFacetTypeRewriteConstraints(
|
|
|
Context& context, SemIR::LocId loc_id,
|
|
|
llvm::SmallVector<SemIR::FacetTypeInfo::RewriteConstraint>& rewrites)
|
|
|
- -> void {
|
|
|
+ -> bool {
|
|
|
if (rewrites.empty()) {
|
|
|
- return;
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
- while (true) {
|
|
|
- bool applied_rewrite = false;
|
|
|
-
|
|
|
- for (auto& constraint : rewrites) {
|
|
|
- if (constraint.lhs_id == SemIR::ErrorInst::InstId ||
|
|
|
- constraint.rhs_id == SemIR::ErrorInst::InstId) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- auto lhs_access =
|
|
|
- context.insts().TryGetAs<SemIR::ImplWitnessAccess>(constraint.lhs_id);
|
|
|
- if (!lhs_access) {
|
|
|
- continue;
|
|
|
- }
|
|
|
+ // Apply rewrite constraints to each other, so that for example:
|
|
|
+ // `.X = Y and .Y = ()` becomes `.X = () and .Y = ()`.
|
|
|
+ AccessRewriteValues rewrite_values;
|
|
|
+ for (auto& constraint : rewrites) {
|
|
|
+ if (constraint.lhs_id == SemIR::ErrorInst::InstId ||
|
|
|
+ constraint.rhs_id == SemIR::ErrorInst::InstId) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ auto lhs_access =
|
|
|
+ context.insts().TryGetAs<SemIR::ImplWitnessAccess>(constraint.lhs_id);
|
|
|
+ if (!lhs_access) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- // Replace any `ImplWitnessAccess` in the RHS of this constraint with the
|
|
|
- // RHS of another constraint that sets the value of the associated
|
|
|
- // constant being accessed in the RHS.
|
|
|
- auto subst_inst_id =
|
|
|
- SubstInst(context, constraint.rhs_id,
|
|
|
- SubstImplWitnessAccessCallbacks(&context, loc_id, rewrites,
|
|
|
- &constraint));
|
|
|
- if (subst_inst_id != constraint.rhs_id) {
|
|
|
- constraint.rhs_id = subst_inst_id;
|
|
|
- if (constraint.rhs_id != SemIR::ErrorInst::InstId) {
|
|
|
- // If the RHS is replaced with a non-error value, we need to do
|
|
|
- // another pass so that the new RHS value can continue to propagate.
|
|
|
- applied_rewrite = true;
|
|
|
- }
|
|
|
- }
|
|
|
+ rewrite_values.InsertNotRewritten(context, *lhs_access, constraint.rhs_id);
|
|
|
+ }
|
|
|
+ for (auto& constraint : rewrites) {
|
|
|
+ if (constraint.lhs_id == SemIR::ErrorInst::InstId ||
|
|
|
+ constraint.rhs_id == SemIR::ErrorInst::InstId) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ auto lhs_access =
|
|
|
+ context.insts().TryGetAs<SemIR::ImplWitnessAccess>(constraint.lhs_id);
|
|
|
+ if (!lhs_access) {
|
|
|
+ continue;
|
|
|
}
|
|
|
|
|
|
- if (!applied_rewrite) {
|
|
|
- break;
|
|
|
+ auto* lhs_rewrite_value = rewrite_values.FindRef(context, *lhs_access);
|
|
|
+ CARBON_CHECK(lhs_rewrite_value);
|
|
|
+ rewrite_values.SetBeingRewritten(*lhs_rewrite_value);
|
|
|
+
|
|
|
+ auto replace_witness_callbacks =
|
|
|
+ SubstImplWitnessAccessCallbacks(&context, loc_id, &rewrite_values);
|
|
|
+ auto subst_inst_id =
|
|
|
+ SubstInst(context, constraint.rhs_id, replace_witness_callbacks);
|
|
|
+ constraint.rhs_id = subst_inst_id;
|
|
|
+ if (constraint.rhs_id == SemIR::ErrorInst::InstId) {
|
|
|
+ return false;
|
|
|
}
|
|
|
+
|
|
|
+ rewrite_values.SetFullyRewritten(*lhs_rewrite_value, subst_inst_id);
|
|
|
}
|
|
|
|
|
|
// We sort the constraints so that we can find different values being written
|
|
|
@@ -542,8 +735,11 @@ auto ResolveFacetTypeRewriteConstraints(
|
|
|
}
|
|
|
constraint.rhs_id = SemIR::ErrorInst::InstId;
|
|
|
next.rhs_id = SemIR::ErrorInst::InstId;
|
|
|
+ return false;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
} // namespace Carbon::Check
|