Cycles are defined as reaching two independent lookups in a chain that
have all the same types involved. The acyclic rule states that this is
not possible and results in an error:
https://docs.carbon-lang.dev/docs/design/generics/details.html#acyclic-rule
To do this we need to track the types involved in impl lookup. The
interface constant includes the whole facet type being looked up, which
includes any specific types for generics or where constraints. Thus we
just need to compare the constant ids to look for this condition.
---------
Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com>
Currently it returns false which just ends typechecking. Instead handle
the error state later and avoid firing overlapping diagnostics in
'extend impl as'.
---------
Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com>
If 'extend impl' is invalid, mark the impl as invalid by putting an
ErrorInst in the witness_id field.
The construction of the witness_id can otherwise return an ErrorInst but
impl lookup was not checking for that. Now have impl lookup check for an
error there before attempting to deduce generic parameters, which avoids
infinite recursion through deduction in cases like a cyclical impl of
itself.
Adds a testcase for infinite impl-of-itself lookup found by fuzzer.
The fn receiving a facet type needs to deduce a type from a
FacetAccessType, which is the SemIR type representing the parameter type
that is a generic parameter. For example:
```
fn F[T: Interface](val: T);
```
Here T is a generic parameter that is a facet value, but the `val`
parameter's type is the facet value converted from a FacetType to a
TypeType, with `as type`. The result of that conversion is a
FacetAccessType. So the deduction code sees a FacetAccessType for the
type of `val`.
We make deduction undo the `as type` conversion to move back to the `T`
parameter declaration, which has type FacetType in order to deduce the
required facet value (which is itself a type constrained by the
FacetType). And when we have a facet value (of type FacetType) to be
deduced, we will also convert the argument if it is of an appropriate
type to a FacetValue that matches the FacetType using the changes from
PR #4863.
Tests with `impl forall` can cause impl deduction to recurse forever and
crash, so those tests are omitted in this PR and they will come in
follow-up work that address the infinite recursion.
Rebased on top of PR #4885
High level, replacing `Id::Invalid` with `Id::None` and `Id::is_valid`
with `Id::has_value` for clarity, as discussed
[here](https://discord.com/channels/655572317891461132/655578254970716160/1331664574545395794).
The `IntId` refactoring is needed together with `AnyIdBase` because it's
also used with `ValueStore`.
Note, trying to be careful not to rewrite `EnumBase::InvalidIndex`, or
`is_valid` in general (e.g., `IdKind::is_valid`).
I've tried to sequence commits here:
1. Automatic replacements:
- `((?:Id|Index)(?: |::|\(|Base(?:\(|::)))Invalid((?:Index)?\W)` ->
`$1None$2`
- `<invalid>` -> `<none>`
- `InvalidNodeId` -> `NoneNodeId`
- `/\*invalid\*/` -> `/*none*/`
- `id((?:_|\(\))(?:\.|->))is_valid` -> `id$1has_value`
2. Manual edits:
- In `int.h` and `int_test.cpp`
- `IntT` has `is_value`, which I'm renaming to `is_embedded_value`.
- Manual edits to comments in this file.
- `AnyIdBase` and `IdBase`
- Declaration of `is_valid` -> `has_value`, `InvalidIndex` ->
`NoneIndex`.
- In `ids.h` and `ids.cpp`
- `is_valid` -> `has_value`
- `// An explicitly invalid ID.` -> `// An ID with no value.`; similar
for index
- Various math on `InvalidIndex` -> `NoneIndex`
- Various mentions of "valid" in comments
- In `value_store.h`, for `IdT::Invalid`, plus one comment
- In `impl.h` and `tokenized_buffer.h`, we had different initialization
of `::None` values (versus `ids.h` syntax) that I fixed manually.
- Spot checks to compile
- Particularly where `is_valid` replacements didn't catch spots due to
different naming.
3. Autoupdate tests
4. verbose.carbon (NOAUTOUPDATE)
5. Comment spot checks
Note there are probably other mentions of "Invalid" that should be swept
up, but I'd like to argue for merging and separating out remaining
cleanup since this is so sweeping (and likely to hit merge conflicts
from churn). We'll probably have lingering mentions of "invalid" for a
bit regardless, just because there are uses of "invalid" in non-Id APIs.
* Change `InterfaceWitness` -> `ImplWitness`
* Include a `SpecificId` in the `ImplWitness`. This allows the
`InstBlock` it contains to have its own identity, allowing it to be
changed as the impl is processed. Evaluation only updates the specific.
* Create the `ImplWitness` at the start of the impl definition. In the
future, this will be populated with the values of non-function
associated constants. For now, it starts full of invalid instruction
ids.
* Implements the model suggested in #4672 .
Note that the non-SemIR testdata changes are to these file:
* `toolchain/check/testdata/impl/lookup/fail_todo_undefined_impl.carbon`
* `toolchain/check/testdata/struct/import.carbon`
* `toolchain/check/testdata/tuple/import.carbon`
The last two are due to an import of generics bug exposed by this PR,
which will be fixed in a follow-on.
---------
Co-authored-by: Josh L <josh11b@users.noreply.github.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Also fix a bug in `Context::GetClassType` that previously tried to
complete the class type before returning it. That's not correct --
`GetCompleteTypeImpl` is only appropriate for cases where the type can
trivially be completed and completing it can't fail -- and led to
infinite recursion with this change because we would call `GetClassType`
when producing a diagnostic if completing that class type failed.
This is essentially the result of looking at `.begin()` uses. We also
frequently do `std::shuffle`, but unfortunately STLExtras doesn't
provide a wrapper for that.
Instead, eagerly import only the impls from the api file corresponding
to the current file, if any, because we need those for impl
redeclaration lookup. For all other cases, load only the impls in
libraries that are referenced as part of an impl lookup query.