mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Replace .Self in facet types (#7097)
This allows `T impls X` constraints to function, since they must contain some reference to `.Self` in order to be valid. This should be sufficient to support the interfaces we need for for loops over C++ range-for-compatible types. We replace `.Self` in the following places: - In a require decl, as we have a specific self facet to replace it with from the declaration, either a user-specified facet or the symbolic `Self`. - When identifying a facet type, as we have a specific self that we are identifying the facet type with. That self gets used for all `.Self` references. - Implicit `.Self` references on the RHS of an `impls` constraint when building a facet type. The `.Self` references there no longer refer to the top level self facet, so replace them with the facet that we now know they refer to, which is found on the LHS of the `where` before the `impls`. - Rewrite constraints in impl lookup when validating them and comparing them with constants from witnesses, which come from identifying a facet type. - Rewrite constraints in ImplWitnessAccess eval when comparing them with constants from witnesses, which come from identifying a facet type. Substitution is done through `SubstPeriodSelf`. It handles replacing `.Self` and `.Self as type`, for a replacement facet that is either of type FacetType or TypeType. Eval currently diagnoses some ambiguous `.Self` references when doing substitution of `.Self` but this is the incorrect place to do it, so there are TODOs about moving this to name lookup. To support these diagnostics there's some additional complexity in `SubstPeriodSelf` that can go away once the TODOs are addressed, such as asking the caller if they want to replace each `.Self`, in order for it to report a diagnostic. There are a number of follow-up work items here: - Some TODO tests. - Remove `SymbolicBindingType` since its intention was to support `.Self` but we don't need it with this approach. - Replace `.Self` in rewrite constraints of require decls. - Replace `.Self` in rewrite constraints of impl as when constructing the witness table. - Reject explicit `.Self` in name lookup when it would be ambiguous. - Officially disallow `.Self.A = B` in rewrite constraints in the design docs, so that we don't have the case where `.A` is allowed but `.Self.A` is not due to ambiguity.
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include "toolchain/base/kind_switch.h"
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/convert.h"
|
||||
#include "toolchain/check/facet_type.h"
|
||||
#include "toolchain/check/generic.h"
|
||||
#include "toolchain/check/handle.h"
|
||||
#include "toolchain/check/inst.h"
|
||||
@@ -267,14 +268,91 @@ static auto ValidateRequire(Context& context, SemIR::LocId full_require_loc_id,
|
||||
return ValidateRequireResult{.identified_facet_type = &identified};
|
||||
}
|
||||
|
||||
// Replace all `.Self` references with the self-type.
|
||||
static auto SubstPeriodSelfInConstraint(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::TypeInstId self_type_inst_id,
|
||||
SemIR::TypeInstId constraint_inst_id)
|
||||
-> SemIR::TypeInstId {
|
||||
auto orig_facet_type = context.insts().GetAs<SemIR::FacetType>(
|
||||
context.constant_values().GetConstantInstId(constraint_inst_id));
|
||||
const auto& orig_info =
|
||||
context.facet_types().Get(orig_facet_type.facet_type_id);
|
||||
|
||||
SubstPeriodSelfCallbacks callbacks(
|
||||
&context, loc_id, context.constant_values().Get(self_type_inst_id));
|
||||
|
||||
auto replace_interface = [&](SemIR::SpecificInterface si) {
|
||||
return SubstPeriodSelf(context, callbacks, si);
|
||||
};
|
||||
auto replace_constraint = [&](SemIR::SpecificNamedConstraint sc) {
|
||||
return SubstPeriodSelf(context, callbacks, sc);
|
||||
};
|
||||
auto replace_type_impls_interface =
|
||||
[&](SemIR::FacetTypeInfo::TypeImplsInterface impls)
|
||||
-> SemIR::FacetTypeInfo::TypeImplsInterface {
|
||||
auto self = SubstPeriodSelf(context, callbacks,
|
||||
context.constant_values().Get(impls.self_type));
|
||||
auto interface =
|
||||
SubstPeriodSelf(context, callbacks, impls.specific_interface);
|
||||
return {context.constant_values().GetInstId(self), interface};
|
||||
};
|
||||
auto replace_type_impls_constraint =
|
||||
[&](SemIR::FacetTypeInfo::TypeImplsNamedConstraint impls)
|
||||
-> SemIR::FacetTypeInfo::TypeImplsNamedConstraint {
|
||||
auto self = SubstPeriodSelf(context, callbacks,
|
||||
context.constant_values().Get(impls.self_type));
|
||||
auto constraint =
|
||||
SubstPeriodSelf(context, callbacks, impls.specific_named_constraint);
|
||||
return {context.constant_values().GetInstId(self), constraint};
|
||||
};
|
||||
|
||||
SemIR::FacetTypeInfo info;
|
||||
llvm::append_range(
|
||||
info.extend_constraints,
|
||||
llvm::map_range(orig_info.extend_constraints, replace_interface));
|
||||
llvm::append_range(
|
||||
info.extend_named_constraints,
|
||||
llvm::map_range(orig_info.extend_named_constraints, replace_constraint));
|
||||
llvm::append_range(
|
||||
info.self_impls_constraints,
|
||||
llvm::map_range(orig_info.self_impls_constraints, replace_interface));
|
||||
llvm::append_range(info.self_impls_named_constraints,
|
||||
llvm::map_range(orig_info.self_impls_named_constraints,
|
||||
replace_constraint));
|
||||
llvm::append_range(info.type_impls_interfaces,
|
||||
llvm::map_range(orig_info.type_impls_interfaces,
|
||||
replace_type_impls_interface));
|
||||
llvm::append_range(info.type_impls_named_constraints,
|
||||
llvm::map_range(orig_info.type_impls_named_constraints,
|
||||
replace_type_impls_constraint));
|
||||
// TODO: Replace .Self in rewrites too. We need to actually validate rewrite
|
||||
// constraints from named constraints in impl lookup (see
|
||||
// todo_fail_require_with_mismatching_rewrite_constraint.carbon).
|
||||
llvm::append_range(info.rewrite_constraints, orig_info.rewrite_constraints);
|
||||
|
||||
info.Canonicalize();
|
||||
if (info == orig_info) {
|
||||
// Nothing was substituted, keep the original instruction.
|
||||
//
|
||||
// It is noteworthy that we keep the non-canonical instruction here, since
|
||||
// it may have a symbolic value (which is attached to a generic, and can be
|
||||
// updated by specifics). Returning the canonical constraint instruction
|
||||
// would lose the attachment to the generic which would be incorrect.
|
||||
return constraint_inst_id;
|
||||
}
|
||||
|
||||
return AddTypeInst<SemIR::FacetType>(
|
||||
context, loc_id,
|
||||
{.type_id = SemIR::TypeType::TypeId,
|
||||
.facet_type_id = context.facet_types().Add(info)});
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool {
|
||||
auto [constraint_node_id, constraint_inst_id] =
|
||||
context.node_stack().PopExprWithNodeId();
|
||||
auto [self_node_id, self_inst_id] =
|
||||
context.node_stack().PopWithNodeId<Parse::NodeCategory::RequireImpls>();
|
||||
|
||||
auto decl_block_id = context.inst_block_stack().Pop();
|
||||
|
||||
// Process modifiers.
|
||||
auto introducer =
|
||||
context.decl_introducer_state_stack().Pop<Lex::TokenKind::Require>();
|
||||
@@ -294,6 +372,7 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool {
|
||||
auto scope_id = context.scope_stack().PeekNameScopeId();
|
||||
context.name_scopes().Get(scope_id).set_has_error();
|
||||
}
|
||||
context.inst_block_stack().Pop();
|
||||
DiscardGenericDecl(context);
|
||||
return true;
|
||||
}
|
||||
@@ -302,19 +381,32 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool {
|
||||
if (identified_facet_type->required_impls().empty()) {
|
||||
// A `require T impls type` adds no actual constraints, so nothing to do.
|
||||
// This is not an error though.
|
||||
context.inst_block_stack().Pop();
|
||||
DiscardGenericDecl(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Substitute .Self here.
|
||||
auto constraint_type_inst_id =
|
||||
context.types().GetAsTypeInstId(constraint_inst_id);
|
||||
// The identified facet type also replaced `.Self` references, but we want to
|
||||
// store the full facet type not just the identified one. So we have to
|
||||
// replace `.Self` references explicitly here in the canonical constraint. We
|
||||
// do this after `ValidateRequire()` which has ensured the constraint is in
|
||||
// fact a FacetType.
|
||||
auto constraint_type_inst_id = SubstPeriodSelfInConstraint(
|
||||
context, constraint_node_id, self_inst_id,
|
||||
context.types().GetAsTypeInstId(constraint_inst_id));
|
||||
// The replacement of `.Self` can create a new FacetType instruction which we
|
||||
// want to be part of the require decl's inst block, so we defer the Pop until
|
||||
// after the subst.
|
||||
auto decl_block_id = context.inst_block_stack().Pop();
|
||||
|
||||
auto require_impls_decl =
|
||||
SemIR::RequireImplsDecl{// To be filled in after.
|
||||
.require_impls_id = SemIR::RequireImplsId::None,
|
||||
.decl_block_id = decl_block_id};
|
||||
auto decl_id = AddPlaceholderInst(context, node_id, require_impls_decl);
|
||||
// TODO: We don't need to store the `self_inst_id` anymore, since we've
|
||||
// encoded it into the constraints of the facet type which was converted to
|
||||
// the form `<Self> where .Self impls <Constraint>`.
|
||||
auto require_impls_id = context.require_impls().Add(
|
||||
{.self_id = self_inst_id,
|
||||
.facet_type_inst_id = constraint_type_inst_id,
|
||||
|
||||
Reference in New Issue
Block a user