Point symbolic witnesses into `.Self` written inside an impl decl at the
impl that is being declared. This is tricky because the impl does not
yet exist. So we use a new instruction `ImplSelfWitness` which _will_ be
replaced by the `ImplWitness` once it becomes available. The
`ImplSelfWitness` acts like a symbolic witness, except it does not
perform lookup, since we know which impl we will get a witness from.
This prevents us from finding other impls when performing lookups into
`.Self` in an impl decl, which produces incorrect/incoherent results.
This makes SubstPeriodSelf more generally useful, with one less gotcha.
Previously calling it with a facet type would just do nothing.
This is possible now because we
- Have SubstOperandsSkipType to make use of
- Have banned constructs which introduce ambiguous .Self, so we don't
need to try avoid finding undesired .Self insts in facet types in other
positions (like in specifics).
The type of `.Self` introduced by `where` may contain a `.Self` inside
it. Freeze the type so that we have a consistent view of `.Self` inside
the facet type, where they are all frozen.
The type of `.Self` only has extend constraints from the LHS of the
`where`. So we need to copy any non-extend constraints into the
`where_stack` so they are available as early-impls and can be used for
impl lookups on the RHS of the where. We need to freeze any `.Self`
references in these just as we do for rewrite constraints.
The implicit/explicit `.Self` concept is a heuristic at best, so we
should avoid relying on it. The ImplicitOnly option is no longer used,
as it was used to remove/disambiguate `.Self` in nested facet types, but
we have banned nesting `where` on the RHS of a `where`. So we no longer
have to worry about ambiguous `.Self`.
We can remove all the designator tracking heuristics in `.Self`
substitution as well now, as they were used for the now-removed
ExplicitOnly (removed in
https://github.com/carbon-language/carbon-lang/pull/7460) and
ImplicitOnly options.
The implicit/explicit `.Self` concept is a heuristic at best, so we
should avoid relying on it. We now only have one value of `.Self` in a
facet type (we have banned nested `where` on the RHS of a `where`). So
we don't need to preserve any `.Self` for the purpose of disambiguation,
and we can subst all `.Self` on the the RHS of a rewrite constraint.
In #7436 we stopped substituting `.Self` when collecting witnesses out
of a facet type. While this was correct, it did not capture all the
cases that need to avoid substituting `.Self`. And it poisoned the
`IdentifiedFacetType` cache by not replacing `.Self` but storing the
result in the cache. This led to incoherent behaviour, where the result
of an impl lookup would change depending on which ones had been done
previously.
Now we use a flag to track for each `.Self` if we're currently
type-checking inside the scope where it was introduced in a facet type.
While inside that scope, identify should not replace the `.Self`. Any
use of it should remain as-is since we don't yet know what value will
replace it. We call this state "frozen" since it should not be modified
by identify. This requires a substitution step when we leave the scope
that introduced the `.Self`, to remove the flag. The flag is set in the
`EntityName` of the `SymbolicBinding`, and is part of the canonical
value, since `.Self` can become part of types, which are constants, and
the flag needs to follow it for correct behaviour.
We also have to ensure the flag is the same when doing comparison with
constants from inside a facet type and constants from outside. For
instance in `(Z where .Z1 = ()) where .Z2 = .Z1`, when we arrive at the
second `.Z1` its `.Self` will be frozen, while the `.Z1 = ()` contains a
non-frozen `.Self`. So we add the frozen flag to the first when storing
it in `where_stack` in order to compare the constant values of the two
`.Z1`.
The `WhereExpr` requirement inst kinds now have an `InstConstantKind` of
`AlwaysUnique` instead of `Never`. This allows us to add them to the
usual InstBlocks, and in an `eval fn` body they have a constant value,
so eval does not fail when trying to call that function. We have to be
careful to not consider `AlwaysUnique` as being actually concrete
though, since their constant value erases `.Self`-dependence. This
allows us to stop special casing them when thawing the requirements
block in a `WhereExpr`, and we can just thaw each `InstId` in the block
in a straightforward manner.
We add the new flag to the instruction's fingerprint and name in
formatted semir.
This performs `.Self` substitution in a single step, for the whole facet
type, instead of doing it individually for each constraint visited in
the top-level facet type. Then we don't need to track state to avoid
subst in constraints that come from other named constraints.
The semir changes are because we now generate a whole other FacetType
from the substitution.
This disallows building a facet type that contains another facet type
with non-extend constraints in it. Which in turn prevents the
possibility of introducing a different `.Self` into a facet type.
Eval can still insert a facet type with non-extend constraints, as we
only prevent it for `where` being written into the facet type. There is
a TODO in handle_where.cpp for this and some tests in
toolchain/check/testdata/facet/nested_facet_types_from_eval.carbon
Depending on the order in which type completion identifies things in the
facet type, it may try to replace `.Self` with a facet that fails to
convert to the type of `.Self` due to a monomorphization error. We
should fail gracefully, not crash.
When replacing `.Self` in a facet type we don't want to destroy the
structure of designators in rewrite constraints so that rewrite
constraint resolution and other similar code can still find them. We
were doing this only for the LHS, and for the RHS if it was a standalone
designator, like `.X = .Y` but we want to do this more robustly to also
avoid rewrite the `.Self` in `.Y` in the expression `.X = C(.Y)`. This
allows resolution, and ImplWitnessAccess to recognize the designator and
replace the `.Y` with the RHS value of another rewrite constraint
assigning to `.Y`.
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>
This lets us stop eliding it in textual semir tests with dump ranges.
Previously it would always get elided, even though it was part of the
range being dumped, and was referred to by other instructions in the
dump range.
Since each `.Self` is unique (can change its type if not its value) in a
facet type, having each one distinct by location also aids
understanding.
A `where` expression nested inside a `T impls X` constraint makes
`.Self` ambiguous on the right-hand side of the `where` if `T` is
anything other than `.Self`. After the `where`, the value of a `.Self`
could be `T` or could be the value of `.Self` before the `impls`
constraint: the so-called top-level value of `.Self`.
Implicit use of `.Self` in designators is always allowed, and they are
bound (and replaced by a reference) to the inner-most possible value of
`.Self`. On the right-hand side of the nested `where` above, they have
the value `T as X`.
`.Self impls ...` is also always allowed, since it acts more as a
keyword here, and it always refers to the inner-most possible value of
`.Self`.
Any other explicit use of `.Self` is diagnosed when ambiguous, in any
kind of constraint. This is done in the handling of `WhereExpr` since it
has enough context to allow `.Self impls` (which is an explicit use)
while disallowing other explicit uses. And because it has non-canonical
instructions to work with, so it is able to diagnose errors with precise
locations.
Since `.Self` is no longer going to be marked with depth modifiers, the
eval of `WhereExpr` does not need an input facet value instruction
representing `.Self` to compare with, as they are now going to all be
equivalent. So revert it back to just looking for the `PeriodSelf` name
id, through a shared helper being introduced as `IsPeriodSelf`. And drop
the period self InstId from the `WhereExpr` instruction. This causes
most of the formatted SemIR changes.
Move helpers for working with and replacing `.Self` to their own file,
out of the `facet_type.h` header/cpp files. These are working with
`.Self` facet values more than facet types, though `.Self` is a name
that only exists inside the scope of a facet type.