Remove the type canonicalization mechanism and instead rely on constant
canonicalization to deduplicate types.
Rename the `Canonicalize*Type` functions to reflect that they're no
longer performing canonicalization. Switch code that creates types due
to semantic checking, rather than due to source syntax, to directly
create type constants through evaluation rather than creating an
instruction and evaluating it to produce a separate constant
representation.
The mapping from `const (const T)` that was previously performed by type
canonicalization is now implemented in expression evaluation instead.
The value `<error>` is now treated as a constant value, with a special
property that an instruction involving `<error>` that could possibly be
constant evaluates to `<error>`. This helps avoid producing follow-on
errors when an error occurs as a subexpression of an expression, such as
a type, that is intended to be constant.
Rather than producing multiple constants with the same value, fold all
instances of a given constant to the same constant instruction.
A future PR will use this to replace the current type canonicalization
system.
Instructions created by splices during conversion are now evaluated, as
are instructions created in cases where we first create a placeholder
instruction and later replace it by a different instruction.
This also removes the ability to set a parse node and instruction
independently after creating an `InstId`, which could lead to them
accidentally not matching.
This is accomplished by tracking an extra bit on the ID we store in the
constant values table, and propagating that from subexpressions to the
enclosing expression. This extra bit is not yet computed correctly for
types; that will be addressed in later PRs.
---------
Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Form a side table with constant values for each instruction. Evaluation
is only supported for a few very simple kinds of instruction for now.
This is not observable outside of the SemIR output, because nothing
depends on expressions having a constant value phase yet.
Namespaces are copied, which means also adding their name to the
underlying instruction. It happened not to be done previously; the name
was only in name lookup.
Since the only import supported right now is the default import,
functionality is limited; in the future I'll need to deal with namespace
vs package conflicts.
Tests of namespace imports are under "namespace" -- I figured this would
be best for scaling as more instructions get support.
This also improves some debugging-related output that I was trying to
use while trying to build the support.
---------
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
This change adds a `BindSymbolicName` instruction for generic bindings,
paralleling the existing `BindName`. A mechanism is also added to allow
both kinds of binding to be accessed uniformly, for convenience in the
case where the two different kinds of binding are treated the same.
Generic bindings of type `type` are allowed to be used as types,
although no operations are provided for such types. For now lowering
treats these types as empty structs, which seems like a reasonable
lowering for non-monomorphized unconstrained types.
This creates a namespace for `package` scope.
It looks like names of class_decls in namespaces lead to an unexpected
instref. This is already true, as best as I can tell. I'm not sure if
there's a preferred approach to address that, so I've left a TODO for
now.
This removes the filename from the file-scoped block, and places it
above to make it clear where the full SemIR begins (with multifile,
providing a barrier between).
Instead of ad-hoc conversion tracking on some kinds of nodes that
conversion creates, consolidate tracking into a single node kind. This
frees up an operand on `Init` instructions that can be used to store the
destination.
Add a `NameId` that is effectively just a wrapper around a `StringId`,
with
some additional predefined values for names that don't correspond to
strings, such as the name of `self` or the function's return slot.
This replaces the use of `VarStorage` in this case.
Add an `UnboundFieldType` type as the type of a field, in cases where
it's referenced without an accompanying object.
Add a `BindName` node to describe the name binding performed for both
variables and fields so that we can handle them more uniformly.
Incomplete types may be nested within other types; for example, a tuple
type might have an incomplete type as an element. Handle such cases by
walking through nested incomplete types when completing a type. This is
done non-recursively in case a very complex type is formed.
Types are generally no longer completed at the point where they're
formed. Instead, we attempt to complete a type when it is used in a
context that requires a complete type, and diagnose if the type cannot
be completed at that point. This will be necessary for classes, which
can become complete after their first use, and helps tease out bugs
where a type completeness check is missing.
Using the computed value representation, fix lowering of struct and
tuple values to use the value representation rather than the object
representation. Fixes an issue found in the review of #3257.
This currently causes us to compute value representations of all types
as they are created, which generates substantially more SemIR to
represent types. We can get some of that back by deferring computation
of the value representation until the type is required to be complete,
but some of the additional cost here will persist with this approach.
I also considered making the computation of the value representation
type be something that lives entirely within the lowering phase, but I
think that's not the right approach in the longer term, because the
value representation will be semantically visible and relevant once we
start allowing it to be customized.
We should consider moving the nodes that exist to compute canonical
non-local types, including value representations, out into a separate
global block. That will clean up the SemIR representation substantially,
and make the SemIR produced for a function not depend on which types we
happen to have encountered beforehand. But that's not being done in this
PR.
---------
Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Bug found by fuzzing. Problem was untyped SemIR nodes had an invalid
type id, which was retrieved by `HandlePrefixOperator` and then passed
to `context.GetUnqualifiedType`, ultimately performing an invalid access
in `semantics_ir_->GetNode`.
We prefer to make a placeholder type for functions and namespaces to
remove the need for checking for the untyped case everywhere. Eventually
functions will have their own types, but this approach will be needed
for namespaces (and perhaps other non-first-class entities like unbound
methods and interface members) long term.
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
A `let` declaration is represented by a `bind_name` node in SemIR:
```carbon-semir
%b: i32 = bind_name "b", %a
```
Because `Check` encounters the pattern before it sees the value, we
first create the `bind_name` node with an unset value and don't add it
to the block. Then, once we've seen and converted the initializer, we
update the `bind_name` to have the value and add it to the current
block, after the initializer code.