We frequently want to operate on singletons. Per discussion, drop
`Singleton` to make the code shorter.
This started off as wanting to write `inst_id.is_error()`, but the
dependency relationship between ids.h and singleton_insts.h would
require some kind of delayed evaluation to allow the implementation to
remain in headers (which I suspect is helpful to have for inlining). I
could have added something like `IsErrorInst`, forward declared in ids.h
and defined in singleton_insts.h (which would always be included by
typed_insts.h), but the template approach felt like a decent balance
between (a) removing the boilerplate `::SingletonInstId`, (b)
understandability, (c) still visually mirroring if we immediately return
a singleton, and (d) flexibility for more than just `ErrorInst`. But TBH
I'd probably still have written `is_error()` if it didn't require
addressing the cross-header cycle.
Then I tried `SemIR::InstId::Is<SemIR::ErrorInst>`, which generally
worked with types but generated the complaint that it didn't shorten
*all* singleton uses. So pulling back on `::Is`, and instead just
dropping `Singleton`.
Use TypeInstId in many more places where the instruction is required
to/known to always be a type value. This should be a somewhat exhaustive
set of places, as it covers all instructions given to
GetTypeIdFromTypeInstId().
The things of interest here are:
- Singleton instructions are always of type TypeType, so they are now
TypeInstIds.
- ErrorInst::SingletonInstId gets upcast to be an InstId because it's
sometimes used to define the type of a variable (as in `auto inst_id =
SemIR::ErrorInst::SingletonInstId;` that may hold other InstIds.
- Parse nodes don't really know about TypeInstId, so NodeStack::Push
needs to do some special casing to avoid CHECK failures when given a
TypeInstId but expecting an InstId. We leave a TODO behind here because
the nodes which are being pushed a TypeInstId should probably be taught
to expect that, but such a change is a bit tricky, so too much for this
PR.
TypeInstId is an InstId whose constant value has a type of TypeType.
This includes:
- Type value instructions, the `ClassType` or `IntLiteralType`
instructions.
- Constraint value instructions, which are the `FacetType` and
`TypeType` instructions, each of which also have type TypeType.
TypeInstId encodes in the type system that it is safe to convert the
instruction's value to a TypeId, and CHECKs at construction that this
invariant is maintained.
---------
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
In preparation for shifting from `TypeId`s potentially representing
attached types to always representing unattached types, using
[terminology suggested on
Discord](https://discord.com/channels/655572317891461132/963846118964350976/1359286326779973712).
This change causes us to track slightly more type spelling information
through SemIR.
One change that has significant impact on the SemIR output is that we
now build a `struct_type` instruction in each class representing the
types of the fields, including the spelling used for those types. This
is now no longer always identical to the corresponding canonical
`struct_type` for the object representation, so it's built separately
and owned by the class.
Also remove `TypeBlock` support entirely, as its only use was
representing `TupleType`s, which now use an `InstBlock`.
This gives a slightly simpler representation for `UnboundElementType`s
in eval blocks, and in principle allows us to preserve the spelling of a
field's type into the `UnboundElementType` and thereby into a field
reference, although as of right now this doesn't affect our diagnostic
output in any way.
During error recovery for a field with a non-concrete type, preserve the
type in the `UnboundElementType` regardless. It's not really problematic
to have a non-concrete type there, and this makes it easier to track the
instruction used to specify the type.
This is a step towards switching symbolic types to always be abstract
during type checking.
For each kind of instruction, specify whether its constant evaluation
needs an `InstId` or not. If it does, ensure that all constant
evaluation of that instruction provides one. Otherwise, allow calling
into the evaluator without providing an `InstId`.
This allows us to reliably use the `InstId` in evaluation steps that
either need a location or need to look at the original operands of the
instruction prior to evaluation, and also to support `TryEvalInst` calls
safely for instructions whose evaluation does not need an `InstId`.
Instead of storing a `TypeId` that always refer to a facet type that
always contains exactly a single interface, store the interface
directly.
Also improve stringification of `LookupImplWitness` and witness access
into it, switching to using newly-added functionality for stringifying
specific interfaces.
Each of these types takes another type as an operand. Instead of storing
that other type as a `TypeId`, store it as an `InstId` so that we can
track how it was written, not only its canonical form.
The canonical constant values of these types continue to store the
canonical constant values of their operands, as normal.
---------
Co-authored-by: Dana Jansens <danakj@orodu.net>
* Add `RequireCompleteFacetType` and `ResolveFacetTypeImplWitness` to
`check::Context`. Goal was to move code from `impl.cpp` (mostly) without
functional changes.
* Complete type information is cached with the facet type, and is stored
in a `complete_facet_types()` table.
* Main functional change is to diagnose attempts to use a rewrite
constraint on an associated function. Some existing diagnostics have
been updated.
* Remove `check::Context::RequireDefinedType`:
* For class types, use `RequireCompleteType`
* For facet types, use `RequireCompleteFacetType`
* Introduce a `SemIR::SpecificInterface` to hold an interface and
specific id pair.
* Keep the specific interface ids in the impl object.
* Avoid some extra copies in `Dump` functions.
* Future work missing from this PR:
* Resolving for member access or actions that require impl lookup.
* Resolving rewrites constraints that refer to non-concrete values.
* Any support for adding implied constraints that result from a `where`
clause (though TODOs have been added).
---------
Co-authored-by: Josh L <josh11b@users.noreply.github.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Co-authored-by: Dana Jansens <danakj@orodu.net>
This creates a new check/type.h for most logic, and also moves some
functions to TypeStore in sem_ir/type.h. My approach for TypeStore is to
focus on moving the read-only functions there.