mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:40:11 +01:00
A nested designator like `.(X.X1).(Y.Y1)` results in nested ImplWitnessAccess instructions, which can produce cycles in the toolchain easily when replacing `.Self`. First, when constructing a facet type like `V:! Z where .Z1 impls (Y where .Y1 = U)` we substitute replace `.Self` in the nested facet type, and in this case we replace `.Self` with `.Z1` which contains a `.Self` of its own. This was coming from us being lazy about replacing `.Self` in an `impl as` declaration, such as `impl C as Z where .Z1 = .Self`. The self type is known there, so we can more eagerly replace `.Self` as we do in a `require impls` declaration. Then the replacement for `.Self` never comes with a `.Self` that needs to also be replaced. Any resulting `.Self` would always be the top-level one. Second, when evaluating ImplWitnessAccess, we were replacing .Self in the LHS of rewrite constraints, but the `.Self` may itself have a type that contains rewrite constraints. If one of those rewrite constraints has nested ImplWitnessAccess instructions, we evaluate the new ImplWitnessAccess, which again finds rewrite constraints to replace `.Self` in, and we repeat forever. For this one we just stop replacing .Self in the LHS of rewrite constraints. Since they are always against .Self, we can always look in the access facet's type for a value. While fixing ImplWitness access, also correct the lookup to search through the types of nested ImplWitnessAccess instructions to find a rewrite value, since it may find it at any level up to the eventual `.Self`. --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk>
103 lines
4.6 KiB
C++
103 lines
4.6 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#ifndef CARBON_TOOLCHAIN_CHECK_IMPL_H_
|
|
#define CARBON_TOOLCHAIN_CHECK_IMPL_H_
|
|
|
|
#include "toolchain/check/context.h"
|
|
#include "toolchain/sem_ir/ids.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
struct RedeclaredImpl {
|
|
// The previous Impl which the query Impl is redeclaring.
|
|
SemIR::ImplId prev_impl_id;
|
|
};
|
|
struct NewImpl {
|
|
// The lookup bucket for the query Impl where it should be added once an
|
|
// ImplId is known.
|
|
SemIR::ImplStore::LookupBucketRef lookup_bucket;
|
|
// Indicates the query Impl is not a redeclaration but an error was diagnosed.
|
|
// The caller should avoid diagnosing more errors in the query impl.
|
|
bool find_had_error;
|
|
};
|
|
|
|
// Finds an existing impl if the `query_impl` is a redeclaration, and returns
|
|
// its `ImplId`. This ensures all (valid) redeclarations share the same
|
|
// `ImplId`. Otherwise, returns the bucket where a new `ImplId` should be added.
|
|
auto FindImplId(Context& context, const SemIR::Impl& query_impl)
|
|
-> std::variant<RedeclaredImpl, NewImpl>;
|
|
|
|
// Adds an impl to the ImplStore, and returns a new `ImplId`.
|
|
//
|
|
// If the impl is modified with `extend` then the parent's scope is extended
|
|
// with it.
|
|
auto AddImpl(Context& context, const SemIR::Impl& impl,
|
|
SemIR::ImplStore::LookupBucketRef lookup_bucket,
|
|
Parse::NodeId extend_node, SemIR::LocId implicit_params_loc_id)
|
|
-> SemIR::ImplId;
|
|
|
|
// Creates and returns an impl witness instruction for an impl declaration.
|
|
//
|
|
// If there are no rewrites into a name of the interface being implemented, a
|
|
// placeholder witness table is created, to be replaced in the impl definition.
|
|
//
|
|
// Adds and returns an `ImplWitness` instruction (created with location set to
|
|
// `loc_id`) that shows the "`Self` type" (from a facet in `impl.self_id`)
|
|
// implements an identified interface (from a facet type in
|
|
// `impl.constraint_id`). This witness reflects the values assigned to
|
|
// associated constant members of that interface by rewrite constraints in the
|
|
// constraint facet type. `self_specific_id` will be the `specific_id` of the
|
|
// resulting witness.
|
|
auto AddImplWitnessForDeclaration(Context& context, SemIR::LocId loc_id,
|
|
const SemIR::Impl& impl,
|
|
SemIR::SpecificId self_specific_id)
|
|
-> SemIR::InstId;
|
|
|
|
// Update `impl`'s witness at the start of a definition.
|
|
auto ImplWitnessStartDefinition(Context& context, SemIR::Impl& impl) -> void;
|
|
|
|
// Adds the function members to the witness for `impl`.
|
|
auto FinishImplWitness(Context& context, const SemIR::Impl& impl_id) -> void;
|
|
|
|
// Checks that any `require` declarations in the interface being implemented by
|
|
// `impl` are satisfied. Otherwise, a diagnostic is issued and the `impl` is
|
|
// made invalid.
|
|
auto CheckRequireDeclsSatisfied(Context& context, SemIR::LocId loc_id,
|
|
SemIR::Impl& impl) -> void;
|
|
|
|
// Sets all unset members of the witness for `impl` to the error instruction and
|
|
// sets the witness id in the `Impl` to an error.
|
|
auto FillImplWitnessWithErrors(Context& context, SemIR::Impl& impl) -> void;
|
|
|
|
// Returns whether the impl is either `final` explicitly, or implicitly due to
|
|
// being concrete.
|
|
auto IsImplEffectivelyFinal(Context& context, const SemIR::Impl& impl) -> bool;
|
|
|
|
// Checks that `impl_function_id` is a valid implementation of the function
|
|
// described in the interface as `interface_function_id`. Returns the value to
|
|
// put into the corresponding slot in the witness table, which can be
|
|
// `ErrorInst::InstId` if the function is not usable.
|
|
auto CheckAssociatedFunctionImplementation(
|
|
Context& context, SemIR::FunctionType interface_function_type,
|
|
SemIR::SpecificId enclosing_specific_id, SemIR::InstId impl_decl_id,
|
|
bool defer_thunk_definition) -> SemIR::InstId;
|
|
|
|
// Checks that the constraint specified for the impl is a facet type. Returns
|
|
// false if an error was diagnosed.
|
|
auto CheckConstraintIsFacetType(Context& context, SemIR::LocId loc_id,
|
|
SemIR::TypeInstId constraint_id) -> bool;
|
|
|
|
// Checks that the constraint specified for the impl is a valid, identified
|
|
// facet type that extends a single interface. Returns the interface that the
|
|
// impl implements. On error, issues a diagnostic and returns `None`.
|
|
auto CheckConstraintIsInterface(Context& context, SemIR::LocId loc_id,
|
|
SemIR::InstId self_id,
|
|
SemIR::TypeInstId constraint_id)
|
|
-> SemIR::SpecificInterface;
|
|
|
|
} // namespace Carbon::Check
|
|
|
|
#endif // CARBON_TOOLCHAIN_CHECK_IMPL_H_
|