Compare commits

...
Author SHA1 Message Date
Christopher Di Bella f7cd39428e Add Destroy.SubobjectDestroy as a temporary replacement for Destroy.Op (#7773)
This change partially implements [PR #7362], which revises how objects
are destroyed. It is a partial implementation for two reasons:

1. This change moves `Destroy.Op`'s current behaviour into
`Destroy.SubobjectDestroy`, but it doesn't add support for objects with
non-trivial destruction.
2. `Destroy.SubobjectDestroy` is a workaround for `require impls
SubobjectDestroy`. We aren't able to use the latter until the dependents
add their requirements' implementations to their own witness tables.

[PR #7362]: https://github.com/carbon-language/carbon-lang/pulls/7362
2026-09-24 00:06:28 +00:00
Richard Smith 03939a9223 Add language server support for SemIR in testdata. (#7803)
Add hover cards and jump to declaration / definition / reference for the
formatted SemIR that appears in check tests. This is done by adding a
heuristic "parser" for SemIR to the language server. The
cross-references are strictly best-effort, since this is just a tool for
Carbon developers, not a user-facing facility.

A couple of other changes made along the way:

* file_test tests with an AUTOUPDATE-SPLIT no longer look for CHECK:
lines outside that split. This was motivated by the tests for this new
facility including CHECK: lines as part of the test input.
* An agent skill for working on the language server, tracking some
things that cost Claude time when working on this.

Assisted-by: Claude via Antigravity
2026-09-23 23:44:32 +00:00
Chandler Carruth 652e0ce7d0 Fix the jj_push.sh script to work on macOS (#7820)
It had a few subtle GNU extensions in it that didn't work on macOS.
There are simple portable alternatives so it was easy to adapt.

Assisted-by: Antigravity with Gemini
2026-09-23 22:47:02 +00:00
Dana Jansens c832d7c11c Skill file for reviewing filetest output changes (#7815)
When an LLM tool is used to prototype a change, one of the major tasks
it must undertake is validating filetest output changes. This skill
helps to ground that validation in some best practices and explain what
sorts of changes should or should not be expected, and how to judge
STDOUT vs STDERR changes.

Assisted-by: Opus 5
2026-09-23 19:18:59 +00:00
Dana Jansens 15e3eeaca9 Only look in facet types for name scopes when they have constraints (#7819)
This falls back to diagnosing that values of type `type` can not be used
for name lookup more consistently.
2026-09-23 17:50:34 +00:00
Richard Smith efbe1d2489 Add default fns and final fns to the eval block for a generic impl (#7817)
When a generic impl uses a default or final fn, it picks the specific
function value out of the interface to put in the witness table.
However, because this is done by modifying an existing instruction
block, the generics machinery has no hook to convert the function
constant into an attached constant, and because it was found in a
specific for a different generic, the constant inst will be unattached.
Fix this by manually mapping to an attached constant inst in the current
generic when building the witness table.
2026-09-23 01:19:28 +00:00
Lucile Rose Nihlen 53b7cfbaba Add basic caller-side support for default values in check (#7800)
Modifies the arity check to include a lower-bound for arguments.
Adds logic to pattern matching to supply default arguments for
missing parameters.
2026-09-22 22:07:35 +00:00
Dana Jansens 795729bb4a Fix git path globs in summarize testdata changes SKILL (#7818)
When there's a wildcard in a path, git treats the path as matching
exactly, unless the path also ends in a wildcard. So
`toolchain/*/testdata` only matches the testdata directory names,
whereas `toolchain/*/testdata/*` matches all the files under them.
2026-09-22 17:23:11 +00:00
Dana Jansens fcae9610bd Make SemIR::TypeType be an empty FacetType instruction (#7813)
The type `type` is now a `FacetType` inst with no constraints. This
brings the model implemented in the toolchain into better alignment with
the language design. The `SemIR::TypeType` struct remains as a scope for
holding the `TypeInstId`, `ConstantId`, and `TypeId` constants, but is
not an `InstKind` anymore.

The `TypeType` inst looks a lot like singletons, but there are many
`FacetType` insts so it doesn't quite fit that model. So we put it
alongside singletons with a fixed inst id but refer to it as a more
general "builtin" inst that is not a singleton.
`Namespace::PackageInstId` is similar, and we group it with `TypeType`
conceptually as another builtin instruction with a fixed id.

No conversion is needed anymore to use a `type` as a facet, since types
also have a `FacetType` type. This simplifies and removes a number of
helpers and branches throughout the code.

The `TypeType` inst is now part of the constant store, so we end up
printing it in the constants block in every test. But it's also named
`type` rather than `%type` to preserve the majority of existing
formatting behaviour, though this does look different from other
constants.

Assisted-by: Opus 5 was used to generate a first draft and validate the
refactoring. Though nearly everything non-trivial the tool wrote has
been modified or rewritten.
2026-09-22 15:04:40 +00:00
Lucile Rose Nihlen 83ca5b71e3 Move default value inst ids to a dedicated value store. (#7810)
Per #7737 we move the default value `InstId` storage from
a block in the `SemIR::Function` data structure to a
`SemIR::File` scoped `ValueStore`.

Moves the default value consistency checking to the general
merge argument pattern matching logic, which changes the
error message issued to the generic one.
2026-09-21 18:27:01 +00:00
Dana Jansens fa12d9ded2 Skill file docs about how to write prose, names, and to prioritize the data model (#7814)
Largely about writing less, and defining what is worth talking about and
what is not.
2026-09-21 17:48:35 +00:00
Richard Smith 76e7fc5900 Preserve the type-as-written for bindings and use it in diagnostics. (#7804)
In the `EntityName` for a binding, preserve the `TypeInstId` describing
how the type was written. When a diagnostic refers to that type via
`TypeOfInstId`, use the type-as-written in the diagnostic rather than
the canonical type.

Assisted-by: Claude Opus via Antigravity
2026-09-19 00:02:49 +00:00
Chandler Carruth d037848a96 Replace hashtable ForEach callback with range-based iteration (#7806)
Replaces the callback-based `ForEach` methods on `RawHashtable`, `Map`,
and
`Set` with a range object supporting range-for loops, structured
bindings, and
the standard range concepts.

- Adds `.entries()` on `Map`, `Set`, and `RawHashtable`, returning a
range that
  models `std::ranges::forward_range` and `std::ranges::common_range`.
  Obtaining one is an explicit call rather than `begin()`/`end()` on the
container, as scanning a whole table is costly and shouldn't be hidden.
- Iterating a `Map` yields a `std::pair` of key and value references,
which
fits in two registers and is returned without being materialized in
memory.
- `Map::Range` and `Set::Range` are aliases of the raw hashtable's range
rather
than wrappers around it. The raw iterator produces the user-facing
reference
itself -- a `KeyT&` for a set, a pair of references for a map -- picked
by
`StorageEntry`, which is already specialized on whether there is a value
  type. That leaves one iterator to reason about instead of three.
- Deletes the rvalue `.entries()` overloads on the owning containers, as
a
  range built from a temporary table would dangle. Views don't own their
  storage, so the operation remains available on them.
- In release builds, the walk over the groups is a single induction
variable: a
  negative byte offset counting up to zero, anchored at the ends of the
metadata and entry arrays. Both arrays are then reached by indexed
addressing
off a base that stays put, and the entry pointer is formed only once a
group
  with a present entry has been found.
- In debug builds, the range hashes the table's metadata when it is
built and
re-checks that hash when it is destroyed, catching mutation of the table
while a range is live. It also picks a random starting group and a
random odd
group stride, which varies the traversal order between ranges while
still
visiting every group exactly once. That entropy is drawn when the range
is
built rather than in `begin()`, so `begin()` stays a pure function of
the
  range and the multi-pass guarantee holds.
- Removes `ForEachEntry` and all of its callers.

Measured against the iteration benchmark added in its own commit, a
traversal is at or ahead of what the callback compiled to across nearly
the
whole size range. The largest tables spend 3-5% fewer cycles, small
`Set`s as
much as 24% fewer, and instruction counts stay within about 1%. What
remains
behind is a handful of mid-sized `Map`s by up to 1%, and `Set` at 65536,
which
sits at exactly half its load factor, by 2%.

Both revisions were built with `-c opt --copt=-march=x86-64-v3` and
compared
with:

```
./scripts/bench_runner.py --exp_benchmark=... --base_benchmark=... \
    --benchmark_args=--benchmark_perf_counters=INSTRUCTIONS,CYCLES \
    --benchmark_args='--benchmark_filter=(Set|Map)Iterate<(Set|Map)<' \
    --extra_metrics_filter='(INSTRUCTIONS|CYCLES)'
```

Trimmed below to the primary integer configurations and to the two
counters;
the pointer- and string-keyed configurations follow the same pattern.

```
 Benchmark                             ┃           CYCLES            ┃        INSTRUCTIONS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 BM_MapIterate<Map<int, int>>/1....... │ 👍  -6.032%      p=1.14e-05 │      ??          p=0.752
                             baseline: │     12.06      ±   1.520%   │     64         ±   3.125%
                           experiment: │     11.33      ±   2.765%   │     65.5       ±   3.817%
                                       │                             │
 BM_MapIterate<Map<int, int>>/2....... │      ??          p=0.155    │      ??          p=0.343
                             baseline: │      7.587     ±   1.285%   │     41         ±   0.000%
                           experiment: │      7.652     ±   0.865%   │     41         ±   2.439%
                                       │                             │
 BM_MapIterate<Map<int, int>>/3....... │      ??          p=0.343    │ 👍  -1.020%      p=0.0039
                             baseline: │      6.663     ±   4.260%   │     32.67      ±   2.041%
                           experiment: │      6.368     ±  12.224%   │     32.33      ±   2.062%
                                       │                             │
 BM_MapIterate<Map<int, int>>/4....... │      ??          p=0.343    │ 👍  -1.786%      p=0.0297
                             baseline: │      6.091     ±  15.470%   │     28         ±   3.571%
                           experiment: │      5.957     ±   8.932%   │     27.5       ±   3.636%
                                       │                             │
 BM_MapIterate<Map<int, int>>/8....... │      ??          p=0.323    │ 👍  -1.220%      p=0.000148
                             baseline: │      4.845     ±   0.800%   │     20.5       ±   0.000%
                           experiment: │      4.814     ±   3.585%   │     20.25      ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/16...... │ 👍  -2.195%      p=0.00908  │ 👍   0.769%      p=6.58e-06
                             baseline: │      4.312     ±   0.187%   │     16.25      ±   0.000%
                           experiment: │      4.218     ±   2.368%   │     16.13      ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/32...... │      ??          p=0.236    │ 👍   0.442%      p=9.53e-06
                             baseline: │      4.051     ±   1.084%   │     14.13      ±   0.000%
                           experiment: │      4.063     ±   0.737%   │     14.06      ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/64...... │      ??          p=0.693    │ 👎   0.227%      p=4.52e-06
                             baseline: │      4.021     ±   0.239%   │     13.75      ±   0.000%
                           experiment: │      4.019     ±   0.417%   │     13.78      ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/256..... │ 👍   0.360%      p=0.00119  │ 👎   0.754%      p=1.37e-05
                             baseline: │      3.996     ±   0.173%   │     13.47      ±   0.000%
                           experiment: │      3.982     ±   0.272%   │     13.57      ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/4096.... │ 👍   0.581%      p=1.96e-05 │ 👎   0.923%      p=1.96e-05
                             baseline: │      4.005     ±   0.816%   │     13.38      ±   0.000%
                           experiment: │      3.981     ±   0.192%   │     13.5       ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/65536... │ 👍  -4.957%      p=1.14e-05 │ 👎   0.934%      p=1.14e-05
                             baseline: │      5.307     ±   0.501%   │     13.38      ±   0.000%
                           experiment: │      5.044     ±   1.746%   │     13.5       ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/1048576. │ 👍  -3.947%      p=9.09e-05 │ 👎   0.935%      p=3.3e-05
                             baseline: │      6.074     ±   0.807%   │     13.38      ±   0.000%
                           experiment: │      5.834     ±   2.159%   │     13.5       ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/16777216 │      ??          p=0.155    │ 👎   0.935%      p=2.11e-05
                             baseline: │      5.082     ±   3.650%   │     13.38      ±   0.000%
                           experiment: │      5.012     ±   1.316%   │     13.5       ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/56...... │ 👎   0.825%      p=0.0268   │ 👍   0.270%      p=1.14e-05
                             baseline: │      3.918     ±   0.501%   │     13.21      ±   0.000%
                           experiment: │      3.951     ±   0.342%   │     13.18      ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/224..... │ 👎   0.788%      p=0.000504 │ 👎   0.346%      p=1.64e-05
                             baseline: │      3.895     ±   0.111%   │     12.89      ±   0.000%
                           experiment: │      3.926     ±   0.285%   │     12.94      ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/3584.... │ 👎   1.028%      p=0.000148 │ 👎   0.545%      p=1.14e-05
                             baseline: │      3.913     ±   0.427%   │     12.79      ±   0.000%
                           experiment: │      3.954     ±   0.325%   │     12.86      ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/57344... │      ??          p=0.236    │ 👎   0.558%      p=2.55e-06
                             baseline: │      4.574     ±   0.721%   │     12.79      ±   0.000%
                           experiment: │      4.51      ±   3.709%   │     12.86      ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/917504.. │ 👍  -3.826%      p=6.58e-06 │ 👎   0.559%      p=2.33e-05
                             baseline: │      5.221     ±   0.507%   │     12.79      ±   0.000%
                           experiment: │      5.021     ±   0.556%   │     12.86      ±   0.000%
                                       │                             │
 BM_MapIterate<Map<int, int>>/14680064 │ 👍  -3.839%      p=1.37e-05 │ 👎   0.559%      p=3.31e-05
                             baseline: │      5.129     ±   1.194%   │     12.79      ±   0.000%
                           experiment: │      4.932     ±   1.475%   │     12.86      ±   0.000%
                                       │                             │

 Benchmark                        ┃           CYCLES            ┃        INSTRUCTIONS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 BM_SetIterate<Set<int>>/1....... │ 👍  -3.104%      p=0.000583 │      ??          p=0.206
                        baseline: │     11.2       ±   6.323%   │     60         ±   3.333%
                      experiment: │     10.85      ±   5.820%   │     61         ±   3.279%
                                  │                             │
 BM_SetIterate<Set<int>>/2....... │ 👍  -7.037%      p=0.0362   │      ??          p=0.155
                        baseline: │      7.086     ±  16.857%   │     37         ±   0.000%
                      experiment: │      6.587     ±   0.479%   │     36         ±   4.167%
                                  │                             │
 BM_SetIterate<Set<int>>/3....... │ 👎   1.400%      p=2.34e-05 │ 👍  -1.163%      p=0.00136
                        baseline: │      5.363     ±   0.463%   │     28.67      ±   2.326%
                      experiment: │      5.438     ±  32.763%   │     28.33      ±   1.176%
                                  │                             │
 BM_SetIterate<Set<int>>/4....... │      ??          p=0.968    │      ??          p=0.286
                        baseline: │      4.642     ±  32.751%   │     23.5       ±   2.128%
                      experiment: │      4.658     ±  38.416%   │     23.63      ±   3.704%
                                  │                             │
 BM_SetIterate<Set<int>>/8....... │ 👍 -23.823%      p=3.74e-06 │ 👍  -1.515%      p=5.52e-05
                        baseline: │      4.701     ±   6.589%   │     16.5       ±   0.000%
                      experiment: │      3.581     ±   7.790%   │     16.25      ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/16...... │ 👍  -4.502%      p=1.37e-05 │ 👍  -1.020%      p=3.31e-05
                        baseline: │      3.124     ±   0.585%   │     12.25      ±   0.000%
                      experiment: │      2.983     ±   0.625%   │     12.13      ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/32...... │ 👍  -4.032%      p=5.46e-06 │ 👍   0.617%      p=1.96e-05
                        baseline: │      2.957     ±   0.260%   │     10.13      ±   0.000%
                      experiment: │      2.838     ±   0.434%   │     10.06      ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/64...... │ 👍  -5.054%      p=4.52e-06 │ 👎   0.321%      p=1.37e-05
                        baseline: │      2.937     ±   0.301%   │      9.75      ±   0.000%
                      experiment: │      2.788     ±   1.143%   │      9.781     ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/256..... │ 👍  -5.325%      p=1.14e-05 │ 👎   1.073%      p=6.58e-06
                        baseline: │      2.916     ±   0.220%   │      9.469     ±   0.000%
                      experiment: │      2.761     ±   0.142%   │      9.57      ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/4096.... │ 👍  -4.865%      p=4.52e-06 │ 👎   1.317%      p=2.34e-05
                        baseline: │      2.921     ±   0.194%   │      9.381     ±   0.000%
                      experiment: │      2.779     ±   0.224%   │      9.504     ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/65536... │ 👎   1.961%      p=3.93e-05 │ 👎   1.332%      p=1.49e-05
                        baseline: │      4.015     ±   0.482%   │      9.375     ±   0.000%
                      experiment: │      4.094     ±   0.613%   │      9.5       ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/1048576. │ 👍  -4.843%      p=1.14e-05 │ 👎   1.333%      p=5.38e-06
                        baseline: │      5.239     ±   0.144%   │      9.375     ±   0.000%
                      experiment: │      4.986     ±   0.139%   │      9.5       ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/16777216 │ 👍   0.840%      p=0.0362   │ 👎   1.333%      p=2.52e-06
                        baseline: │      3.719     ±   1.420%   │      9.375     ±   0.000%
                      experiment: │      3.688     ±   1.308%   │      9.5       ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/56...... │ 👍  -2.857%      p=9.53e-06 │ 👍   0.388%      p=3.31e-05
                        baseline: │      2.942     ±   0.439%   │      9.214     ±   0.000%
                      experiment: │      2.858     ±   0.619%   │      9.179     ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/224..... │ 👍  -2.161%      p=2.34e-05 │ 👎   0.502%      p=4.52e-06
                        baseline: │      2.888     ±   0.347%   │      8.893     ±   0.000%
                      experiment: │      2.826     ±   0.450%   │      8.938     ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/3584.... │ 👍  -1.750%      p=6.58e-06 │ 👎   0.793%      p=2.34e-05
                        baseline: │      2.89      ±   0.261%   │      8.792     ±   0.000%
                      experiment: │      2.84      ±   0.411%   │      8.862     ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/57344... │      ??          p=0.502    │ 👎   0.812%      p=2.78e-05
                        baseline: │      3.684     ±   4.246%   │      8.786     ±   0.000%
                      experiment: │      3.644     ±   4.431%   │      8.857     ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/917504.. │ 👍  -2.629%      p=0.000148 │ 👎   0.813%      p=3.08e-06
                        baseline: │      4.372     ±   0.693%   │      8.786     ±   0.000%
                      experiment: │      4.257     ±   0.210%   │      8.857     ±   0.000%
                                  │                             │
 BM_SetIterate<Set<int>>/14680064 │ 👍  -2.927%      p=0.0219   │ 👎   0.813%      p=3.03e-06
                        baseline: │      4.154     ±   3.286%   │      8.786     ±   0.000%
                      experiment: │      4.032     ±   3.198%   │      8.857     ±   0.000%
                                  │                             │
```

Assisted-by: Antigravity with Opus
2026-09-18 20:19:46 +00:00
Chandler Carruth 681b3b10c4 Add a jj push wrapper that runs prek before pushing. (#7805)
`scripts/jj_push.sh` takes the same arguments as `jj git push`, runs
prek over the commits that push would send, and pushes only if they
pass. It learns what is being sent by running `jj git push --dry-run`
and reading back the plan, so `--bookmark`, `--change`, `--all` and the
rest work without reimplementing how they select commits.

Hooks that rewrite files need a commit to write into, so the checks run
with the working copy on top of the commit being pushed. When the
working copy is already an empty commit there, which is the common case,
it is used directly; otherwise one is created, and named in the error so
the fixes can be squashed.

`scripts/jj_prek.sh` gets two changes. It forwards its arguments to
`prek run`, so `jj_push.sh` can ask for a specific range, and it now
changes to the workspace root before running. It exports `GIT_DIR`,
which makes git treat the current directory as the work tree, so prek
could not find its configuration from a subdirectory.

`jj` does not expand aliases when completing arguments, so `jj push`
completed file names. `scripts/completions` has Bash, Zsh, and Fish
completions that give it the same completions as `jj git push`.

`docs/project/contribution_tools.md` documents the `push` alias, and a
`prek` alias for `jj_prek.sh`, with the other per-repository `jj`
configuration. Both are opt-in.

Assisted-by: Claude Code
2026-09-18 19:40:25 +00:00
Nicholas Bishop dbf79d5229 Fix accessing protected members from templates derived classes (#7780)
When evaluating a deferred member access action, the scope stack cannot
be relied on, so `LookupUnqualifiedName` cannot be used in
`GetHighestAllowedAccess` to get the `Self` type.

Instead, store the `Self` type in the `Context` when evaluating a
method, and use that in `GetHighestAllowedAccess`.
2026-09-18 19:12:05 +00:00
Dana Jansens 413ac55d4f Add rules for working with jj history and prek (#7808)
Add rules to not overwrite git/jj history without asking, since this
destroys the reviewer's view of things. And some information on dealing
with stacks of commits within a single bookmark/PR.

Prek can make fixes for whatever caused a failure, and then pass when
you run it again, even though the user didn't change anything, and that
is now explained.

Assisted-by: Opus 5
2026-09-18 18:36:58 +00:00
ATHARVAandDana Jansens 804359dce0 Fix crash when lowering Carbon derived class with C++ virtual base class (#7745)
If a Carbon class overrides virtual functions from a C++ base class but
is never referenced from C++, it is never exported to Clang. During
lowering, `BuildVtable` then fails to find a `CXXRecordDecl` and crashes
when attempting to get the vtable from Clang's code generator.

Ensure dynamic classes with foreign vtables are exported to Clang when
completing the class definition in `CheckCompleteClassType`, and look up
`first_decl_id()` in `BuildVtable`.

Fixes #7721

---------

Co-authored-by: Dana Jansens <danakj@orodu.net>
2026-09-18 18:08:53 +00:00
Dana Jansens d84387f8b8 Add --build-mode to autoupdate script (#7809)
Allow the user to specify a build mode instead of detecting the last
used one.
2026-09-18 15:15:26 +00:00
David Blaikie 094742740a Handle instantiating an imported class/vtable (#7792)
Usual LoadImportRef, plus some generalization of importing entities.
2026-09-17 23:50:28 +00:00
Lucile Rose Nihlen 994bad5143 Use non-canonical instructions in default values. (#7737)
Per feedback on #7665, this PR switches the default value
table storage from canonical constant inst_ids to
non-canonical.

Furthermore, this PR simplifies the default value support in
check by requiring that the first owned declaration of a
function completely specify all of its default values.
Updates the diagnostic code and tests to reflect this new
stricter requirement.
2026-09-17 22:04:43 +00:00
Chandler Carruth ae4be1be14 Use inline small storage for small SemIR ID sets in toolchain (#7796)
Apply SmallSize = 16 to frequent identifier and instruction/function
sets in ScopeStack, Class, FacetType, and SpecificCoalescer to avoid
dynamic heap allocations on small scopes. Also defer dest_field_names
set allocation in struct conversion and provide default KeyContext for
SetBase. This was found by inspection, but does seem to be a clear 1.5%
win on overall compile time.

```
Ran baseline and experiment 10 times on 128 x 2450 MHz CPUs
CPU caches:
  L1 Data 32Ki
  L1 Instruction 32Ki
  L2 Unified 512Ki
  L3 Unified 32Mi
Load avg: 1.2041 1.16895 2.86133
Computing statistically significant deltas only wherethe P-value < 𝛂 of 0.05
Metric key:
   BenchmarkName... 👍 <delta>    p=<U-test P-value>
          baseline:    <median> ± <% at 95th conf>
        experiment:    <median> ± <% at 95th conf>

 Benchmark                                                      ┃          CPU Time          ┃           CYCLES           ┃       INSTRUCTIONS        ┃          Lines
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━
 BM_CompileApiFileDenseDecls<Lang::Carbon, Phase::Check>/256... │ 👍  -1.768%      p=0.0346  │ 👍  -1.683%      p=0.0346  │ 👍   0.160%    p=0.000428 │ ~
                                                      baseline: │     49.11  ms  ±   1.363%  │    155.6   M   ±   1.875%  │    293.9   M ±   0.015%   │     4.093 k ±   1.360%
                                                    experiment: │     48.24  ms  ±   2.213%  │    153     M   ±   2.465%  │    293.4   M ±   0.081%   │     4.166 k ±   2.165%
                                                                │                            │                            │                           │
 BM_CompileApiFileDenseDecls<Lang::Carbon, Phase::Check>/1024.. │      ??          p=0.159   │      ??          p=0.067   │ 👍   0.153%    p=0.000249 │ ~
                                                      baseline: │     50.61  ms  ±   2.379%  │    160.7   M   ±   2.777%  │    309.7   M ±   0.015%   │    19.96  k ±   2.326%
                                                    experiment: │     50.32  ms  ±   0.971%  │    159.6   M   ±   0.787%  │    309.2   M ±   0.086%   │    20.07  k ±   0.980%
                                                                │                            │                            │                           │
 BM_CompileApiFileDenseDecls<Lang::Carbon, Phase::Check>/4096.. │ 👍  -1.593%      p=0.0112  │ 👍  -1.632%      p=0.00743 │ 👍   0.138%    p=0.000328 │ ~
                                                      baseline: │     58.58  ms  ±   1.673%  │    186.5   M   ±   1.487%  │    371.2   M ±   0.102%   │    70.96  k ±   1.645%
                                                    experiment: │     57.65  ms  ±   1.032%  │    183.5   M   ±   1.079%  │    370.7   M ±   0.091%   │    72.11  k ±   1.043%
                                                                │                            │                            │                           │
 BM_CompileApiFileDenseDecls<Lang::Carbon, Phase::Check>/16384. │ 👍  -1.695%      p=0.029   │ 👍  -1.823%      p=0.0411  │ 👍   0.221%    p=0.000428 │ ~
                                                      baseline: │     90.07  ms  ±   3.686%  │    287.1   M   ±   3.694%  │    618.9   M ±   0.106%   │   186.9   k ±   3.555%
                                                    experiment: │     88.55  ms  ±   1.891%  │    281.8   M   ±   2.142%  │    617.6   M ±   0.158%   │   190.1   k ±   1.856%
                                                                │                            │                            │                           │
 BM_CompileApiFileDenseDecls<Lang::Carbon, Phase::Check>/65536. │ 👍  -1.797%      p=0.0201  │ 👍  -1.762%      p=0.0201  │ 👍   0.222%    p=0.000328 │ ~
                                                      baseline: │    222.1   ms  ±   2.560%  │    711     M   ±   2.417%  │      1.613 G ±   0.158%   │   304.2   k ±   2.496%
                                                    experiment: │    218.1   ms  ±   1.950%  │    698.5   M   ±   2.010%  │      1.61  G ±   0.075%   │   309.7   k ±   1.989%
                                                                │                            │                            │                           │
 BM_CompileApiFileDenseDecls<Lang::Carbon, Phase::Check>/262144 │      ??          p=0.398   │      ??          p=0.36    │ 👍   0.166%    p=0.000931 │ ~
                                                      baseline: │    782.2   ms  ±   2.666%  │      2.502 G   ±   2.546%  │      5.596 G ±   0.038%   │   345.8   k ±   2.597%
                                                    experiment: │    780.6   ms  ±   4.249%  │      2.483 G   ±   4.691%  │      5.587 G ±   0.024%   │   346.5   k ±   4.075%
                                                                │                            │                            │                           │
```

Assisted-by: Antigravity with Gemini
2026-09-17 15:49:43 +00:00
Chandler Carruth e65694db57 Narrow the benchmark test runs for set and benchmark (#7795)
Hopefully this reduces the occurances of timeouts on GitHub, and it
shouldn't reduce coverage meaningfully.
2026-09-17 12:42:57 +00:00
Chandler Carruth 5c601f8224 Make the filesystem benchmark test less expensive (#7794)
While some of the slowness here is unrelated, there isn't really any
reason to test even as much of the benchmark as it is.
2026-09-17 12:41:59 +00:00
1054 changed files with 68050 additions and 24703 deletions
+81
View File
@@ -33,6 +33,87 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- **Python**: Use `pre-commit run black --files <file.py>` to format Python
files.
## Comments
- **Describe the code that is there.** A comment should explain what the
current code does or why it is the way it is, not narrate what the code used
to be. Do not add a comment to justify a deletion or explain that something
is no longer necessary; a reader of the new code has no idea what is being
contrasted against, and the comment rots as soon as the old shape is
forgotten. Put that reasoning in the commit message instead.
- **Do not introduce a local variable just to host a comment.** If a call
argument reads clearly on its own, inline it rather than naming it so that a
comment has somewhere to attach.
- **Lead with a plain summary.** Start a doc comment with a direct statement
of what the function does or returns, such as "Returns true if the InstKind
is a singleton." Put nuance in a following sentence rather than qualifying
the summary into something harder to read.
- **Re-read a comment before updating it.** When a symbol is renamed or
changes meaning, a comment that mentions it is not automatically stale. Work
out what the comment actually asserts first: it may be describing what the
code _uses_, which is still true, and rewriting it loses information.
## Documentation
This covers standalone prose: `/docs`, `README.md` files, and the skill files
under `.agents/skills`.
- **Be true of the tree it lands in.** Documentation shipping in the same
commit as the code it describes should name that code freely. Documentation
that lands separately must not: a reader who greps for an identifier from an
unlanded change finds nothing, and a claim that only holds after that change
is false until it lands. This is easy to get wrong when writing up a lesson
while the change that taught it is still in flight.
- **Use placeholders for illustrations.** When an example only needs to show a
shape, name it `Foo` rather than reaching for a real symbol. Save real
identifiers for documenting that identifier, where going stale is at least
detectable.
- **Make each point stand alone.** A reader has none of the discussion that
produced it. State the rule, and enough of the reason to apply it, without
assuming knowledge of the change that motivated it.
## Naming
- **A name is a claim, so keep it true.** When a change invalidates the
invariant a name describes, rename it in the same change. A factory called
`MakeSingletonFooId` has to be renamed once `Foo` is no longer a singleton,
even though nothing forces you to.
- **Types in a signature are part of the claim.** A function returning the id
of a namespace should return `InstId`, not `TypeInstId`, because a namespace
is not a type. Do not let a convenient wider or narrower id type imply
something false.
- **Delete a predicate whose name stops distinguishing anything.** If a change
widens a test so that it no longer says what its name implies, remove it and
let callers use the underlying test, rather than keeping a wrapper that
sounds meaningful. What callers actually want is often the inverse, and is
worth adding under its own name.
- **Name a variable for its role, not its representation.** If the role a
value plays is unchanged, keep its name even when a change means it is now
held or spelled differently.
## Commit descriptions
- **Say what the change is, not how you made it.** Describe the resulting
code. Only describe process when the process is more interesting than the
change, as with a large mechanical transformation.
- **Do not narrate your own work.** Statements like "each such site was
audited" or "we investigated every caller" describe effort, not the change.
- **Omit routine mechanical steps.** Do not mention running the testdata
autoupdater or the formatter. Every change is expected to include those.
- **Do not enumerate each edit.** Describe the change as a whole rather than
listing every function or file touched; the diff already lists them.
- **Use the project's terms precisely.** Reach for the term of art the
codebase uses. Calling something a "type alias" or a "namespace" when it is
a named scope misleads, and is worse than a vaguer but accurate word.
- **Scope claims to what changed.** Say the specific thing that is now true,
rather than a broader statement that happens to contain it.
## Design
- **Do not distort the data model to improve output.** If printed or golden
output is undesirable, change the printer, not the data structures that
feed it.
## Style Guides
- **C++ style**: Follow the
+58
View File
@@ -42,3 +42,61 @@ blocking or waiting for terminal paging.
and `jj new`.
- **Abandon/discard current changes**: `jj --no-pager abandon`
- **Rebase current change onto trunk**: `jj --no-pager rebase -o trunk`
### Working with a stack of changes
A change is often built as a stack of commits sent up as a single pull request.
The stack is not necessarily based on `trunk`; it may be based on another change
that is itself still in flight.
> [!WARNING] **Never rewrite the history of a change that has been submitted as
> a pull request.** Reviewers track a PR by its commits, and squashing,
> reordering, or abandoning them discards review that is already in progress.
> This cannot be undone from their side.
>
> Before rewriting history in any other case, propose the exact command and wait
> for confirmation. This applies to `squash`, `rebase`, `abandon`, and
> `describe` on an existing change.
#### Finding the base of the stack
Bookmarks delimit the stack. List the bookmarks that are ancestors of the
working copy, nearest first:
```bash
jj --no-pager log -r '::@ & bookmarks()'
```
Reading the result takes care, because two situations produce similar output:
- **Editing an existing change.** The nearest bookmark names the change being
worked on, and the bookmark below it is the base.
- **Starting a new change.** The commits above the nearest bookmark have no
bookmark of their own yet, so the nearest bookmark is itself the base.
`trunk` is only ever a base. Finding `trunk` nearest means new work is being
built on top of it, never that `trunk` itself is being worked on.
The graph does not distinguish the two cases: an unbookmarked or empty commit
above a bookmark may be the next commit of that change or the start of a new
one. Ask which it is when it is not clear, and ask before choosing where a fix
should land rather than after. Guessing wrong means squashing into a change that
may already be under review.
Once the base is known, use it to scope commands to the current stack:
```bash
jj --no-pager log -r '<base-bookmark>..@'
```
#### Managing the stack
- **Fold a fix into an earlier change**:
`jj --no-pager squash --into <change-id> [path]`. Follow-up fixes and
formatter reflows belong in the change that introduced the code, not in a
trailing "fixes" commit, unless that change has already been submitted.
Naming a path squashes only that part of the working copy, leaving unrelated
work in place.
- **Descriptions**: only one change in the stack needs a long description, the
one used as the pull request description. Every other change gets a short
one-line summary. Do not repeat the long text across the stack.
+197
View File
@@ -0,0 +1,197 @@
---
name: Language server
description:
Instructions for working on Carbon's LSP language server, including its
architecture, its file_test-based tests, and the VS Code extension.
---
# Language server
<!--
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
-->
## Introduction
This skill covers [`toolchain/language_server/`](/toolchain/language_server/),
which implements `carbon language-server`, and
[`utils/vscode/`](/utils/vscode/), the VS Code extension that launches it.
## Architecture
The server is built on clangd's LSP transport (`clang::clangd`), not on a
Carbon-specific one. That means clangd's `Protocol.h` types (`Position`,
`Range`, `Location`, `Hover`, `MarkupContent`) are the interface currency.
- `server.cpp` / `incoming_messages.cpp`: message dispatch. A handler must be
registered in `incoming_messages.cpp` before it can be called.
- `handle_*.cpp`: one file per request family, each declaring its entry point
in `handle.h`.
- `handle_initialize.cpp`: the advertised capabilities. **Adding a capability
changes `Content-Length` in every test that calls `initialize`**, so expect
a large autoupdate diff.
- `context.h` / `context.cpp`: `Context::File` per open document, plus the
compile driver. `Context::File::unit()` has a `CARBON_CHECK` on the compile
driver, so any handler that reaches for the parse tree must first rule out
documents that were never compiled.
- `position.h`, `sem_ir_index.h`: mapping source positions to SemIR
instructions for real Carbon files.
- `sem_ir_text.h`, `handle_sem_ir_text.h`: navigation within the *formatted
SemIR* in a test file's `// CHECK:STDOUT:` lines. This is a heuristic text
index, deliberately independent of the real SemIR data structures. See
[the SemIR text reader](#the-semir-text-reader).
### Document kinds
The server handles two kinds of document, distinguished by the `languageId`
from `textDocument/didOpen`, with a content sniff as a fallback:
- `carbon`: a real Carbon file. Compiled; diagnostics published.
- `carbon-testdata`: a test file. **Not compiled**, because we lack logic to
split it into one file per `// ---` split marker.
> [!IMPORTANT]
> A handler that assumes every file was compiled will crash on a test file.
> When adding one, give the test-file path an explicit early return.
## Tests
There is **no language-server-specific test target**.
`toolchain/language_server/BUILD` only declares
`filegroup(name = "testdata")`, which is pulled into
`//toolchain/testing:all_testdata` and run by `//toolchain/testing:file_test`.
```bash
# Run just the language server tests (or any subset).
bazelisk test //toolchain/testing:file_test \
--test_arg=--file_tests=toolchain/language_server/testdata/position/hover_and_goto.carbon
# See the raw output, which is much easier to read than a test failure.
bazelisk run //toolchain/testing:file_test -- --dump_output \
--file_tests=toolchain/language_server/testdata/position/hover_and_goto.carbon
# Update expectations. Never hand-write CHECK lines.
./toolchain/autoupdate_testdata.py toolchain/language_server/testdata/...
```
These tests run serially, because clangd's logging is a global singleton.
### Test file shape
The request stream is a `// --- STDIN` split written with the `[[@LSP-*]]`
keywords, and the responses land in a trailing `// --- AUTOUPDATE-SPLIT`.
Documents come from other splits by way of `"text": "FROM_FILE_SPLIT"`, which
is substituted with the content of the split whose name matches the `uri`.
```carbon
// --- position.carbon
fn Abs(n: i32) -> i32 { return n; }
// --- STDIN
[[@LSP-CALL:initialize:"capabilities": {}]]
[[@LSP-NOTIFY:textDocument/didOpen:
"textDocument": {
"uri": "file:/position.carbon",
"languageId": "carbon",
"text": "FROM_FILE_SPLIT"
}
]]
[[@LSP-CALL:textDocument/hover:
"textDocument": {"uri": "file:/position.carbon"},
"position": {"line": 0, "character": 3}
]]
[[@LSP-CALL:shutdown]]
[[@LSP-NOTIFY:exit]]
// --- AUTOUPDATE-SPLIT
```
Full keyword documentation is in
[`testing/file_test/README.md`](/testing/file_test/README.md).
### Traps
> [!WARNING]
> **A blank line inside the `STDIN` split breaks the JSON transport.** It
> terminates a header block, so clangd logs a timestamped
> `Warning: Missing Content-Length header, or zero-length message.` The
> timestamp makes the test unreproducible, so it fails on the next run.
> Comment lines between messages are fine; blank lines are not. A single blank
> line immediately before `// --- AUTOUPDATE-SPLIT` is also fine.
Other things worth knowing:
- **`positionEncoding` is UTF-16.** A `character` is a UTF-16 code unit
offset, not a byte offset.
- **Line and character numbers in requests are 0-based**, while the `locN_M`
suffixes in SemIR output are 1-based. Off-by-ones here are silent: the
request succeeds and returns the wrong thing.
- **A split can hold a document that itself contains `// CHECK:STDOUT:`
lines**, because `CHECK` lines only form expectations inside the
`AUTOUPDATE-SPLIT`. Such a document still can't contain a literal `// ---`
line, which would split the enclosing test file; write it as
`[[@0x2f]]/ --- name.carbon`.
## The SemIR text reader
`sem_ir_text.cpp` indexes the formatted SemIR inside a test file's
`// CHECK:STDOUT:` lines so that hover and go-to-definition work on operand
names. It is a heuristic reader, not a parser, and its correctness rests on
facts about `toolchain/sem_ir/formatter.cpp` and
`toolchain/sem_ir/inst_namer.cpp`. Re-check these if the formatter changes:
- There are exactly four scope keywords: `file`, `generated`, `imports`, and
`constants` (`InstNamer::GetScopeName`). Everything else is `@entityname`.
- A reference is `%name` within its own scope and `scope.%name` otherwise
(`InstNamer::GetNameFor`). Names may contain `.` and may _start_ with one,
as in `%.Self.frozen`.
- **Type annotations are printed in the `constants` scope.**
`Formatter::FormatTypeOfInst` does
`llvm::SaveAndRestore file_scope(scope_, InstNamer::ScopeId::Constants)`,
so in `%x: %foo = ...` a bare `%foo` means `constants.%foo`. This does not
apply to ordinary operands or to `[concrete = ...]` annotations.
- **`*_decl` braces hold the declared entity's scope.** The braces of
`%F.decl: ... = fn_decl @F [...] { ... } { ... }` are lexically inside
`file { }`, but their names belong to `@F`.
- **`!with Self:` switches scope without a brace**, until `!members:`
switches it back. Brace counting alone cannot see this.
- A `specific @F(args) { }` block uses `@F`'s scope and defines nothing; each
`%name => value` row references an instruction of the generic.
### Validating a change to the reader
The unit tests only cover a handful of cases. To check a change against the
real corpus, drive the server over a sample of check testdata, hovering on
every `%name`, and compare the resolved fraction before and after. Roughly 99%
of names resolve; the residue are names the formatter references but never
emits a definition line for, such as `%I.WithSelf.F`, which only ever appears
inside a `[symbolic = ...]` annotation.
Two things to get right in such a harness:
- Feed the request stream from a **file**, not a pipe. The server reads stdin
as a file and reports `error: Input/output error` on a pipe.
- Read the output as **bytes**. Python's `text=True` rewrites the `\r\n`
framing, and `Content-Length` counts bytes.
## VS Code extension
[`utils/vscode/`](/utils/vscode/) declares three languages in `package.json`:
| Language id | Applies to |
| ---------------- | --------------------------------- |
| `carbon` | `*.carbon` |
| `carbon-testdata`| `**/testdata/**/*.carbon` |
| `semir` | `*.semir` |
> [!NOTE]
> The TextMate _scope_ for SemIR is `source.carbon-semir`, but the
> _language id_ is `semir`. Markdown code fences in hover text resolve language
> ids, so a fence must say ` ```semir `.
`extension.ts` launches the server over stdio, using the `carbonPath` setting
(default `./bazel-bin/toolchain/carbon`). Its `documentSelector` controls which
files are sent to the server at all; a new document kind has to be added there
as well as in the server.
+15
View File
@@ -43,6 +43,21 @@ script:
This script runs `prek` on all files that have changed between `trunk` and your
current Jujutsu `@` change.
Note that the script always compares against `trunk`. If your change is based on
another bookmark rather than on `trunk`, the script also checks the files
changed by that underlying change, so a reported failure may not be in your own
work.
## Hooks that rewrite files
Some hooks, notably `clang-format` and `rumdl`, fix problems in place rather
than only reporting them. When they do, `prek` reports a failure and exits
non-zero even though the tree is now correct.
Re-run `prek` after any failure that modified files, and treat the second, clean
run as the result. Review what it changed: a reflow is expected, but a content
change may not be what you intended.
## Prek dependency errors
> [!TIP] If `prek` fails with an error about resolving dependencies or security
@@ -0,0 +1,366 @@
---
name: Review testdata changes
description:
Instructions for judging whether changes to file test output
(`// CHECK:STDOUT:` and `// CHECK:STDERR:` lines) are correct, which
changes are acceptable churn, and which are regressions in disguise.
---
# Review testdata changes
<!--
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
-->
## Introduction
Most toolchain work moves file test output. `./toolchain/autoupdate_testdata.py`
rewrites the `// CHECK:STDOUT:` and `// CHECK:STDERR:` lines in
`toolchain/*/testdata/` to match current behavior, so after running it the tests
pass again whether or not the new behavior is right. Deciding that the new
output is the output you wanted is a separate, manual step, and it is the step
this skill covers.
Three skills divide the work:
- [Toolchain tests](../toolchain_tests/SKILL.md): how to _author_ tests and
generate their output.
- This skill: how to _judge_ an output diff.
- [Summarize testdata changes](../summarize_testdata_changes/SKILL.md): how to
_report_ an output diff once you believe it is correct.
> [!IMPORTANT] Never hand-edit `// CHECK:STDOUT:` or `// CHECK:STDERR:` lines.
> Everything below is about changing the _code_ until the generated output is
> right, never about editing the output to match the code.
## The rule that generates all the others
**Every line of testdata churn must have a cause you can name.** Not "it is
similar to the other changes", not "the tests pass now" — an actual sentence
saying which code change produced it and why that is the intended result.
A diff you cannot narrate is a diff you have not reviewed. Changes you cannot
explain are where regressions hide, because a regression and an intended change
look exactly alike once the autoupdater has written them down.
> [!CAUTION] Autoupdating is destructive to your evidence. Once the autoupdater
> has run, the previous expectations are gone from the working copy. Read the
> diff after _every_ autoupdate run, and if something changed for a reason you
> cannot name, fix the code before autoupdating again. Recovering the old
> expectations later means reverting and re-running.
## STDERR and STDOUT are different kinds of evidence
Treat them separately; they have different standards of proof.
**STDERR is user-visible behavior.** These are the diagnostics a Carbon
programmer sees. A change here is a change to the language implementation as
users experience it, so each one needs an individual justification. For a
refactoring, the expected STDERR diff is empty.
**STDOUT is internal representation.** SemIR dumps, parse trees, LLVM IR. Users
never see it. Churn here is normal and often unavoidable, so the standard is not
"no change" but "no change I cannot account for".
For a change that is supposed to preserve behavior, write the list of accepted
STDERR changes down _before_ you autoupdate, and keep it current. Order matters
more than form: written first, the list is a prediction the diff can falsify;
written afterwards, it is a description of whatever happened, and describes a
regression exactly as well as an intended change.
The list does not have to be a deliverable. Scratch notes you never publish do
the job, because the work is in committing to the list, not in presenting it.
What a reader needs is not the list but its exceptions: diagnostics that changed
without being predicted, and predictions that did not occur. Both are findings.
The matches are not, and reporting them buries the two entries that matter.
## Judging STDOUT churn
Sort each STDOUT change into mechanical or structural.
### Mechanical churn
Expected, and cheap to accept in bulk once you have confirmed the pattern:
- **Renaming.** An instruction, type, or scope prints under a new name.
- **Positional name renumbering.** Names like `%x.loc18_46.3` embed a line,
column, and disambiguating index. Two different events move them:
- Adding or removing an instruction at a location renumbers the rest, so a
_single_ removed instruction can show up as many changed lines in the
same block. Confirm the cascade is a cascade before accepting it as one.
- Adding or removing a _diagnostic_ moves the source lines themselves, so
the line component changes everywhere below it in the file. See
[Autoupdate to a fixed point](#autoupdate-to-a-fixed-point).
- **Fingerprint-derived names.** Mangled names and some scope names are
derived from a hash of their inputs. If you changed a hashed input, these
move. Confirm that each such difference is _only_ the fingerprint, and not a
fingerprint difference concealing a structural one.
> [!TIP] Mechanical churn is usually wide and shallow: the same substitution,
> repeated across many files. If a "mechanical" pattern needs a different
> explanation in each file, it is not mechanical.
### Structural churn
Each of these needs its own explanation:
- Instructions appearing or disappearing.
- Changed `[concrete = ...]`, `[symbolic = ...]`, or other constant-value
annotations.
- Changed types on existing instructions.
- Changed control flow: new or removed blocks, changed branch targets.
- Raw instruction ids renumbering in `--dump-raw-sem-ir` output when you did
not intend to change the id layout.
## Fewer instructions is not automatically better
A diff that removes instructions looks like an optimization. Whether it is one
depends on what the changed function owes its caller, and two functions can
produce the same shrinking diff for opposite reasons:
- A function that only needs a constant value, but built instructions on the
way to it, was doing wasted work. Dropping them and using the constant
directly is correct, and the shorter output reflects that.
- A function whose caller needs a **non-canonical instruction** can be made
shorter the same way, by using the constant instead of creating an
instruction of its own — and that is wrong. The instruction carries location
information, and symbolic constant substitution operates on it; the constant
value alone loses both.
The instruction count is identical evidence in both cases, so it cannot be the
thing you judge. Decide what the function is required to produce; the count
follows from that.
The same reasoning runs in reverse: a diff that _adds_ instructions is not
automatically a regression.
## Judging STDERR churn
### A diagnostic's authority depends on the file's prefix
The `fail_` and `todo_` prefixes (see
[Toolchain tests](../toolchain_tests/SKILL.md)) say how much the recorded
diagnostics are worth:
- **`fail_...`** — the test should and does produce errors. The recorded
diagnostic **is the specification**. Changing it is a user-visible behavior
change and needs justification on its own merits.
- **`fail_todo_...`** — the test produces errors (or crashes) but shouldn't,
or produces the wrong errors. The recorded diagnostic is explicitly **not**
the specification; the file exists to record that today's behavior is wrong.
Changing it replaces one wrong answer with another. That is acceptable when
you can say why the new message follows from your change and why it is no
further from the intended eventual behavior — which, for many such files, is
no diagnostic at all.
- **`todo_fail_...`** — the test should produce errors but does not. Gaining a
diagnostic here may be _progress_, not a regression. Either way the file
must be renamed, since the framework requires a `fail_` prefix on any file
that errors: `fail_...` if it now produces the right error, `fail_todo_...`
if the error is the wrong one. Both renames make the test pass, so record
which case it is instead of letting the rename settle it.
- **`todo_...`** — behavior is wrong but produces no errors, and shouldn't.
Gaining a diagnostic here is a regression unless you can argue otherwise.
> [!IMPORTANT] This is a reason to look at the _filename_ before judging a
> diagnostic change, not a license to ignore `todo_` files. "It was already
> broken" does not excuse making it differently broken for no reason.
### Reclassifying tests
If your change moves a test between the states above, the prefix must move with
it: when the test is fixed, when it starts failing, and when it starts failing
differently. The correspondence between the `fail_` prefix and whether
compilation actually failed is enforced by the test framework, not by the
autoupdater, so it surfaces when you run `bazelisk test` and not when you
autoupdate. **Autoupdating is not a substitute for running the tests.**
Only that half of the name is checked. Nothing enforces `todo_`, so a file whose
behavior you have just fixed can keep its `todo_` prefix indefinitely and still
pass. A missing `fail_` stops the build; a stale `todo_` is silent, and is yours
to catch.
When a fix drops a file's prefix, also check that the file still belongs where
it is and that its comments do not still describe the old broken behavior.
### Vaguer diagnostics are a signal, not a verdict
When a diagnostic becomes less specific — a general "unsupported" message
replacing one that named the problem — that usually means a code path stopped
finding information it previously had. Sometimes that is correct: the
information was misleading, and the old message was confidently wrong.
Do not accept it silently and do not reject it reflexively. Say which direction
it moved and whether the new message is closer to or further from the eventual
intended behavior.
## Signals in the shape of the diff
### Zero churn is a result
If you removed something you believed was doing work and _no_ testdata moved,
that is not a missing test run — it is the proof that the thing was a no-op.
Say so explicitly; it is one of the strongest pieces of evidence a refactoring
can produce.
The converse is also informative. If you expected a path to churn and it didn't,
either your model of the code is wrong or that path is untested. Find out which,
and consider adding a test before continuing.
### Churn should be proportional to the change
- **Wide churn from a narrow change** means your model of the code is wrong.
Do not autoupdate over it. Find the structural mistake first.
- **Narrow churn from a sweeping change** means the affected paths are
probably untested.
Set a rough expectation for the size of the diff before you run the autoupdater,
and treat a large mismatch in either direction as a finding.
### Never make the diff smaller by weakening the test
Editing test _input_ (the Carbon source, not the CHECK lines) to make a diff
look better is a behavior change in disguise. Deleting a test that now produces
awkward output is worse. If a test's input has to change, that is a separate,
explicitly-justified change, not diff cleanup.
## What the autoupdater will not fix for you
- **`NOAUTOUPDATE` files.** Their expectations are maintained by hand. They
fail under `bazelisk test` rather than being silently rewritten.
- **Hand-written C++ expectations**, for example golden output asserted in a
`_test.cpp`. When several assertions in one of these break together, often
only the first failure is reported, so fixing it can reveal another. Re-run
until clean rather than assuming one fix was the whole repair.
- **Golden files outside `testdata/`**, and documentation that quotes compiler
output.
## Review loop
### Run the autoupdater
Autoupdate everything, then read the diff a directory at a time. Passing no
paths is the default and updates every file test in the toolchain:
```bash
./toolchain/autoupdate_testdata.py
```
Narrow the scope only while iterating on one subdirectory you know you are not
done with, where each round would otherwise regenerate output you have already
read:
```bash
./toolchain/autoupdate_testdata.py toolchain/check/testdata/SUBDIR/**/*
```
The globs are expanded by the shell; the script filters its arguments to
`.carbon` files under a `testdata/` directory.
> [!TIP] If intermediate states crash on `CARBON_CHECK` failures, pass
> `--non-fatal-checks` so you can see the full set of downstream damage in one
> run instead of one crash at a time.
> [!IMPORTANT] Return to the full scope before judging the diff. Whether churn
> is proportional to the change, and whether a change to one phase moved another
> phase's testdata, are only visible across everything.
### Autoupdate to a fixed point
One pass is not always enough, because the autoupdater's output is part of its
own input. `// CHECK:STDERR:` lines sit inline, immediately above the source
line they describe, and the compiler reads them as comments in the file.
Gaining or losing a diagnostic therefore moves every source line below it.
Within a single run, the compiler has already read the file as it was, so the
two kinds of output end up in different states:
- **STDERR is correct after one pass.** These lines locate themselves
relatively, as `[[@LINE+N]]`, and the autoupdater recomputes `N` as it
places them.
- **STDOUT is stale after one pass.** SemIR names like `%x.loc18_46.3` embed
an absolute line and column with no filename attached, and the autoupdater
only remaps `file.carbon:18`-style references. Nothing rewrites the `loc`,
so it still describes where the instruction was _before_ the diagnostic
lines moved it.
Running again compiles the shifted file and the names catch up. The diagnostic
set does not change this time, so nothing shifts again and a third run is a
no-op. Keep running the autoupdater until it stops changing files: one pass when
the diagnostics held still, two when they didn't.
> [!WARNING] The intermediate state is self-inconsistent, not just unfinished:
> its `loc` names describe a file layout that no longer exists. Keep reading the
> diff after every run — that rule does not change — but do not chase positional
> churn to a cause until the file has converged, and do not present the diff
> until then either.
The converging pass should be positional renumbering and nothing else. If it
moves an instruction, a type, or a constant value, then something other than a
`loc` name is sensitive to where lines fall in the file. Find out what before
accepting it.
The file tests do catch a file left unconverged, since each test re-runs the
autoupdate in memory and fails with
`Autoupdate would make changes to the file content` when the result differs. But
that arrives at `bazelisk test` time, after you have already read a diff that
was describing a file which had moved out from under it.
### Inspect the diff
Inspect the diagnostics first, since that is the acceptance criterion:
```bash
# STDERR-only view, with jj.
jj --no-pager diff --git 'glob:toolchain/*/testdata/**' \
| grep -E '^[-+].*CHECK:STDERR'
# The same, with git.
git diff -- 'toolchain/*/testdata/*' \
| grep -E '^[-+].*CHECK:STDERR'
```
For a structured view separating test input, STDERR, and STDOUT changes, use the
helper from the
[Summarize testdata changes](../summarize_testdata_changes/SKILL.md) skill:
```bash
jj --no-pager diff --git 'glob:toolchain/*/testdata/**' \
| python3 .agents/skills/summarize_testdata_changes/scripts/parse_diff.py
git diff -- 'toolchain/*/testdata/*' \
| python3 .agents/skills/summarize_testdata_changes/scripts/parse_diff.py
```
The argument after the tool name is not interchangeable: `glob:...` is a jj
fileset, `-- ...` is a git pathspec. `--git` and `--no-pager` are jj flags; git
already emits this format and skips the pager when piped.
### Run the tests
Then run the tests, which is what catches prefix mismatches and non-autoupdated
expectations:
```bash
bazelisk test //toolchain/...
```
See the [Bazel usage](../bazel/SKILL.md) skill.
## Checklist
Before presenting a testdata diff as finished:
- [ ] The autoupdater was run until it made no further changes, so no `loc`
name describes a stale line numbering.
- [ ] The list of accepted STDERR changes was written before autoupdating, and
every change either matches it or is reported as an exception.
- [ ] Every STDOUT change is either an instance of a named mechanical pattern
or has its own explanation.
- [ ] Instructions that appeared or disappeared are justified by what the
changed function owes its caller, not by the instruction count.
- [ ] Files whose prefix no longer matches their behavior have been renamed.
- [ ] No test input was changed, and no test was deleted, to make the diff
smaller.
- [ ] The size of the diff is proportional to the size of the change.
@@ -17,6 +17,10 @@ This skill provides instructions for creating a comprehensive report summarizing
changes to Carbon testdata files (`toolchain/*/testdata`) and associating them
with related code changes.
This skill is about _reporting_ a diff. For deciding whether the diff is correct
in the first place, see the
[Review testdata changes](../review_testdata_changes/SKILL.md) skill.
## Goals
Produce a report that:
@@ -40,10 +44,11 @@ input changes.
#### For Git Users:
- **Summarize code changes**: `git diff --stat -- ':!toolchain/*/testdata'`
- **Summarize code changes**: `git diff --stat -- ':!toolchain/*/testdata/*'`
- To see content of non-testdata changes:
`git diff -- ':!toolchain/*/testdata'`
- **Identify testdata changes**: `git diff --name-only 'toolchain/*/testdata'`
`git diff -- ':!toolchain/*/testdata/*'`
- **Identify testdata changes**:
`git diff --name-only 'toolchain/*/testdata/*'`
#### For Jujutsu (jj) Users:
@@ -80,7 +85,7 @@ STDOUT changes. This script reads a unified diff from stdin.
```bash
# For Git:
git diff -- 'toolchain/*/testdata' | python3 .agents/skills/summarize_testdata_changes/scripts/parse_diff.py
git diff -- 'toolchain/*/testdata/*' | python3 .agents/skills/summarize_testdata_changes/scripts/parse_diff.py
# For Jujutsu (jj):
jj diff --git 'toolchain/*/testdata' | python3 .agents/skills/summarize_testdata_changes/scripts/parse_diff.py
@@ -35,6 +35,10 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
([SKILL.md](../builtins/SKILL.md)) for guidelines on registering, mapping,
constant evaluating, and lowering compiler builtin primitives (e.g.
`"int.convert_float"`).
- **Language server**: Refer to the **Language server** skill
([SKILL.md](../language_server/SKILL.md)) before working on
`toolchain/language_server/` or `utils/vscode/`. Neither follows the
patterns described here.
- **Phases**: Lex -> Parse -> Check -> Lower.
- **Definitions**: Many kinds (tokens, parse nodes, SemIR instructions) are
defined in `.def` files and expanded by way of macros.
+9
View File
@@ -23,6 +23,11 @@ Toolchain tests evaluate Carbon source files through Lexing, Parsing, Checking,
and optionally Lowering. Output (for example SemIR dumps, Clang errors) is
captured and validated using inline CHECK records.
Language server tests also use `file_test`, but with quite different
conventions (an LSP message stream, `AUTOUPDATE-SPLIT`, `FROM_FILE_SPLIT`).
Refer to the **Language server** skill
([SKILL.md](../language_server/SKILL.md)) for those.
## Structure and Authoring
### File Layout and Headers
@@ -182,3 +187,7 @@ updater:
Review the updated test outputs (for example, by way of `git diff`). Ensure
logic paths are correctly tested rather than producing massive boilerplate
blocks.
Autoupdating makes the tests pass whether or not the new behavior is correct, so
deciding that the new output is the output you wanted is a separate step. See
the [Review testdata changes](../review_testdata_changes/SKILL.md) skill.
+8
View File
@@ -20,3 +20,11 @@ contributing to the Carbon Language project.
> [!IMPORTANT] Always use `bazelisk` instead of `bazel` for all commands in the
> Carbon project. Refer to the
> [Bazel usage skill](/.agents/skills/bazel/SKILL.md) for detailed instructions.
## Version control
> [!IMPORTANT] Never rewrite the history of a change that has been submitted as
> a pull request. Reviewers track a PR by its commits, and rewriting them
> discards their in-progress review. Ask before rewriting history in any case.
> Refer to the [Jujutsu (jj) usage skill](/.agents/skills/jj/SKILL.md) for
> details.
+4 -4
View File
@@ -287,9 +287,9 @@ sh_test(
srcs = [":filesystem_benchmark"],
args = [
"--benchmark_dry_run",
# Restrict the sizes to 4-digit ones or smaller to keep test times low.
# Restrict the sizes to 2-digit ones or smaller to keep test times low.
# The `$$` is repeated for Bazel escaping of `$`.
"--benchmark_filter=^[^/]+(/[0-9]{1,4}(/[0-9]+)?)?/real_time$$",
"--benchmark_filter=^[^/]+(/[0-9]{1,2}(/[0-9]+)?)?/real_time$$",
],
)
@@ -501,7 +501,7 @@ sh_test(
args = [
"--benchmark_dry_run",
# The `$$` is repeated for Bazel escaping of `$`.
"--benchmark_filter=^[^/]*/[1-9][0-9]{0,3}(/[0-9]+)?$$",
"--benchmark_filter=^[^/]*/[1-9][0-9]{0,2}(/[0-9]+)?$$",
],
)
@@ -688,7 +688,7 @@ sh_test(
args = [
"--benchmark_dry_run",
# The `$$` is repeated for Bazel escaping of `$`.
"--benchmark_filter=^[^/]*/[1-9][0-9]{0,3}(/[0-9]+)?$$",
"--benchmark_filter=^[^/]*/[1-9][0-9]{0,2}(/[0-9]+)?$$",
],
)
+2 -2
View File
@@ -444,9 +444,9 @@ auto BM_CreateDirectories(benchmark::State& state) -> void {
CARBON_CHECK(existing_depth <= depth);
CARBON_CHECK(depth > 0);
// Use a batch size of 10 to get avoid completely swamping the measurements
// Use a batch size of 5 to get avoid completely swamping the measurements
// with overhead from creating existing directories and cleaning up.
constexpr int BatchSize = 10;
constexpr int BatchSize = 5;
// Pre-build both the paths and the existing paths. Note that we use
// relatively short paths here, which if anything makes the benefits of the
+21 -18
View File
@@ -69,6 +69,14 @@ class MapView
using KeyContextT = ImplT::KeyContextT;
using MetricsT = ImplT::MetricsT;
// A key and its value, as a pair of references. This is what iterating the
// map produces; there is no object in the table combining the two.
using Entry = ImplT::EntryRefT;
// A range over the key-value entries of the map. Bound to the lifetime of
// the viewed map, and invalidated by mutating it.
using Range = ImplT::EntryRange;
// This type represents the result of lookup operations. It encodes whether
// the lookup was a success as well as accessors for the key and value.
class LookupKVResult {
@@ -111,10 +119,8 @@ class MapView
auto operator[](LookupKeyT lookup_key) const -> ValueT*
requires(std::default_initializable<KeyContextT>);
// Run the provided callback for every key and value in the map.
template <typename CallbackT>
auto ForEach(CallbackT callback) -> void
requires(std::invocable<CallbackT, KeyT&, ValueT&>);
// Returns a range for iterating over all key-value entries in the map.
auto entries() const -> Range;
// This routine is relatively inefficient and only intended for use in
// benchmarking or logging of performance anomalies. The specific metrics
@@ -169,6 +175,8 @@ class MapBase : protected RawHashtable::BaseImpl<InputKeyT, InputValueT,
using ViewT = MapView<KeyT, ValueT, KeyContextT>;
using LookupKVResult = ViewT::LookupKVResult;
using MetricsT = ImplT::MetricsT;
using Entry = ViewT::Entry;
using Range = ViewT::Range;
// The result type for insertion operations both indicates whether an insert
// was needed (as opposed to finding an existing element), and provides access
@@ -228,12 +236,12 @@ class MapBase : protected RawHashtable::BaseImpl<InputKeyT, InputValueT,
}
// Convenience forwarder to the view type.
template <typename CallbackT>
auto ForEach(CallbackT callback) const -> void
requires(std::invocable<CallbackT, KeyT&, ValueT&>)
{
return ViewT(*this).ForEach(callback);
}
auto entries() const& -> Range { return ViewT(*this).entries(); }
// Deleted on rvalues: the range refers to storage owned by this table, so a
// range built from a temporary map would dangle. Both qualifiers are needed
// as `&&` alone would leave a const rvalue binding to the `const&` overload.
auto entries() && = delete;
auto entries() const&& = delete;
// Convenience forwarder to the view type.
auto ComputeMetrics(KeyContextT key_context = KeyContextT()) const
@@ -424,14 +432,9 @@ auto MapView<InputKeyT, InputValueT, InputKeyContextT>::operator[](
}
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
template <typename CallbackT>
auto MapView<InputKeyT, InputValueT, InputKeyContextT>::ForEach(
CallbackT callback) -> void
requires(std::invocable<CallbackT, KeyT&, ValueT&>)
{
this->ForEachEntry(
[callback](EntryT& entry) { callback(entry.key(), entry.value()); },
[](auto...) {});
auto MapView<InputKeyT, InputValueT, InputKeyContextT>::entries() const
-> Range {
return this->ImplT::EntriesImpl();
}
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
+58
View File
@@ -93,6 +93,17 @@ struct MapWrapperImpl {
}
auto BenchErase(KeyT k) -> bool { return m.erase(k) != 0; }
// Visits every entry in the map, calling `cb` with the key and value of each
// one. Each map type is expected to traverse using whatever API it provides
// for this, so that the benchmark measures iterating the map rather than any
// specific iteration API.
template <typename CallbackT>
auto BenchIterate(CallbackT cb) -> void {
for (const auto& entry : m) {
cb(entry.first, entry.second);
}
}
};
// Explicit (partial) specialization for the Carbon map type that uses its
@@ -126,6 +137,13 @@ struct MapWrapperImpl<Map<KT, VT, MinSmallSize>> {
}
auto BenchErase(KeyT k) -> bool { return m.Erase(k); }
template <typename CallbackT>
auto BenchIterate(CallbackT cb) -> void {
for (auto [k, v] : m.entries()) {
cb(k, v);
}
}
};
// Provide a way to override the Carbon Map specific benchmark runs with another
@@ -516,5 +534,45 @@ static void BM_MapInsertSeq(benchmark::State& state) {
}
MAP_BENCHMARK_ONE_OP(BM_MapInsertSeq, SizeArgs);
// Benchmark visiting every entry in a map.
//
// Unlike the lookup benchmarks, this walks the table's storage from end to end
// rather than probing it, so it is largely a measure of how densely entries are
// packed and how cheaply empty slots can be skipped. There is no dependency
// between the entries visited, and so this is a throughput measurement.
//
// Each batch is a single complete traversal of the map, with the batch size set
// to the number of entries so that the reported time is the per-entry cost.
template <typename MapT>
static void BM_MapIterate(benchmark::State& state) {
using MapWrapperT = MapWrapper<MapT>;
using KT = typename MapWrapperT::KeyT;
using VT = typename MapWrapperT::ValueT;
MapWrapperT m;
auto [keys, _] = GetKeysAndMissKeys<KT>(state.range(0));
for (auto k : keys) {
bool inserted = m.BenchInsert(k, MakeValue<VT>());
CARBON_DCHECK(inserted, "Must be a successful insert!");
}
while (state.KeepRunningBatch(keys.size())) {
ssize_t sum = 0;
m.BenchIterate([&sum](const KT& k, const VT& v) {
// Consume both the key and the value so that neither the traversal nor
// the loads out of the entries can be optimized away.
sum += ValueToBool(k) + ValueToBool(v);
});
benchmark::DoNotOptimize(sum);
}
// The time is already per-entry, so an iteration-invariant rate of one gives
// the throughput of entries visited.
state.counters["KeyRate"] =
benchmark::Counter(1, benchmark::Counter::kIsIterationInvariantRate);
ReportMetrics(m, state);
}
MAP_BENCHMARK_ONE_OP(BM_MapIterate, SizeArgs);
} // namespace
} // namespace Carbon
+113 -2
View File
@@ -7,7 +7,10 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <concepts>
#include <initializer_list>
#include <iterator>
#include <ranges>
#include <type_traits>
#include <utility>
#include <vector>
@@ -37,6 +40,7 @@ using RawHashtable::MoveOnlyTestData;
using RawHashtable::TestData;
using RawHashtable::TestKeyContext;
using ::testing::Pair;
using ::testing::UnorderedElementsAre;
using ::testing::UnorderedElementsAreArray;
template <typename MapT, typename MatcherRangeT>
@@ -47,9 +51,9 @@ auto ExpectMapElementsAre(MapT&& m, MatcherRangeT element_matchers) -> void {
std::vector<
std::pair<std::reference_wrapper<KeyT>, std::reference_wrapper<ValueT>>>
map_entries;
m.ForEach([&map_entries](KeyT& k, ValueT& v) {
for (auto [k, v] : m.entries()) {
map_entries.push_back({std::ref(k), std::ref(v)});
});
}
// Use the GoogleMock unordered container matcher to validate and show errors
// on wrong elements.
@@ -865,5 +869,112 @@ TEST(MapContextTest, Basic) {
m, MakeKeyValues([](int k) { return k * 100 + 1; }, llvm::seq(1, 512)));
}
TYPED_TEST(MapTest, Range) {
using MapT = TypeParam;
using Range = decltype(std::declval<const MapT&>().entries());
using Iter = typename Range::Iterator;
static_assert(std::forward_iterator<Iter>);
static_assert(std::same_as<decltype(std::declval<Range>().begin()), Iter>);
static_assert(std::same_as<decltype(std::declval<Range>().end()), Iter>);
static_assert(std::ranges::forward_range<Range>);
static_assert(std::ranges::common_range<Range>);
MapT m;
EXPECT_EQ(m.entries().begin(), m.entries().end());
for (auto [k, v] : m.entries()) {
static_cast<void>(k);
static_cast<void>(v);
FAIL() << "Empty map range should have no elements";
}
for (int i = 1; i <= 5; ++i) {
m.Insert(i, i * 10);
}
int count = 0;
for (const auto& [k, v] : m.entries()) {
EXPECT_EQ(v, m.Lookup(k).value());
++count;
}
EXPECT_EQ(count, 5);
EXPECT_THAT(m.entries(),
UnorderedElementsAre(Pair(1, 10), Pair(2, 20), Pair(3, 30),
Pair(4, 40), Pair(5, 50)));
using KeyT = typename MapT::KeyT;
using ValueT = typename MapT::ValueT;
using KeyContextT = typename MapT::KeyContextT;
MapView<const KeyT, const ValueT, KeyContextT> cv = m;
int cv_count = 0;
for (auto [k, v] : cv.entries()) {
static_assert(std::is_const_v<std::remove_reference_t<decltype(k)>>);
static_assert(std::is_const_v<std::remove_reference_t<decltype(v)>>);
EXPECT_EQ(v, m.Lookup(k).value());
++cv_count;
}
EXPECT_EQ(cv_count, 5);
EXPECT_THAT(cv.entries(),
UnorderedElementsAre(Pair(1, 10), Pair(2, 20), Pair(3, 30),
Pair(4, 40), Pair(5, 50)));
for (auto [k, v] : m.entries()) {
if constexpr (requires { v.value; }) {
v.value = 99;
} else {
v = 99;
}
}
for (const auto& [k, v] : m.entries()) {
if constexpr (requires { v.value; }) {
EXPECT_EQ(v.value, 99);
} else {
EXPECT_EQ(v, 99);
}
}
EXPECT_THAT(m.entries(),
UnorderedElementsAre(Pair(1, 99), Pair(2, 99), Pair(3, 99),
Pair(4, 99), Pair(5, 99)));
auto r = m.entries();
int iter_count = 0;
for (auto it = r.begin(); it != r.end(); ++it) {
EXPECT_EQ(it->second, m.Lookup(it->first).value());
EXPECT_EQ((*it).second, m.Lookup((*it).first).value());
++iter_count;
}
EXPECT_EQ(iter_count, 5);
auto it = r.begin();
auto prev = it++;
EXPECT_NE(it, prev);
}
TYPED_TEST(MoveOnlyMapTest, Range) {
TypeParam m;
m.Insert(1, 10);
m.Insert(2, 20);
int count = 0;
for (auto [k, v] : m.entries()) {
EXPECT_EQ(v.value, k.value * 10);
++count;
}
EXPECT_EQ(count, 2);
}
#ifndef NDEBUG
TEST(MapDeathTest, MutateDuringIterationFails) {
EXPECT_DEATH(([] {
Map<int, int> m;
m.Insert(1, 10);
auto range = m.entries();
m.Insert(2, 20);
}()),
"Hashtable mutated during iteration");
}
#endif
} // namespace
} // namespace Carbon::Testing
+5
View File
@@ -10,4 +10,9 @@ namespace Carbon::RawHashtable {
volatile std::byte global_addr_seed{1};
#ifndef NDEBUG
std::atomic<HashCode> entropy_hash =
Carbon::HashValue(reinterpret_cast<uint64_t>(&global_addr_seed));
#endif
} // namespace Carbon::RawHashtable
+423 -79
View File
@@ -6,6 +6,7 @@
#define CARBON_COMMON_RAW_HASHTABLE_H_
#include <algorithm>
#include <atomic>
#include <concepts>
#include <cstddef>
#include <cstring>
@@ -18,6 +19,7 @@
#include "common/concepts.h"
#include "common/hashing.h"
#include "common/raw_hashtable_metadata_group.h"
#include "llvm/ADT/iterator.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MathExtras.h"
@@ -122,10 +124,15 @@
// null. Since it doesn't track the exact number of filled entries in a table,
// it doesn't support a container-style `size` API.
//
// - There is no direct iterator support because of the complexity of embedding
// the group-based metadata scanning into an iterator model. Instead, there is
// just a for-each method that is passed a lambda to observe all entries. The
// order of this observation is also not guaranteed.
// - Iteration is provided by a range object rather than by iterators hanging
// directly off the table, because the debug-only checks for mutation during
// iteration need state that outlives a single iterator: see `EntryRange`
// below. Obtaining one is an explicit call (`entries()`), as scanning an
// entire table is a costly operation that shouldn't be hidden behind a bare
// `begin()`/`end()` pair.
//
// The order of iteration is not guaranteed, and debug builds actively vary it
// between ranges to keep callers from depending on it.
namespace Carbon::RawHashtable {
// Which prefetch strategies to enable can be controlled via macros to enable
@@ -152,7 +159,7 @@ inline constexpr ssize_t MinAllocatedSize = std::max<ssize_t>(64, MaxGroupSize);
// An entry in the hashtable storage of a `KeyT` and `ValueT` object.
//
// Allows manual construction, destruction, and access to these values so we can
// create arrays af the entries prior to populating them with actual keys and
// create arrays of the entries prior to populating them with actual keys and
// values.
template <typename KeyT, typename ValueT>
struct StorageEntry {
@@ -168,6 +175,20 @@ struct StorageEntry {
IsTriviallyRelocatable || (std::is_copy_constructible_v<KeyT> &&
std::is_copy_constructible_v<ValueT>);
// How iteration refers to an entry, and the iterator traits that follow.
//
// The key and value are stored side by side with nothing combining them, so
// a reference to an entry is a pair of references built on demand. That pair
// is a *proxy* reference: C++20 forward iterators permit one, but C++17
// algorithms may assume a forward iterator's reference is a real lvalue, so
// the C++17 category is `input`.
using RefT = std::pair<KeyT&, ValueT&>;
using IterValueT = RefT;
using IterPointerT = const RefT*;
using IterCategoryT = std::input_iterator_tag;
auto ref() -> RefT { return RefT(key(), value()); }
auto key() const -> const KeyT& {
// Ensure we don't need more alignment than available. Inside a method body
// to apply to the complete type.
@@ -194,11 +215,21 @@ struct StorageEntry {
// construction. As a consequence, this struct only provides the storage and
// we have to manually manage the construction, move, and destruction of the
// objects.
//
// Destroys the key and value behind an entry reference. Iteration hands back
// `RefT` rather than the entry, so this is how a walked entry is destroyed.
static auto DestroyRef(RefT ref) -> void {
ref.first.~KeyT();
ref.second.~ValueT();
}
// Destroys the key and value of this entry. The common case is destroying an
// entry found in the table's storage, where there is no reference to hand to
// `DestroyRef`.
auto Destroy() -> void {
static_assert(!IsTriviallyDestructible,
"Should never instantiate when trivial!");
key().~KeyT();
value().~ValueT();
DestroyRef(ref());
}
auto CopyFrom(const StorageEntry& entry) -> void {
@@ -241,6 +272,15 @@ struct StorageEntry<KeyT, void> {
static constexpr bool IsCopyable =
IsTriviallyRelocatable || std::is_copy_constructible_v<KeyT>;
// As above, but a set's entry is nothing but its key, so a reference to an
// entry is a true lvalue reference and the iterator is a plain forward one.
using RefT = KeyT&;
using IterValueT = std::remove_cv_t<KeyT>;
using IterPointerT = KeyT*;
using IterCategoryT = std::forward_iterator_tag;
auto ref() -> RefT { return key(); }
auto key() const -> const KeyT& {
// Ensure we don't need more alignment than available.
static_assert(
@@ -254,10 +294,12 @@ struct StorageEntry<KeyT, void> {
return const_cast<KeyT&>(const_cast<const StorageEntry*>(this)->key());
}
static auto DestroyRef(RefT ref) -> void { ref.~KeyT(); }
auto Destroy() -> void {
static_assert(!IsTriviallyDestructible,
"Should never instantiate when trivial!");
key().~KeyT();
DestroyRef(ref());
}
auto CopyFrom(const StorageEntry& entry) -> void
@@ -360,6 +402,13 @@ class ViewImpl {
using EntryT = StorageEntry<KeyT, ValueT>;
using MetricsT = Metrics;
// What iterating over the table's entries produces: a `KeyT&` for a set, and
// a `std::pair<KeyT&, ValueT&>` for a map. See `StorageEntry`.
using EntryRefT = EntryT::RefT;
// The range type produced by `EntriesImpl`.
class EntryRange;
friend class BaseImpl<KeyT, ValueT, KeyContextT>;
template <typename InputBaseT, ssize_t SmallSize>
friend class TableImpl;
@@ -385,13 +434,11 @@ class ViewImpl {
auto LookupEntry(LookupKeyT lookup_key, KeyContextT key_context) const
-> EntryT*;
// Calls `entry_callback` for each entry in the hashtable. All the entries
// within a specific group are visited first, and then `group_callback` is
// called on the group itself. The `group_callback` is typically only used by
// the internals of the hashtable.
template <typename EntryCallbackT, typename GroupCallbackT>
auto ForEachEntry(EntryCallbackT entry_callback,
GroupCallbackT group_callback) const -> void;
// Returns a range for iterating over all entries in the hashtable.
//
// The returned range copies this view, so it remains valid for as long as the
// underlying table does, independent of this view's lifetime.
auto EntriesImpl() const -> EntryRange;
// Returns a collection of informative metrics on the the current state of the
// table, useful for performance analysis. These include relatively slow to
@@ -425,7 +472,7 @@ class ViewImpl {
auto metadata() const -> uint8_t* {
return reinterpret_cast<uint8_t*>(storage_);
}
auto entries() const -> EntryT* {
auto entries_data() const -> EntryT* {
return reinterpret_cast<EntryT*>(reinterpret_cast<std::byte*>(storage_) +
EntriesOffset(alloc_size_));
}
@@ -457,6 +504,172 @@ class ViewImpl {
Storage* storage_;
};
// A range over the entries of a hashtable.
//
// A dedicated range object is used rather than a plain pair of iterators (such
// as `llvm::iterator_range`) because the range scopes two debug-only behaviors
// that a bare iterator pair has nowhere to store:
//
// - Mutation checking: the range snapshots a hash of the table's metadata on
// construction and re-checks it on destruction, catching tables that were
// mutated while iteration was active.
// - Traversal order: the group at which iteration starts, and the stride it
// walks the groups with, are drawn from an entropy pool once when the range
// is constructed.
// Deriving them here rather than in `begin()` keeps `begin()` a pure function
// of the range so that it can be called repeatedly, as forward ranges
// require, while still varying the order between separately created ranges.
//
// The range holds the view *by value*; views are two words and designed to be
// cheap to copy. It deliberately does not point back at the view it was created
// from, as views are routinely temporaries or by-value parameters whose
// lifetime is shorter than the table they refer to.
//
// This type provides only the minimal `begin()` and `end()` interface needed by
// range-based for loops and the range concepts, which also avoids any
// compile-time cost from including `<ranges>`.
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
class ViewImpl<InputKeyT, InputValueT, InputKeyContextT>::EntryRange {
public:
class Iterator;
using value_type = typename EntryT::IterValueT;
using reference = EntryRefT;
using difference_type = ssize_t;
explicit EntryRange(ViewImpl view);
// Copyable: every member is a scalar snapshot of the table. Copying a range
// in a debug build simply validates the same table state more than once.
EntryRange(const EntryRange&) = default;
auto operator=(const EntryRange&) -> EntryRange& = default;
#ifndef NDEBUG
// Only debug builds declare a destructor, and so only they re-check the
// table on the way out. Release builds leave the range trivially
// destructible, and so trivial for the purposes of calls, letting it be
// passed and returned in registers.
~EntryRange() { CheckInvariants(); }
#endif
auto begin() const -> Iterator;
auto end() const -> Iterator;
private:
// The facade `Iterator` derives from. A class can't name one of its own
// aliases in its base-specifier, so naming it here lets `Iterator` spell it
// once instead of repeating it to get at the members it inherits.
using IteratorBase =
llvm::iterator_facade_base<Iterator, typename EntryT::IterCategoryT,
value_type, difference_type,
typename EntryT::IterPointerT, reference>;
#ifndef NDEBUG
// Checks that the table's metadata has not changed since construction.
auto CheckInvariants() const -> void;
#endif
ViewImpl view_;
#ifndef NDEBUG
HashCode initial_metadata_hash_ = {};
ssize_t start_group_ = 0;
ssize_t step_ = GroupSize;
#endif
};
// Two-level forward iterator through present hashtable entries.
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
class ViewImpl<InputKeyT, InputValueT, InputKeyContextT>::EntryRange::Iterator
: public EntryRange::IteratorBase {
public:
// Both the set and map forms satisfy C++20's `std::forward_iterator`. A
// map's `reference` is a proxy, which pins its C++17 `iterator_category` to
// `input`, but the C++20 concept is unaffected. See `EntryRefT`.
using iterator_concept = std::forward_iterator_tag;
Iterator() = default;
using EntryRange::IteratorBase::operator++;
[[clang::always_inline]] auto operator*() const -> EntryRefT {
CARBON_DCHECK(present_bits_ != 0, "Dereferencing end iterator!");
__builtin_assume(present_bits_ != 0);
// `index_ptr` folds scaling the match index by the entry size together
// with decoding the index itself, which saves a shift on the portable
// byte-encoded code path.
return MatchIndex(present_bits_).index_ptr(group_entries())->ref();
}
[[clang::always_inline]] auto operator++() -> Iterator& {
CARBON_DCHECK(present_bits_ != 0, "Incrementing end iterator!");
__builtin_assume(present_bits_ != 0);
present_bits_ &= (present_bits_ - 1);
if (LLVM_LIKELY(present_bits_ != 0)) {
return *this;
}
AdvanceToNextPresentGroup();
return *this;
}
friend auto operator==(const Iterator& lhs, const Iterator& rhs) -> bool {
if (lhs.present_bits_ == 0 || rhs.present_bits_ == 0) {
return lhs.present_bits_ == rhs.present_bits_;
}
// The entry pointer already encodes the base and the group offset, so it
// uniquely identifies the group without a separate index.
return lhs.group_entries() == rhs.group_entries() &&
lhs.present_bits_ == rhs.present_bits_;
}
private:
friend class EntryRange;
using MatchBitsT = typename MetadataGroup::MatchPresentRange::BitsT;
using MatchIndex = typename MetadataGroup::MatchIndex;
// Builds an iterator to the first present entry of `range`, or an iterator
// equal to `end()` when the range has no entries to walk. The parameters of
// the walk differ between builds, so both are drawn from the range here
// rather than passed in.
[[clang::always_inline]] explicit Iterator(const EntryRange& range);
[[clang::always_inline]] auto AdvanceToNextPresentGroup() -> void;
// The entries of the group the iterator is currently within. Both builds
// track the current group, but they encode it differently, so the encoding
// is hidden behind this accessor.
auto group_entries() const -> EntryT* {
#ifndef NDEBUG
return group_entries_;
#else
return entries_end_ + group_offset_;
#endif
}
#ifndef NDEBUG
// Debug builds walk groups in a randomized order and so must retain the
// array bases along with the parameters of the walk. The randomized walk
// revisits no group but also never reaches the end of the array, so it does
// need an explicit count of the groups left to visit.
EntryT* group_entries_ = nullptr;
const uint8_t* metadata_ = nullptr;
EntryT* entries_ = nullptr;
ssize_t groups_remaining_ = 0;
ssize_t group_index_ = 0;
size_t probe_mask_ = 0;
ssize_t step_ = GroupSize;
#else
// Release builds walk the groups in order, tracking the position as a
// *negative* byte offset from the end of each array that counts up to zero.
// Anchoring at the ends rather than the beginnings means the walk needs only
// this one induction variable, and reaching zero is the bound.
EntryT* entries_end_ = nullptr;
const uint8_t* metadata_end_ = nullptr;
ssize_t group_offset_ = 0;
#endif
MatchBitsT present_bits_ = 0;
};
// Implementation helper for defining a read-write base type for a hashtable
// that type-erases any SSO buffer.
//
@@ -495,7 +708,10 @@ class BaseImpl {
// NOLINTNEXTLINE(google-explicit-constructor): Designed to implicitly decay.
explicit(false) operator ViewImplT() const { return view_impl(); }
auto view_impl() const -> ViewImplT { return view_impl_; }
auto view_impl() const -> const ViewImplT& { return view_impl_; }
// Destroys all non-trivially destructible entries in the table.
auto DestroyEntries() -> void;
// Looks up the provided key in the hashtable. If found, returns a pointer to
// that entry and `false`.
@@ -510,7 +726,7 @@ class BaseImpl {
// Grow the table to specific allocation size.
//
// This will grow the the table if necessary for it to have an allocation size
// This will grow the table if necessary for it to have an allocation size
// of `target_alloc_size` which must be a power of two. Note that this will
// not allow that many keys to be inserted into the hashtable, but a smaller
// number based on the load factor. If a specific number of insertions need to
@@ -561,7 +777,7 @@ class BaseImpl {
auto storage() const -> Storage* { return view_impl_.storage_; }
auto storage() -> Storage*& { return view_impl_.storage_; }
auto metadata() const -> uint8_t* { return view_impl_.metadata(); }
auto entries() const -> EntryT* { return view_impl_.entries(); }
auto entries_data() const -> EntryT* { return view_impl_.entries_data(); }
auto small_alloc_size() const -> ssize_t {
return static_cast<unsigned>(small_alloc_size_);
}
@@ -665,6 +881,25 @@ inline auto ComputeSeed() -> uint64_t {
return reinterpret_cast<uint64_t>(&global_addr_seed);
}
#ifndef NDEBUG
// A pool of entropy used to vary the iteration order of hashtables in debug
// builds. It is seeded from ASLR where available.
extern std::atomic<HashCode> entropy_hash;
// Returns a pseudo-random value from the entropy pool, advancing the pool.
//
// The load and store are separate relaxed operations rather than one atomic
// read-modify-write so that consuming entropy is just a load, and refreshing
// the pool doesn't block the iteration that follows. Racing callers can lose an
// update and draw the same value, which is fine for a debug aid.
inline auto NextRangeEntropy() -> HashCode {
HashCode prev_entropy_hash = entropy_hash.load(std::memory_order_relaxed);
entropy_hash.store(Carbon::HashValue(prev_entropy_hash),
std::memory_order_relaxed);
return prev_entropy_hash;
}
#endif
inline auto ComputeProbeMaskFromSize(ssize_t size) -> size_t {
CARBON_DCHECK(llvm::isPowerOf2_64(size),
"Size must be a power of two for a hashed buffer!");
@@ -748,7 +983,7 @@ auto ViewImpl<InputKeyT, InputValueT, InputKeyContextT>::LookupEntry(
HashCode hash = key_context.HashKey(lookup_key, ComputeSeed());
auto [hash_index, tag] = hash.ExtractIndexAndTag<7>();
EntryT* local_entries = entries();
EntryT* local_entries = entries_data();
// Walk through groups of entries using a quadratic probe starting from
// `hash_index`.
@@ -799,41 +1034,11 @@ auto ViewImpl<InputKeyT, InputValueT, InputKeyContextT>::LookupEntry(
} while (LLVM_UNLIKELY(true));
}
// Note that we force inlining here because we expect to be called with lambdas
// that will in turn be inlined to form the loop body. We don't want function
// boundaries within the loop for performance, and recognizing the degree of
// simplification from inlining these callbacks may be difficult to
// automatically recognize.
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
template <typename EntryCallbackT, typename GroupCallbackT>
[[clang::always_inline]] auto
ViewImpl<InputKeyT, InputValueT, InputKeyContextT>::ForEachEntry(
EntryCallbackT entry_callback, GroupCallbackT group_callback) const
-> void {
uint8_t* local_metadata = metadata();
EntryT* local_entries = entries();
ssize_t local_size = alloc_size_;
for (ssize_t group_index = 0; group_index < local_size;
group_index += GroupSize) {
auto g = MetadataGroup::Load(local_metadata, group_index);
auto present_matched_range = g.MatchPresent();
if (!present_matched_range) {
continue;
}
for (ssize_t byte_index : present_matched_range) {
entry_callback(local_entries[group_index + byte_index]);
}
group_callback(&local_metadata[group_index]);
}
}
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
auto ViewImpl<InputKeyT, InputValueT, InputKeyContextT>::ComputeMetricsImpl(
KeyContextT key_context) const -> Metrics {
uint8_t* local_metadata = metadata();
EntryT* local_entries = entries();
EntryT* local_entries = entries_data();
ssize_t local_size = alloc_size_;
Metrics metrics;
@@ -898,6 +1103,147 @@ auto ViewImpl<InputKeyT, InputValueT, InputKeyContextT>::ComputeMetricsImpl(
return metrics;
}
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
[[clang::always_inline]] auto
ViewImpl<InputKeyT, InputValueT, InputKeyContextT>::EntriesImpl() const
-> EntryRange {
return EntryRange(*this);
}
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
[[clang::always_inline]]
ViewImpl<InputKeyT, InputValueT, InputKeyContextT>::EntryRange::Iterator::
Iterator(const EntryRange& range) {
const ViewImpl& view = range.view_;
ssize_t alloc_size = view.alloc_size_;
// An empty or moved-from table has no groups to load from, and the
// default-initialized state left behind already compares equal to `end()`.
if (alloc_size == 0 || view.storage_ == nullptr) {
return;
}
#ifndef NDEBUG
entries_ = view.entries_data();
metadata_ = view.metadata();
// The starting group and stride were drawn when the range was constructed,
// so every iterator built from it walks the same order.
group_index_ = range.start_group_;
group_entries_ = entries_ + group_index_;
groups_remaining_ = alloc_size / GroupSize - 1;
probe_mask_ = ComputeProbeMaskFromSize(alloc_size);
step_ = range.step_;
auto g = MetadataGroup::Load(metadata_, group_index_);
#else
// The allocation size bounds the metadata array directly, so anchoring at
// the ends of the arrays lets the walk run off a single induction variable
// without ever dividing by the group size.
entries_end_ = view.entries_data() + alloc_size;
metadata_end_ = view.metadata() + alloc_size;
group_offset_ = -alloc_size;
auto g = MetadataGroup::Load(metadata_end_, group_offset_);
#endif
auto present_range = g.MatchPresent();
if (present_range) {
present_bits_ = static_cast<MatchBitsT>(present_range);
} else {
AdvanceToNextPresentGroup();
}
}
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
[[clang::always_inline]] auto
ViewImpl<InputKeyT, InputValueT,
InputKeyContextT>::EntryRange::Iterator::AdvanceToNextPresentGroup()
-> void {
#ifndef NDEBUG
while (--groups_remaining_ >= 0) {
group_index_ = static_cast<ssize_t>(
static_cast<size_t>(group_index_ + step_) & probe_mask_);
auto g = MetadataGroup::Load(metadata_, group_index_);
auto range = g.MatchPresent();
if (range) {
group_entries_ = entries_ + group_index_;
present_bits_ = static_cast<MatchBitsT>(range);
return;
}
}
#else
for (group_offset_ += GroupSize; group_offset_ != 0;
group_offset_ += GroupSize) {
auto g = MetadataGroup::Load(metadata_end_, group_offset_);
auto range = g.MatchPresent();
if (range) {
present_bits_ = static_cast<MatchBitsT>(range);
return;
}
}
#endif
present_bits_ = 0;
}
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
ViewImpl<InputKeyT, InputValueT, InputKeyContextT>::EntryRange::EntryRange(
ViewImpl view)
: view_(view) {
#ifndef NDEBUG
if (view_.alloc_size_ <= 0 || view_.storage_ == nullptr) {
return;
}
initial_metadata_hash_ = Carbon::HashValue(
llvm::ArrayRef<uint8_t>(view_.metadata(), view_.alloc_size_));
// Draw the traversal order once, here, so that `begin()` remains a pure
// function of the range and can be called repeatedly. Two separately
// constructed ranges still walk the table in different orders.
start_group_ = NextRangeEntropy().ExtractIndex() &
ComputeProbeMaskFromSize(view_.alloc_size_);
// Walk the groups with a stride of an odd number of groups. The group count
// is always a power of two, so any odd stride is coprime with it and visits
// every group exactly once before repeating. That scrambles the group order
// far more thoroughly than a forward or reverse scan, and costs nothing in
// the loop itself as the increment already adds a stride and masks.
ssize_t num_groups = view_.alloc_size_ / GroupSize;
ssize_t stride_groups =
(NextRangeEntropy().ExtractIndex() & (num_groups - 1)) | 1;
step_ = stride_groups * GroupSize;
#endif
}
#ifndef NDEBUG
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
auto ViewImpl<InputKeyT, InputValueT,
InputKeyContextT>::EntryRange::CheckInvariants() const -> void {
if (view_.alloc_size_ <= 0 || view_.storage_ == nullptr) {
return;
}
HashCode current_hash = Carbon::HashValue(
llvm::ArrayRef<uint8_t>(view_.metadata(), view_.alloc_size_));
CARBON_CHECK(current_hash == initial_metadata_hash_,
"Hashtable mutated during iteration: metadata changed!");
}
#endif
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
[[clang::always_inline]] auto
ViewImpl<InputKeyT, InputValueT, InputKeyContextT>::EntryRange::begin() const
-> Iterator {
// The traversal order is fixed when the range is constructed, so repeated
// calls yield equal iterators as forward ranges require.
return Iterator(*this);
}
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
[[clang::always_inline]] auto
ViewImpl<InputKeyT, InputValueT, InputKeyContextT>::EntryRange::end() const
-> Iterator {
return Iterator();
}
// TODO: Evaluate whether it is worth forcing this out-of-line given the
// reasonable ABI boundary it forms and large volume of code necessary to
// implement it.
@@ -921,7 +1267,7 @@ auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::InsertImpl(
ssize_t group_with_deleted_index;
MetadataGroup::MatchIndex deleted_match = {};
EntryT* local_entries = entries();
EntryT* local_entries = entries_data();
auto return_insert_at_index = [&](ssize_t index) -> std::pair<EntryT*, bool> {
// We'll need to insert at this index so set the control group byte to the
@@ -1017,7 +1363,7 @@ BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::GrowToAllocSizeImpl(
bool old_small = is_small();
Storage* old_storage = storage();
uint8_t* old_metadata = metadata();
EntryT* old_entries = entries();
EntryT* old_entries = entries_data();
// Configure for the new size and allocate the new storage.
alloc_size() = target_alloc_size;
@@ -1093,7 +1439,7 @@ auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::EraseImpl(
// If we mark the slot as empty, we'll also need to increase the growth
// budget.
uint8_t* local_metadata = metadata();
EntryT* local_entries = entries();
EntryT* local_entries = entries_data();
ssize_t index = entry - local_entries;
ssize_t group_index = index & ~GroupMask;
auto g = MetadataGroup::Load(local_metadata, group_index);
@@ -1114,16 +1460,10 @@ auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::EraseImpl(
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::ClearImpl() -> void {
view_impl_.ForEachEntry(
[](EntryT& entry) {
if constexpr (!EntryT::IsTriviallyDestructible) {
entry.Destroy();
}
},
[](uint8_t* metadata_group) {
// Clear the group.
std::memset(metadata_group, 0, GroupSize);
});
DestroyEntries();
if (storage() != nullptr) {
std::memset(metadata(), 0, alloc_size());
}
growth_budget_ = GrowthThresholdForAllocSize(alloc_size());
}
@@ -1186,10 +1526,7 @@ auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::Destroy() -> void {
}
// Destroy all the entries.
if constexpr (!EntryT::IsTriviallyDestructible) {
view_impl_.ForEachEntry([](EntryT& entry) { entry.Destroy(); },
[](auto...) {});
}
DestroyEntries();
// If small, nothing to deallocate.
if (is_small()) {
@@ -1201,6 +1538,16 @@ auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::Destroy() -> void {
Deallocate(storage(), alloc_size());
}
template <typename InputKeyT, typename InputValueT, typename InputKeyContextT>
auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::DestroyEntries()
-> void {
if constexpr (!EntryT::IsTriviallyDestructible) {
for (typename EntryT::RefT entry : view_impl_.EntriesImpl()) {
EntryT::DestroyRef(entry);
}
}
}
// Copy all of the slots over from another table that is exactly the same
// allocation size.
//
@@ -1224,9 +1571,9 @@ auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::CopySlotsFrom(
// all of the keys. This is especially important as we don't have an easy way
// to access the key context needed for rehashing here.
uint8_t* local_metadata = metadata();
EntryT* local_entries = entries();
EntryT* local_entries = entries_data();
const uint8_t* local_arg_metadata = arg.metadata();
const EntryT* local_arg_entries = arg.entries();
const EntryT* local_arg_entries = arg.entries_data();
memcpy(local_metadata, local_arg_metadata, local_size);
for (ssize_t group_index = 0; group_index < local_size;
@@ -1269,9 +1616,9 @@ auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::MoveFrom(
// themselves. We do this preserving their slots and even tombstones to
// avoid rehashing.
uint8_t* local_metadata = this->metadata();
EntryT* local_entries = this->entries();
EntryT* local_entries = this->entries_data();
uint8_t* local_arg_metadata = arg.metadata();
EntryT* local_arg_entries = arg.entries();
EntryT* local_arg_entries = arg.entries_data();
memcpy(local_metadata, local_arg_metadata, local_size);
if (EntryT::IsTriviallyRelocatable) {
memcpy(local_entries, local_arg_entries, local_size * sizeof(EntryT));
@@ -1306,7 +1653,7 @@ auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::InsertIntoEmpty(
HashCode hash) -> EntryT* {
auto [hash_index, tag] = hash.ExtractIndexAndTag<7>();
uint8_t* local_metadata = metadata();
EntryT* local_entries = entries();
EntryT* local_entries = entries_data();
for (ProbeSequence s(hash_index, alloc_size());; s.Next()) {
ssize_t group_index = s.index();
@@ -1392,7 +1739,7 @@ auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::GrowToNextAllocSize(
bool old_small = is_small();
Storage* old_storage = storage();
uint8_t* old_metadata = metadata();
EntryT* old_entries = entries();
EntryT* old_entries = entries_data();
#ifndef NDEBUG
// Count how many of the old table slots will end up being empty after we grow
@@ -1417,7 +1764,7 @@ auto BaseImpl<InputKeyT, InputValueT, InputKeyContextT>::GrowToNextAllocSize(
// Now extract the new components of the table.
uint8_t* new_metadata = metadata();
EntryT* new_entries = entries();
EntryT* new_entries = entries_data();
// Walk the metadata groups, clearing deleted to empty, duplicating the
// metadata for the low and high halves, and updating it based on where each
@@ -1596,10 +1943,7 @@ auto TableImpl<InputBaseT, SmallSize>::operator=(const TableImpl& arg)
return *this;
}
CARBON_DCHECK(arg.storage() != this->storage());
if constexpr (!EntryT::IsTriviallyDestructible) {
this->view_impl_.ForEachEntry([](EntryT& entry) { entry.Destroy(); },
[](auto...) {});
}
this->DestroyEntries();
} else {
// The sizes don't match so destroy everything and re-setup the table
// storage.
+20 -21
View File
@@ -7,6 +7,7 @@
#include <concepts>
#include <type_traits>
#include <utility>
#include "common/check.h"
#include "common/hashtable_key_context.h"
@@ -60,6 +61,10 @@ class SetView : RawHashtable::ViewImpl<InputKeyT, void, InputKeyContextT> {
using KeyContextT = ImplT::KeyContextT;
using MetricsT = ImplT::MetricsT;
// A range over the keys of the set. Bound to the lifetime of the viewed set,
// and invalidated by mutating it.
using Range = ImplT::EntryRange;
// This type represents the result of lookup operations. It encodes whether
// the lookup was a success as well as accessors for the key.
class LookupResult {
@@ -91,10 +96,8 @@ class SetView : RawHashtable::ViewImpl<InputKeyT, void, InputKeyContextT> {
auto Lookup(LookupKeyT lookup_key,
KeyContextT key_context = KeyContextT()) const -> LookupResult;
// Run the provided callback for every key in the set.
template <typename CallbackT>
auto ForEach(CallbackT callback) const -> void
requires(std::invocable<CallbackT, KeyT&>);
// Returns a range for iterating over all keys in the set.
auto entries() const -> Range;
// This routine is relatively inefficient and only intended for use in
// benchmarking or logging of performance anomalies. The specific metrics
@@ -131,7 +134,7 @@ class SetView : RawHashtable::ViewImpl<InputKeyT, void, InputKeyContextT> {
// A pointer or reference to this type is the preferred way to pass a mutable
// handle to a `Set` type across API boundaries as it avoids encoding specific
// SSO sizing information while providing a near-complete mutable API.
template <typename InputKeyT, typename InputKeyContextT>
template <typename InputKeyT, typename InputKeyContextT = DefaultKeyContext>
class SetBase
: protected RawHashtable::BaseImpl<InputKeyT, void, InputKeyContextT> {
protected:
@@ -143,6 +146,7 @@ class SetBase
using ViewT = SetView<KeyT, KeyContextT>;
using LookupResult = ViewT::LookupResult;
using MetricsT = ImplT::MetricsT;
using Range = ViewT::Range;
// The result type for insertion operations both indicates whether an insert
// was needed (as opposed to the key already being in the set), and provides
@@ -190,12 +194,12 @@ class SetBase
}
// Convenience forwarder to the view type.
template <typename CallbackT>
auto ForEach(CallbackT callback) const -> void
requires(std::invocable<CallbackT, KeyT&>)
{
return ViewT(*this).ForEach(callback);
}
auto entries() const& -> Range { return ViewT(*this).entries(); }
// Deleted on rvalues: the range refers to storage owned by this table, so a
// range built from a temporary set would dangle. Both qualifiers are needed
// as `&&` alone would leave a const rvalue binding to the `const&` overload.
auto entries() && = delete;
auto entries() const&& = delete;
// Convenience forwarder to the view type.
auto ComputeMetrics(KeyContextT key_context = KeyContextT()) const
@@ -211,10 +215,10 @@ class SetBase
auto Insert(LookupKeyT lookup_key, KeyContextT key_context = KeyContextT())
-> InsertResult;
// Insert a key into the map and call the provided callback if necessary to
// produce a new key when no existing value is found.
// Insert a key into the set and call the provided callback if necessary to
// produce a new key when no existing key is found.
//
// Example: `m.Insert(key_equivalent, [] { return real_key; });`
// Example: `s.Insert(key_equivalent, [] { return real_key; });`
//
// The point of this function is when the lookup key is _different_from the
// stored key. However, we don't restrict it in case that blocks generic
@@ -333,13 +337,8 @@ auto SetView<InputKeyT, InputKeyContextT>::Lookup(LookupKeyT lookup_key,
}
template <typename InputKeyT, typename InputKeyContextT>
template <typename CallbackT>
auto SetView<InputKeyT, InputKeyContextT>::ForEach(CallbackT callback) const
-> void
requires(std::invocable<CallbackT, KeyT&>)
{
this->ForEachEntry([callback](EntryT& entry) { callback(entry.key()); },
[](auto...) {});
auto SetView<InputKeyT, InputKeyContextT>::entries() const -> Range {
return this->ImplT::EntriesImpl();
}
template <typename InputKeyT, typename InputKeyContextT>
+70 -1
View File
@@ -35,8 +35,9 @@ static constexpr bool IsCarbonSet = IsCarbonSetImpl<SetT>::value;
// support different APIs. The primary template assumes a roughly
// `std::unordered_set` API design, and types with a different API design are
// supported through specializations.
template <typename SetT>
template <typename InSetT>
struct SetWrapperImpl {
using SetT = InSetT;
using KeyT = SetT::key_type;
SetT s;
@@ -58,6 +59,17 @@ struct SetWrapperImpl {
}
auto BenchErase(KeyT k) -> bool { return s.erase(k) != 0; }
// Visits every key in the set, calling `cb` with each one. Each set type is
// expected to traverse using whatever API it provides for this, so that the
// benchmark measures iterating the set rather than any specific iteration
// API.
template <typename CallbackT>
auto BenchIterate(CallbackT cb) -> void {
for (const auto& k : s) {
cb(k);
}
}
};
// Explicit (partial) specialization for the Carbon map type that uses its
@@ -85,6 +97,13 @@ struct SetWrapperImpl<Set<KT, MinSmallSize>> {
}
auto BenchErase(KeyT k) -> bool { return s.Erase(k); }
template <typename CallbackT>
auto BenchIterate(CallbackT cb) -> void {
for (const auto& k : s.entries()) {
cb(k);
}
}
};
// Provide a way to override the Carbon Set specific benchmark runs with another
@@ -123,6 +142,17 @@ using SetWrapper =
SetWrapperOverride<SetT, SetOverride::CARBON_SET_BENCH_OVERRIDE>;
#endif
// Reports extra statistics about the table, when it is in fact a Carbon table.
// Note that this has to inspect the *wrapped* type in order to work correctly
// when the Carbon benchmarks are overridden with another implementation.
template <typename SetT>
auto ReportMetrics(const SetWrapper<SetT>& s_wrapper, benchmark::State& state)
-> void {
if constexpr (IsCarbonSet<typename SetWrapper<SetT>::SetT>) {
ReportTableMetrics(s_wrapper.s, state);
}
}
// NOLINTBEGIN(bugprone-macro-parentheses): Parentheses are incorrect here.
#define MAP_BENCHMARK_ONE_OP_SIZE(NAME, APPLY, KT) \
BENCHMARK(NAME<Set<KT>>)->Apply(APPLY); \
@@ -375,5 +405,44 @@ static void BM_SetInsertSeq(benchmark::State& state) {
}
MAP_BENCHMARK_OP_SEQ(BM_SetInsertSeq);
// Benchmark visiting every key in a set.
//
// Unlike the lookup benchmarks, this walks the table's storage from end to end
// rather than probing it, so it is largely a measure of how densely keys are
// packed and how cheaply empty slots can be skipped. There is no dependency
// between the keys visited, and so this is a throughput measurement.
//
// Each batch is a single complete traversal of the set, with the batch size set
// to the number of keys so that the reported time is the per-key cost.
template <typename SetT>
static void BM_SetIterate(benchmark::State& state) {
using SetWrapperT = SetWrapper<SetT>;
using KT = typename SetWrapperT::KeyT;
SetWrapperT s;
auto [keys, _] = GetKeysAndMissKeys<KT>(state.range(0));
for (auto k : keys) {
bool inserted = s.BenchInsert(k);
CARBON_DCHECK(inserted, "Must be a successful insert!");
}
while (state.KeepRunningBatch(keys.size())) {
ssize_t sum = 0;
s.BenchIterate([&sum](const KT& k) {
// Consume the key so that neither the traversal nor the loads out of the
// entries can be optimized away.
sum += ValueToBool(k);
});
benchmark::DoNotOptimize(sum);
}
// The time is already per-key, so an iteration-invariant rate of one gives
// the throughput of keys visited.
state.counters["KeyRate"] =
benchmark::Counter(1, benchmark::Counter::kIsIterationInvariantRate);
ReportMetrics(s, state);
}
MAP_BENCHMARK_ONE_OP(BM_SetIterate, SizeArgs);
} // namespace
} // namespace Carbon
+181 -1
View File
@@ -7,7 +7,12 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <concepts>
#include <initializer_list>
#include <iterator>
#include <ranges>
#include <set>
#include <string>
#include <type_traits>
#include <vector>
@@ -19,6 +24,7 @@ namespace {
using RawHashtable::IndexKeyContext;
using RawHashtable::MoveOnlyTestData;
using RawHashtable::TestData;
using ::testing::UnorderedElementsAre;
using ::testing::UnorderedElementsAreArray;
template <typename SetT, typename MatcherRangeT>
@@ -26,7 +32,9 @@ auto ExpectSetElementsAre(SetT&& s, MatcherRangeT element_matchers) -> void {
// Collect the elements into a container.
using KeyT = std::remove_reference<SetT>::type::KeyT;
std::vector<std::reference_wrapper<KeyT>> entries;
s.ForEach([&entries](KeyT& k) { entries.push_back(std::ref(k)); });
for (auto& k : s.entries()) {
entries.push_back(std::ref(k));
}
// Use the GoogleMock unordered container matcher to validate and show errors
// on wrong elements.
@@ -176,6 +184,8 @@ TYPED_TEST(SetTest, Move) {
SetT other_s1 = std::move(s);
ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 24)));
// A moved-from set has a size but no storage, and must iterate as empty.
EXPECT_EQ(s.entries().begin(), s.entries().end());
// Add some more elements.
for (int i : llvm::seq(24, 32)) {
@@ -432,5 +442,175 @@ TEST(SetContextTest, Basic) {
ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 512)));
}
TYPED_TEST(SetTest, Range) {
using SetT = TypeParam;
using Range = decltype(std::declval<const SetT&>().entries());
using Iter = typename Range::Iterator;
static_assert(std::forward_iterator<Iter>);
static_assert(std::same_as<decltype(std::declval<Range>().begin()), Iter>);
static_assert(std::same_as<decltype(std::declval<Range>().end()), Iter>);
static_assert(std::ranges::forward_range<Range>);
static_assert(std::ranges::common_range<Range>);
SetT s;
EXPECT_EQ(s.entries().begin(), s.entries().end());
for (const auto& k : s.entries()) {
static_cast<void>(k);
FAIL() << "Empty set range should have no elements";
}
for (int i = 1; i <= 5; ++i) {
s.Insert(i);
}
// Range-for traversal by const ref.
int count = 0;
for (const auto& k : s.entries()) {
EXPECT_GE(k, 1);
EXPECT_LE(k, 5);
++count;
}
EXPECT_EQ(count, 5);
// Direct GMock container matching.
EXPECT_THAT(s.entries(), UnorderedElementsAre(1, 2, 3, 4, 5));
// Const view range iteration.
using KeyT = typename SetT::KeyT;
using KeyContextT = typename SetT::KeyContextT;
SetView<const KeyT, KeyContextT> cv = s;
int cv_count = 0;
for (const auto& k : cv.entries()) {
static_assert(std::is_const_v<std::remove_reference_t<decltype(k)>>);
EXPECT_GE(k, 1);
EXPECT_LE(k, 5);
++cv_count;
}
EXPECT_EQ(cv_count, 5);
EXPECT_THAT(cv.entries(), UnorderedElementsAre(1, 2, 3, 4, 5));
// Explicit iterator traversal, dereference, and post-increment.
auto r = s.entries();
int iter_count = 0;
for (auto it = r.begin(); it != r.end(); ++it) {
EXPECT_NE(*it, 0);
++iter_count;
}
EXPECT_EQ(iter_count, 5);
auto it = r.begin();
auto prev = it++;
EXPECT_NE(it, prev);
}
TYPED_TEST(MoveOnlySetTest, Range) {
TypeParam s;
s.Insert(1);
s.Insert(2);
int count = 0;
for (const auto& k : s.entries()) {
EXPECT_GT(k.value, 0);
++count;
}
EXPECT_EQ(count, 2);
}
#ifndef NDEBUG
TEST(SetDeathTest, MutateDuringIterationFails) {
EXPECT_DEATH(([] {
Set<int> s;
s.Insert(1);
auto range = s.entries();
s.Insert(2);
}()),
"Hashtable mutated during iteration");
}
#endif
// A range outlives the *view* it was built from: views don't own storage, and
// the range copies the view rather than pointing at it.
TEST(SetTest, RangeOutlivesTemporaryView) {
Set<int> s;
s.Insert(1);
auto make_view = [&s]() -> SetView<int> { return s; };
auto range = make_view().entries();
EXPECT_THAT(range, UnorderedElementsAre(1));
}
#ifdef NDEBUG
// Release iteration state is two end pointers, a group offset, and the
// present-bit mask; it needs to stay small enough to live in registers across
// the loop. Debug builds add the randomized walk and mutation-check state.
static_assert(sizeof(Set<int>::Range::Iterator) <= 4 * sizeof(void*));
#endif
// Forward ranges guarantee multi-pass: `begin()` must be a pure function of the
// range. Debug builds draw their traversal entropy when the range is
// constructed rather than in `begin()` precisely so that repeated calls start
// from the same group.
TEST(SetTest, RangeIsMultiPass) {
Set<int, 16> s;
for (int i = 1; i <= 64; ++i) {
s.Insert(i);
}
auto range = s.entries();
EXPECT_EQ(range.begin(), range.begin());
// Two passes over the same range must agree on both the keys visited and the
// order they're visited in.
std::vector<int> first;
for (int k : range) {
first.push_back(k);
}
std::vector<int> second;
for (int k : range) {
second.push_back(k);
}
EXPECT_EQ(first, second);
EXPECT_EQ(static_cast<ssize_t>(first.size()), 64);
}
// Whatever order a range picks, it has to be a genuine permutation of the
// table. Debug builds additionally vary that order between ranges over the same
// table so that callers can't come to depend on it.
TEST(SetTest, TraversalOrderIsAVaryingPermutation) {
Set<int, 64> s;
std::vector<int> inserted;
// Enough keys to populate every group of the small storage.
for (int i = 0; i < 36; ++i) {
int key = i * 17 + 7;
EXPECT_TRUE(s.Insert(key).is_inserted());
inserted.push_back(key);
}
std::set<std::vector<int>> distinct_orders;
for (int i = 0; i < 64; ++i) {
std::vector<int> visited;
for (int k : s.entries()) {
visited.push_back(k);
}
// A walk that skipped a group would drop keys and one that revisited a
// group would duplicate them, so comparing as a multiset covers both. This
// is what makes an odd group stride a valid traversal.
EXPECT_THAT(visited, UnorderedElementsAreArray(inserted));
distinct_orders.insert(visited);
}
#ifndef NDEBUG
// Debug builds randomize both the starting group and the stride, so across
// this many ranges we should see more than the two orders (pure forward and
// pure reverse) that a simple direction flip would produce.
EXPECT_GT(distinct_orders.size(), 2)
<< "Debug traversal order does not appear to be randomized.";
#else
// Release builds always scan the groups in order.
EXPECT_EQ(distinct_orders.size(), 1);
#endif
}
} // namespace
} // namespace Carbon
+25 -6
View File
@@ -1,16 +1,35 @@
// 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
package Core library "prelude/destroy";
// TODO: Add `Destructor`, as in:
// interface Destructor {
// private fn Op(ref self);
// TODO: uncomment when `require impls` adds a witness table entry.
// interface SubobjectDestroy {
// final fn Op(ref self) = "subobject.destroy";
// }
// Destroys objects. This will invoke `Destructor` impls recursively on members;
// it does not deallocate memory.
interface Destroy {
// TODO: uncomment when `require impls` adds a witness table entry.
// require impls SubobjectDestroy;
// TODO: change `Self` to `partial Self`.
fn Op(ref self);
// TODO: remove when `require impls` adds a witness table entry.
final fn SubobjectDestroy(unused ref self) {
// This function body is ignored by the toolchain. We can't use
// a built-in because the compiler treats it as an error. We
// should address this at some point, but it's not a high
// priority right now.
}
final fn SelfDestruct(ref self) {
self.Op();
self.SubobjectDestroy();
}
}
// TODO: uncomment when `require impls` adds a witness table entry.
// impl forall [T: SubobjectDestroy] partial T as Destroy {
// alias Op = T.Op;
// }
+18
View File
@@ -333,6 +333,12 @@ jj config set --repo 'revset-aliases."trunk()"' 'trunk@upstream'
# Treat github.com/carbon-language/carbon-lang as immutable, but treat your fork
# as mutable.
jj config set --repo 'revset-aliases."immutable_heads()"' 'remote_bookmarks(*, upstream)'
# Run `prek` over the commits a push would send, and push only if they pass.
jj config set --repo aliases.push '["util", "exec", "--", "sh", "-c", "exec \"$(jj workspace root)/scripts/jj_push.sh\" \"$@\"", "jj push"]'
# Run `prek` over the changes between `trunk()` and `@`.
jj config set --repo aliases.prek '["util", "exec", "--", "sh", "-c", "exec \"$(jj workspace root)/scripts/jj_prek.sh\" \"$@\"", "jj prek"]'
```
<!-- google-doc-style-resume -->
@@ -341,6 +347,18 @@ The above assumes that you have configured the remote name `origin` to refer to
your fork and `upstream` to refer to `github.com/carbon-language/carbon-lang`,
and will need to be adjusted if you use different remote names.
The `prek` alias runs [`scripts/jj_prek.sh`](/scripts/jj_prek.sh), which runs
`prek` against `@` from anywhere in the workspace, including a non-colocated
one. Arguments go to `prek run`, so `jj prek --all-files` checks everything.
The `push` alias runs [`scripts/jj_push.sh`](/scripts/jj_push.sh), which checks
the commits the push would send and leaves anything the hooks change in a commit
for you to squash. It takes the same arguments as `jj git push`, and `--dry-run`
still runs the checks. `jj` only knows the name of an alias, not what it expands
to, so it completes file names after `jj push`.
[`scripts/completions`](/scripts/completions/README.md) has Bash, Zsh, and Fish
completions for the alias.
#### AI assistants
When using AI assistants and reviewing terminal commands, some commands which
+57
View File
@@ -0,0 +1,57 @@
<!--
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
-->
# Shell completions for `jj push`
Completions for a `jj push` alias that runs
[`scripts/jj_push.sh`](/scripts/jj_push.sh). See
[the Jujutsu section of the contribution tools doc](/docs/project/contribution_tools.md#jujutsu-jj)
to set up the alias.
`jj` only knows the name of an alias, not what it expands to, so it completes
file names after `jj push`. Each file here rewrites `push` to `git push` in the
command line before passing it to `jj`, giving the alias the flags, bookmarks,
revsets, and remotes of `jj git push`.
Each file loads `jj`'s own completions itself, and needs `jj` on `PATH` when it
runs. Remove any other setup that loads `jj`'s completions.
Run the commands below from your Carbon checkout, so that
`jj workspace root` fills in its path.
## Bash
```sh
echo "source $(jj workspace root)/scripts/completions/jj_push.bash" >>~/.bashrc
```
If your distribution ships a `jj` file in
`/usr/share/bash-completion/completions`, Bash loads it on demand and it
overrides this. Symlink this file to
`~/.local/share/bash-completion/completions/jj` instead of sourcing it.
## Zsh
```sh
echo "source $(jj workspace root)/scripts/completions/jj_push.zsh" >>~/.zshrc
```
This has to come after `compinit` in `.zshrc`, so move the line if `compinit`
runs later in the file.
## Fish
Fish loads completions on demand, after running `config.fish`, so sourcing this
at startup doesn't work: what fish loads later is added on top. Install it as
the file fish loads for `jj`:
```sh
ln -s "$(jj workspace root)/scripts/completions/jj_push.fish" \
~/.config/fish/completions/jj.fish
```
`~/.config/fish/completions` is first in `$fish_complete_path`, so this
overrides any `jj.fish` from your distribution.
+41
View File
@@ -0,0 +1,41 @@
# 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
#
# Bash completions for a `jj push` alias that runs `scripts/jj_push.sh`. See
# `scripts/completions/README.md` for how to install this.
#
# `jj` only knows the name of an alias, not what it expands to, so it completes
# file names after `jj push`. Rewriting `push` to `git push` in the command line
# before passing it to `jj` gets the completions of `jj git push`.
source <(COMPLETE=bash jj)
_carbon_jj_complete() {
# Shadow the two variables `jj`'s completion function reads. Bash scopes them
# dynamically, so it sees the rewrite below.
local -a COMP_WORDS=("${COMP_WORDS[@]}")
local COMP_CWORD=$COMP_CWORD
local i
# Only look before the cursor. A `push` at the cursor is still being typed.
for ((i = 1; i < COMP_CWORD; i++)); do
case "${COMP_WORDS[i]}" in
-*) ;;
push)
COMP_WORDS=("${COMP_WORDS[@]:0:i}" git push "${COMP_WORDS[@]:i+1}")
((COMP_CWORD++))
break
;;
*) break ;;
esac
done
_clap_complete_jj "$@"
}
if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then
complete -o nospace -o bashdefault -o nosort -F _carbon_jj_complete jj
else
complete -o nospace -o bashdefault -F _carbon_jj_complete jj
fi
+37
View File
@@ -0,0 +1,37 @@
# 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
#
# Fish completions for a `jj push` alias that runs `scripts/jj_push.sh`. See
# `scripts/completions/README.md` for how to install this.
#
# `jj` only knows the name of an alias, not what it expands to, so it completes
# file names after `jj push`. Rewriting `push` to `git push` in the command line
# before passing it to `jj` gets the completions of `jj git push`.
#
# This replaces the completions fish loads for `jj`, so it has to be installed
# with that file's name. Loading both leaves `jj`'s file-name completion
# registered.
function __jj_completion_tokens --description 'Command line tokens, with the `push` alias expanded'
# `--cut-at-cursor` drops the token being completed, so any `push` here is
# a complete word.
set -l tokens (commandline --current-process --tokenize --cut-at-cursor)
for i in (seq 2 (count $tokens))
switch $tokens[$i]
case '-*'
continue
case push
printf '%s\n' $tokens[1..(math $i - 1)] git push \
$tokens[(math $i + 1)..-1]
return
case '*'
break
end
end
printf '%s\n' $tokens
end
complete -e -c jj
complete --keep-order --exclusive --command jj \
--arguments "(COMPLETE=fish jj -- (__jj_completion_tokens) (commandline --current-token))"
+33
View File
@@ -0,0 +1,33 @@
# 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
#
# Zsh completions for a `jj push` alias that runs `scripts/jj_push.sh`. See
# `scripts/completions/README.md` for how to install this.
#
# `jj` only knows the name of an alias, not what it expands to, so it completes
# file names after `jj push`. Rewriting `push` to `git push` in the command line
# before passing it to `jj` gets the completions of `jj git push`.
source <(COMPLETE=zsh jj)
_carbon_jj_complete() {
local i
# Only look before the cursor. A `push` at the cursor is still being typed.
for ((i = 2; i < CURRENT; i++)); do
case ${words[i]} in
-*) ;;
push)
words=(${words[1, i - 1]} git push ${words[i + 1, -1]})
((CURRENT++))
break
;;
*) break ;;
esac
done
_clap_dynamic_completer_jj "$@"
}
compdef _carbon_jj_complete jj
+13 -2
View File
@@ -15,6 +15,12 @@ set -eu
# both check the wrong thing and fail to write back their fixes.
HEAD="$(jj show --no-patch -r @ --template 'commit_id')"
# Run from the workspace root. Setting `GIT_DIR` makes git treat the current
# directory as the work tree, and prek looks there for its configuration, so
# running from a subdirectory would find neither. Hooks also expect paths
# relative to the root.
cd "$(jj workspace root --ignore-working-copy)"
# Find the .git directory. The working copy was snapshotted above, so this
# doesn't need to do so again.
export GIT_DIR="$(jj git root --ignore-working-copy)"
@@ -24,5 +30,10 @@ export GIT_INDEX_FILE="$(mktemp)"
trap 'rm -f "$GIT_INDEX_FILE"' EXIT
git read-tree "$HEAD"
# Run prek with the `.git` directory and index we built earlier.
prek run --from-ref trunk --to-ref "$HEAD"
# Run prek with the `.git` directory and index we built earlier. Arguments
# select what gets checked; with none, check everything between `trunk` and `@`.
if (($# > 0)); then
prek run "$@"
else
prek run --from-ref trunk --to-ref "$HEAD"
fi
+112
View File
@@ -0,0 +1,112 @@
#!/usr/bin/env bash
#
# 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
#
# Runs prek over the commits `jj git push` would send, and pushes only if they
# pass. Takes the same arguments as `jj git push`.
set -eu
JJ_PREK="$(dirname "${BASH_SOURCE[0]}")/jj_prek.sh"
# Succeeds if the working-copy commit is in the given revset. This snapshots the
# working copy, so it sees anything the hooks rewrote.
working_copy_is() {
[[ -n "$(jj log --no-graph -r "@ & ($1)" --template 'commit_id')" ]]
}
# Succeeds if the working copy is an empty, undescribed child of the target,
# which hooks can run in and write their fixes into.
working_copy_sits_on() {
working_copy_is "empty() & description(exact:\"\") & children($1)"
}
# Returns the working copy to where it started. jj discards an empty,
# undescribed commit when the working copy moves off it, so the original may be
# gone. Build a new one on the same parents in that case.
restore_working_copy() {
if [[ -n "$(jj log --no-graph --ignore-working-copy \
-r "present($ORIG_CHANGE)" --template 'commit_id')" ]]; then
jj edit --quiet "$ORIG_CHANGE"
else
jj new --quiet $ORIG_PARENTS
fi
}
# `--help` describes `jj git push`, and has nothing to check.
for arg in "$@"; do
case "$arg" in
-h | --help)
exec jj git push "$@"
;;
esac
done
# Ask jj what the push would do. This checks the arguments and lists the commits
# being sent, without contacting the remote. A `--dry-run` already in `$@` is
# harmless here, and still suppresses the push at the end.
if ! PLAN="$(jj git push --dry-run "$@" 2>&1)"; then
echo "$PLAN" >&2
exit 1
fi
REMOTE="$(sed -n 's/^Changes to push to \(.*\):$/\1/p' <<<"$PLAN")"
# Each updated bookmark or tag reports the commit it moves to. Deletions have no
# such commit, and send nothing to check.
TARGETS="$(sed -E -n 's/^ (bookmark|tag): .* to ([0-9a-f]{8,})\]$/\2/p' <<<"$PLAN" |
paste -sd '|' -)"
# Nothing to check, so just push.
if [[ -z "$REMOTE" || -z "$TARGETS" ]]; then
exec jj git push "$@"
fi
# Only the heads need checking; a head's range covers everything below it.
HEADS="$(jj log --no-graph --ignore-working-copy -r "heads($TARGETS)" \
--template 'commit_id ++ "\n"')"
ORIG_CHANGE="$(jj log --no-graph -r @ --template 'change_id')"
ORIG_PARENTS="$(jj log --no-graph -r 'parents(@)' --template 'commit_id ++ " "')"
for target in $HEADS; do
# Check with the working copy on top of the target.
made_scratch=0
if ! working_copy_sits_on "$target"; then
jj new --quiet "$target"
made_scratch=1
fi
# Check from the newest ancestor already on the remote. When there is none,
# there is no range to diff, so check every file.
base="$(jj log --no-graph --ignore-working-copy \
-r "heads(::$target & ::remote_bookmarks(remote=exact:$REMOTE))" \
--template 'commit_id ++ "\n"' | head -n 1)"
if [[ -n "$base" ]]; then
check=(--from-ref "$base" --to-ref "$target")
else
check=(--all-files)
fi
result=0
"$JJ_PREK" "${check[@]}" || result=$?
# Discard the scratch commit unless the hooks wrote something into it.
if ((made_scratch)) && working_copy_is 'empty()'; then
restore_working_copy
fi
if ((result)); then
echo >&2
if ! working_copy_is 'empty()'; then
change="$(jj log --no-graph -r @ --template 'change_id.shortest()')"
echo "Hooks changed files. They are in $change." >&2
fi
echo "Error: checks failed, nothing pushed." >&2
exit 1
fi
done
exec jj git push "$@"
+1
View File
@@ -86,6 +86,7 @@ Example usage:
"constant": "SemIR::MakeConstantId",
"constraint": "SemIR::MakeNamedConstraintId",
"declared_facet_type": "SemIR::MakeDeclaredFacetTypeId",
"default_value": "SemIR::MakeDefaultValueId",
"entity_name": "SemIR::MakeEntityNameId",
"function": "SemIR::MakeFunctionId",
"generated_function": "SemIR::MakeGeneratedFunctionId",
+7
View File
@@ -284,6 +284,13 @@ Supported comment markers are:
Output line matchers may contain `[[@LINE+offset]` and `{{regex}}` syntaxes,
similar to `FileCheck`.
When the file uses an `AUTOUPDATE-SPLIT`, only `CHECK` lines in that split
are matchers; elsewhere they are ordinary content. This is what allows a
split to hold a test file that itself contains `CHECK` lines, as the
language server's SemIR tests do. Note that such a split still can't contain
a `// ---` line, which would split the enclosing file; write the `/`
characters as `[[@0x2f]]` to avoid that.
- ```
// TIP: <tip>
```
+33 -9
View File
@@ -25,6 +25,9 @@ using ::testing::Matcher;
using ::testing::MatchesRegex;
using ::testing::StrEq;
// The name of the trailing split that autoupdate writes `CHECK` lines into.
static constexpr llvm::StringLiteral AutoupdateSplit = "AUTOUPDATE-SPLIT";
// Represents the different kinds of version-control conflict markers that are
// relevant for the autoupdater. One key concern here is the distinction between
// "snapshot" and "diff" conflict regions. Snapshot regions are the more
@@ -735,6 +738,22 @@ static auto TryConsumeSetFlag(llvm::StringRef line_trimmed,
return true;
}
// Returns whether the file uses a `// --- AUTOUPDATE-SPLIT` split.
//
// Autoupdate writes every `CHECK` into that split, so when one is present, a
// `CHECK` line anywhere else is part of the test input rather than an
// expectation. That's what allows a split to hold a test file that itself
// contains `CHECK` lines.
static auto UsesAutoupdateSplit(llvm::StringRef content) -> bool {
for (llvm::StringRef line : llvm::split(content, '\n')) {
llvm::StringRef trimmed = line.trim();
if (trimmed.consume_front("// ---") && trimmed.trim() == AutoupdateSplit) {
return true;
}
}
return false;
}
// Process content for either the main file (with `test_file` and
// `found_autoupdate` provided) or an included file (with those arguments null).
//
@@ -761,6 +780,8 @@ static auto ProcessFileContent(llvm::StringRef filename,
// Otherwise conflict markers are errors.
auto previous_conflict_marker = MarkerKind::None;
const bool uses_autoupdate_split = UsesAutoupdateSplit(content_cursor);
SplitState split_state;
while (!content_cursor.empty()) {
@@ -802,13 +823,18 @@ static auto ProcessFileContent(llvm::StringRef filename,
continue;
}
CARBON_ASSIGN_OR_RETURN(
is_consumed,
TryConsumeCheck(running_autoupdate, line_index, line, line_trimmed,
test_file ? &test_file->expected_stdout : nullptr,
test_file ? &test_file->expected_stderr : nullptr));
if (is_consumed) {
continue;
// `CHECK` lines are only expectations where autoupdate would write them.
// Everywhere else they're input, which is how a split can hold a test file
// that itself contains `CHECK` lines.
if (!uses_autoupdate_split || split_state.filename == AutoupdateSplit) {
CARBON_ASSIGN_OR_RETURN(
is_consumed,
TryConsumeCheck(running_autoupdate, line_index, line, line_trimmed,
test_file ? &test_file->expected_stdout : nullptr,
test_file ? &test_file->expected_stderr : nullptr));
if (is_consumed) {
continue;
}
}
if (test_file) {
@@ -895,8 +921,6 @@ auto ProcessTestFile(llvm::StringRef test_name, bool running_autoupdate)
return ErrorBuilder() << "Missing AUTOUPDATE/NOAUTOUPDATE setting";
}
constexpr llvm::StringLiteral AutoupdateSplit = "AUTOUPDATE-SPLIT";
// Validate AUTOUPDATE-SPLIT use, and remove it from test files if present.
if (test_file.has_splits) {
for (const auto& test_file :
@@ -0,0 +1,30 @@
// 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
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //testing/file_test:file_test_base_test --test_arg=--file_tests=testing/file_test/testdata/check_outside_autoupdate_split.carbon
// TIP: To dump output, run:
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/check_outside_autoupdate_split.carbon
// When the file uses an `AUTOUPDATE-SPLIT`, `CHECK` lines elsewhere are
// content rather than expectations, so a split can hold a test file of its
// own. Such a split still can't contain a literal `// ---` line, so the `/`
// characters are written as `[[@0x2f]]`.
//
// The echoed line numbers below show that both lines are part of `a.carbon`:
// if the `CHECK` had been consumed it would be an unmatched expectation, and
// if the `// ---` had split, there would be a second file in the arguments.
// --- a.carbon
// CHECK:STDOUT: not an expectation
this line follows a CHECK line
[[@0x2f]][[@0x2f]] --- not a split
this line follows a split marker
// --- AUTOUPDATE-SPLIT
// CHECK:STDOUT: 2 args: `default_args`, `a.carbon`
// CHECK:STDOUT: a.carbon:2: this line follows a CHECK line
// CHECK:STDOUT: a.carbon:4: this line follows a split marker
+36 -20
View File
@@ -18,28 +18,10 @@ import shlex
import subprocess
import sys
from pathlib import Path
from typing import Any
def main() -> None:
# Parse arguments.
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--non-fatal-checks", action="store_true")
parser.add_argument(
"--print_slowest_tests", default=0, help="Forwarded to file_test"
)
parser.add_argument("--threads", help="Forwarded to file_test")
parser.add_argument(
"--verbose", "-v", action="store_true", help="Produce verbose output"
)
parser.add_argument("files", nargs="*")
args = parser.parse_args()
printv = print if args.verbose else lambda _: None
bazel = str(Path(__file__).parents[1] / "scripts" / "run_bazel.py")
configs = []
# Use the most recently used build mode, or `fastbuild` if missing
# `bazel-bin`.
def _detect_build_mode(bazel: str, printv: Any) -> str:
build_mode = "fastbuild"
workspace = subprocess.check_output(
[
@@ -64,6 +46,40 @@ def main() -> None:
"Detected --compilation_mode: none (no `./bazel-bin`), "
+ f"falling back to {build_mode}"
)
return build_mode
def main() -> None:
# Parse arguments.
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("--non-fatal-checks", action="store_true")
parser.add_argument(
"--print_slowest_tests", default=0, help="Forwarded to file_test"
)
parser.add_argument("--threads", help="Forwarded to file_test")
parser.add_argument(
"--verbose", "-v", action="store_true", help="Produce verbose output"
)
parser.add_argument(
"--compilation-mode",
"-c",
help=(
"Compilation mode to use. The default is to detect the mode used "
"by the last bazel invocation"
),
choices=["dbg", "fastbuild", "opt"],
)
parser.add_argument("files", nargs="*")
args = parser.parse_args()
printv = print if args.verbose else lambda _: None
bazel = str(Path(__file__).parents[1] / "scripts" / "run_bazel.py")
configs = []
# Unless the user chose one explicitly, use the most recently-used build
# mode, or `fastbuild` if missing `bazel-bin`.
build_mode = args.compilation_mode or _detect_build_mode(bazel, printv)
if args.non_fatal_checks:
if build_mode == "optimize":
+6 -9
View File
@@ -554,16 +554,13 @@ auto SourceGen::AppendUniqueIdentifiers(
// Append all the identifiers directly out of the set. We make no guarantees
// about the relative order so we just use the non-deterministic order of the
// set and avoid additional storage.
//
// TODO: It's awkward the `ForEach` here can't early-exit. This just walks the
// whole set which is harmless if inefficient. We should add early exiting
// the loop support to `Set` and update this code.
unique_idents.ForEach([&](llvm::StringRef ident) {
if (number > 0) {
dest.push_back(ident);
--number;
for (llvm::StringRef ident : unique_idents.entries()) {
if (number == 0) {
break;
}
});
dest.push_back(ident);
--number;
}
CARBON_CHECK(number == 0);
}
+23 -10
View File
@@ -42,9 +42,13 @@ enum class EntityKind : uint8_t {
} // namespace
// Resolves the callee expression in a call to a specific callee, or diagnoses
// if no specific callee can be identified. This verifies the arity of the
// callee and determines any compile-time arguments, but doesn't check that the
// runtime arguments are convertible to the parameter types.
// if no specific callee can be identified. This determines any compile-time
// arguments, but doesn't check that the runtime arguments are convertible to
// the parameter types. It also verifies that the number of arguments is within
// the range [callee_arity - arity_lower_bound_margin, callee_arity]. This
// allows arity matching when the callee has default arguments for some
// subpatterns. In all other cases supply the default value `0` for exact arity
// checking.
//
// `self_id` and `arg_ids` are the self argument and explicit arguments in the
// call.
@@ -56,14 +60,21 @@ static auto ResolveCalleeInCall(Context& context, SemIR::LocId loc_id,
EntityKind entity_kind_for_diagnostic,
SemIR::SpecificId enclosing_specific_id,
SemIR::InstId self_id,
llvm::ArrayRef<SemIR::InstId> arg_ids)
llvm::ArrayRef<SemIR::InstId> arg_ids,
int32_t arity_lower_bound_margin = 0)
-> std::optional<SemIR::SpecificId> {
// Check that the arity matches the explicit arguments.
// Check that the arity exactly matches or is the upper bound of the explicit
// arguments.
auto param_patterns =
context.inst_blocks().GetOrEmpty(entity.param_patterns_id);
size_t expected_args_size =
param_patterns.size() - (self_id.has_value() ? 1 : 0);
if (arg_ids.size() != expected_args_size) {
CARBON_CHECK(static_cast<size_t>(arity_lower_bound_margin) <=
expected_args_size);
size_t size_lower_bound =
expected_args_size - static_cast<size_t>(arity_lower_bound_margin);
if (arg_ids.size() < size_lower_bound ||
arg_ids.size() > expected_args_size) {
CARBON_DIAGNOSTIC(CallArgCountMismatch, Error,
"{0} argument{0:s} passed to "
"{1:=0:function|=1:generic class|=2:generic "
@@ -219,11 +230,13 @@ auto PerformCallToFunction(Context& context, SemIR::LocId loc_id,
llvm::ArrayRef<SemIR::InstId> arg_ids,
bool is_desugared) -> SemIR::InstId {
// If the callee is a generic function, determine the generic argument values
// for the call.
// for the call. Also check the arity of the function against the arguments,
// with allowance for default argument values.
const auto& function = context.functions().Get(callee_function.function_id);
auto callee_specific_id = ResolveCalleeInCall(
context, loc_id, context.functions().Get(callee_function.function_id),
EntityKind::Function, callee_function.enclosing_specific_id,
callee_function.self_id, arg_ids);
context, loc_id, function, EntityKind::Function,
callee_function.enclosing_specific_id, callee_function.self_id, arg_ids,
function.default_value_arity);
if (!callee_specific_id) {
return SemIR::ErrorInst::InstId;
}
+27 -8
View File
@@ -174,13 +174,15 @@ static auto CompareVirtualWithOverrider(const SemIR::Function& base_fn,
return OverrideMatchResult::Match;
}
// Builds and returns a vtable for the current class. Assumes that the virtual
// functions for the class are listed as the top element of the `vtable_stack`.
// Builds and returns a vtable for the current class, along with a bool
// indicating whether it is a Carbon-native vtable (false for a foreign vtable
// inherited from a C++ base class). Assumes that the virtual functions for the
// class are listed as the top element of the `vtable_stack`.
static auto BuildVtable(Context& context, Parse::ClassDefinitionId node_id,
SemIR::ClassId class_id,
std::optional<SemIR::ClassType> base_class_type,
llvm::ArrayRef<SemIR::InstId> vtable_contents)
-> SemIR::VtableId {
-> std::pair<SemIR::VtableId, bool> {
auto base_vtable_id = SemIR::VtableId::None;
auto base_class_specific_id = SemIR::SpecificId::None;
@@ -222,7 +224,7 @@ static auto BuildVtable(Context& context, Parse::ClassDefinitionId node_id,
};
llvm::SmallVector<SemIR::InstId> vtable;
Set<SemIR::FunctionId> implemented_impls;
Set<SemIR::FunctionId, 16> implemented_impls;
bool carbon_native_vtable = true;
// Add vtable entries from the base class, updating them to point to a derived
@@ -358,10 +360,11 @@ static auto BuildVtable(Context& context, Parse::ClassDefinitionId node_id,
}
}
return context.vtables().Add(
auto vtable_id = context.vtables().Add(
{{.class_id = class_id,
.virtual_functions_id = context.inst_blocks().Add(vtable),
.carbon_native_vtable = carbon_native_vtable}});
return {vtable_id, carbon_native_vtable};
}
// Checks that the specified finished class definition is valid and builds and
@@ -411,9 +414,11 @@ static auto CheckCompleteClassType(
{.name_id = SemIR::NameId::Base, .type_inst_id = base_type_inst_id});
}
bool foreign_vtable = false;
if (class_info.is_dynamic) {
auto vtable_id = BuildVtable(context, node_id, class_id, base_class_type,
vtable_contents);
auto [vtable_id, carbon_native_vtable] = BuildVtable(
context, node_id, class_id, base_class_type, vtable_contents);
foreign_vtable = !carbon_native_vtable;
auto vptr_type_id = GetPointerType(context, SemIR::VtableType::TypeInstId);
class_info.vtable_decl_id = AddInst<SemIR::VtableDecl>(
context, node_id, {.type_id = vptr_type_id, .vtable_id = vtable_id});
@@ -422,11 +427,25 @@ static auto CheckCompleteClassType(
auto struct_type_id = GetStructType(
context, AddStructTypeFields(context, struct_type_fields, field_decls));
return AddInst<SemIR::CompleteTypeWitness>(
auto complete_type_witness_id = AddInst<SemIR::CompleteTypeWitness>(
context, node_id,
{.type_id = GetSingletonType(context, SemIR::WitnessType::TypeInstId),
.object_repr_type_inst_id =
context.types().GetTypeInstId(struct_type_id)});
class_info.complete_type_witness_id = complete_type_witness_id;
if (foreign_vtable) {
if (class_info.generic_id.has_value()) {
context.TODO(class_info.first_decl_id(),
"generic class deriving from C++ virtual class");
} else {
ExportAndCompleteClassToCpp(
context,
context.types().GetAs<SemIR::ClassType>(class_info.self_type_id));
}
}
return complete_type_witness_id;
}
auto ComputeClassObjectRepr(Context& context, Parse::ClassDefinitionId node_id,
+12
View File
@@ -352,6 +352,8 @@ class Context {
auto core_identifiers() -> CoreIdentifierCache& { return core_identifiers_; }
auto access_context() -> SemIR::NameScopeId& { return access_context_; }
// --------------------------------------------------------------------------
// Directly expose SemIR::File data accessors for brevity in calls.
// --------------------------------------------------------------------------
@@ -399,6 +401,9 @@ class Context {
auto declared_facet_types() -> SemIR::DeclaredFacetTypeStore& {
return sem_ir().declared_facet_types();
}
auto default_values() -> SemIR::DefaultValueStore& {
return sem_ir().default_values();
}
auto identified_facet_types() -> SemIR::IdentifiedFacetTypeStore& {
return sem_ir().identified_facet_types();
}
@@ -626,6 +631,13 @@ class Context {
CoreIdentifierCache core_identifiers_;
bool mangle_string_fingerprint_;
// Scope for querying member access. For example, when checking a class
// method, this would be set to the scope of that method's class.
//
// This is updated by `DeclNameStack`. During monomorphization, it is updated
// by `TryEvalBlockForSpecific`.
SemIR::NameScopeId access_context_ = SemIR::NameScopeId::None;
};
inline constexpr Context::FormExpr Context::FormExpr::Error = {
+3 -1
View File
@@ -157,7 +157,9 @@ static auto AddCleanups(Context& context, ScopeStack::CleanupScopeDepth depth)
// cleanup blocks, so we'll want to avoid this in the future.
BuildUnaryOperator(context,
context.insts().GetLocIdForDesugaring(destroy_id),
{.interface_name = CoreIdentifier::Destroy}, destroy_id);
{.interface_name = CoreIdentifier::Destroy,
.op_name = CoreIdentifier::SelfDestruct},
destroy_id);
}
}
+26 -31
View File
@@ -649,15 +649,15 @@ static auto ConvertStructToStructOrClass(
value_id = MaterializeIfInitializer(context, value_id);
}
Set<SemIR::NameId> dest_field_names;
for (auto field : dest_elem_fields) {
dest_field_names.Insert(field.name_id);
}
// Prepare to look up fields in the source by index. Also check for
// source fields that don't match any field in the destination.
Map<SemIR::NameId, int32_t> src_field_indexes;
if (src_type.fields_id != dest_type.fields_id) {
Set<SemIR::NameId, 16> dest_field_names;
for (auto field : dest_elem_fields) {
dest_field_names.Insert(field.name_id);
}
for (auto [i, field] : llvm::enumerate(src_elem_fields)) {
if (!dest_field_names.Lookup(field.name_id)) {
if (target.diagnose) {
@@ -1195,15 +1195,14 @@ static auto CanRemoveQualifiers(SemIR::TypeQualifiers quals,
static auto DiagnoseConversionFailureToConstraintValue(
Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
SemIR::TypeId target_type_id) -> void {
CARBON_CHECK(context.types().IsFacetType(target_type_id));
CARBON_CHECK(context.types().Is<SemIR::FacetType>(target_type_id));
// If the source type is/has a facet value (converted with `as type` or
// otherwise), then we can include its `FacetType` in the diagnostic to help
// explain what interfaces the source type implements.
auto const_expr_id = GetCanonicalFacetOrTypeValue(context, expr_id);
// If the source is a facet with constraints (possibly converted to `type`),
// then we can include those constraints in the diagnostic.
auto const_expr_id = GetCanonicalFacet(context, expr_id);
auto const_expr_type_id = context.insts().Get(const_expr_id).type_id();
if (context.types().Is<SemIR::FacetType>(const_expr_type_id)) {
if (context.types().IsConstrainedFacetType(const_expr_type_id)) {
CARBON_DIAGNOSTIC(ConversionFailureFacetToFacet, Error,
"cannot convert type {0} that implements {1} into type "
"implementing {2}",
@@ -1556,7 +1555,7 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
}
}
if (sem_ir.types().IsFacetType(target.type_id)) {
if (sem_ir.types().Is<SemIR::FacetType>(target.type_id)) {
auto type_value_id = SemIR::TypeInstId::None;
// A tuple of types converts to type `type`.
@@ -1574,7 +1573,7 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
}
if (type_value_id != SemIR::InstId::None) {
if (sem_ir.types().Is<SemIR::FacetType>(target.type_id)) {
if (target.type_id != SemIR::TypeType::TypeId) {
// Use the converted `TypeType` value for converting to a facet.
value_id = type_value_id;
value_type_id = SemIR::TypeType::TypeId;
@@ -1585,21 +1584,18 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
}
}
// FacetType converts to Type by wrapping the facet value in
// FacetAccessType.
// All facets convert to `type` by wrapping the facet in FacetAccessType.
if (target.type_id == SemIR::TypeType::TypeId &&
sem_ir.types().Is<SemIR::FacetType>(value_type_id)) {
sem_ir.types().IsConstrainedFacetType(value_type_id)) {
return AddInst<SemIR::FacetAccessType>(
context, loc_id,
{.type_id = target.type_id, .facet_value_inst_id = value_id});
}
// Type values can convert to facet values, and facet values can convert to
// other facet values, as long as they satisfy the required interfaces of the
// target `FacetType`.
if (sem_ir.types().Is<SemIR::FacetType>(target.type_id) &&
sem_ir.types().IsOneOf<SemIR::TypeType, SemIR::FacetType>(
value_type_id)) {
// All facets (including types) can convert into other facets, as long as they
// satisfy the constraints of the target `FacetType`.
if (sem_ir.types().IsConstrainedFacetType(target.type_id) &&
sem_ir.types().Is<SemIR::FacetType>(value_type_id)) {
// TODO: Runtime facet values should be allowed to convert based on their
// FacetTypes, but we assume constant values for impl lookup at the moment.
if (!context.constant_values().Get(value_id).is_constant()) {
@@ -1608,9 +1604,9 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
}
// Get the canonical type for which we want to attach a new set of witnesses
// to match the requirements of the target FacetType.
// to match the requirements of the target `FacetType`.
auto type_inst_id = SemIR::TypeInstId::None;
if (sem_ir.types().Is<SemIR::FacetType>(value_type_id)) {
if (value_type_id != SemIR::TypeType::TypeId) {
type_inst_id = AddTypeInst<SemIR::FacetAccessType>(
context, loc_id,
{.type_id = SemIR::TypeType::TypeId,
@@ -1627,8 +1623,7 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
// would evaluate back to the original SymbolicBinding as its canonical
// form. We can skip past the whole impl lookup step then and do that
// here.
auto facet_value_inst_id =
GetCanonicalFacetOrTypeValue(context, type_inst_id);
auto facet_value_inst_id = GetCanonicalFacet(context, type_inst_id);
if (sem_ir.insts().Get(facet_value_inst_id).type_id() == target.type_id) {
return facet_value_inst_id;
}
@@ -1742,8 +1737,7 @@ static auto PerformUserDefinedConversion(Context& context, SemIR::LocId loc_id,
target.kind == ConversionTarget::ExplicitAs ? 1
: target.kind == ConversionTarget::ExplicitUnsafeAs ? 2
: 0;
if (target.type_id == SemIR::TypeType::TypeId ||
context.types().Is<SemIR::FacetType>(target.type_id)) {
if (context.types().Is<SemIR::FacetType>(target.type_id)) {
CARBON_DIAGNOSTIC(
ConversionFailureNonTypeToFacet, Context,
"cannot{0:=0: implicitly|:} convert non-type value of type {1} "
@@ -2113,7 +2107,7 @@ static auto ConversionNeedsCompleteTarget(Context& context,
// We allow conversion to incomplete facet types, since their representation
// is fixed. This allows us to support using the `Self` of an interface inside
// its definition.
if (context.types().IsFacetType(target.type_id)) {
if (context.types().Is<SemIR::FacetType>(target.type_id)) {
return false;
}
@@ -2388,9 +2382,10 @@ auto ConvertCallArgs(Context& context, SemIR::InstId self_id,
SemIR::InstId return_arg_id, const SemIR::Function& callee,
SemIR::SpecificId callee_specific_id, bool is_desugared)
-> SemIR::InstBlockId {
// The caller should have ensured this callee has the right arity.
// The caller should have ensured this callee has the right arity, modulo
// default arguments.
CARBON_CHECK(
(self_id.has_value() ? 1 : 0) + arg_refs.size() ==
(self_id.has_value() ? 1 : 0) + arg_refs.size() <=
context.inst_blocks().GetOrEmpty(callee.param_patterns_id).size());
return CallerPatternMatch(context, callee_specific_id, callee.self_param_id,
+2
View File
@@ -76,9 +76,11 @@ CARBON_CORE_IDENTIFIER(Optional)
CARBON_CORE_IDENTIFIER(OrderedWith)
CARBON_CORE_IDENTIFIER(RightShiftAssignWith)
CARBON_CORE_IDENTIFIER(RightShiftWith)
CARBON_CORE_IDENTIFIER(SelfDestruct)
CARBON_CORE_IDENTIFIER(String)
CARBON_CORE_IDENTIFIER(SubAssignWith)
CARBON_CORE_IDENTIFIER(SubWith)
CARBON_CORE_IDENTIFIER(SubobjectDestroy)
CARBON_CORE_IDENTIFIER(UInt)
CARBON_CORE_IDENTIFIER(ULong32)
CARBON_CORE_IDENTIFIER(ULong64)
+3 -4
View File
@@ -30,10 +30,9 @@ namespace Carbon::Check {
static auto IsTemplateArg(Context& context, SemIR::InstId arg_id) -> bool {
auto arg_type_id = context.insts().Get(arg_id).type_id();
auto arg_type = context.types().GetAsInst(arg_type_id);
return arg_type
.IsOneOf<SemIR::TypeType, SemIR::FacetType, SemIR::CppTemplateNameType,
SemIR::GenericClassType, SemIR::GenericInterfaceType,
SemIR::GenericNamedConstraintType>();
return arg_type.IsOneOf<SemIR::FacetType, SemIR::CppTemplateNameType,
SemIR::GenericClassType, SemIR::GenericInterfaceType,
SemIR::GenericNamedConstraintType>();
}
// Splits a call argument list into a list of template arguments followed by a
+11 -2
View File
@@ -293,6 +293,16 @@ auto ExportClassToCpp(Context& context, SemIR::ClassType class_type)
return record_decl;
}
auto ExportAndCompleteClassToCpp(Context& context, SemIR::ClassType class_type)
-> clang::TagDecl* {
auto* tag_decl = ExportClassToCpp(context, class_type);
if (tag_decl && context.cpp_context() &&
context.ast_context().getExternalSource()) {
context.ast_context().getExternalSource()->CompleteType(tag_decl);
}
return tag_decl;
}
// Export the bindings in a generic as a `clang::TemplateParameterList`.
static auto ExportGenericBindings(Context& context, SemIR::LocId loc_id,
SemIR::GenericId generic_id,
@@ -322,8 +332,7 @@ static auto ExportGenericBindings(Context& context, SemIR::LocId loc_id,
CARBON_CHECK(param_ident, "non-identifier param name {0}",
entity_name.name_id);
if (symbolic_binding.type_id != SemIR::TypeType::TypeId &&
!context.types().Is<SemIR::FacetType>(symbolic_binding.type_id)) {
if (!context.types().Is<SemIR::FacetType>(symbolic_binding.type_id)) {
context.TODO(loc_id, "binding maps to a non-type template parameter");
return nullptr;
}
+5
View File
@@ -36,6 +36,11 @@ auto ExportNameScopeToCpp(Context& context, SemIR::LocId loc_id,
auto ExportClassToCpp(Context& context, SemIR::ClassType class_type)
-> clang::TagDecl*;
// Exports a dynamic Carbon class with a foreign (C++) vtable into C++ as a
// class, and completes its definition.
auto ExportAndCompleteClassToCpp(Context& context, SemIR::ClassType class_type)
-> clang::TagDecl*;
// Exports a generic Carbon class into C++ as a templated class.
//
// If the generic class has already been exported, returns the existing
+5 -1
View File
@@ -501,10 +501,14 @@ auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void {
llvm::SmallVector<PendingVirtualFunction> pending_virtual_functions;
if (class_info.vtable_decl_id.has_value()) {
LoadImportRef(*context_, class_info.vtable_decl_id);
auto canonical_vtable_decl_id =
context_->constant_values().GetConstantInstId(
class_info.vtable_decl_id);
auto vtable_inst_block = context_->inst_blocks().Get(
context_->vtables()
.Get(context_->insts()
.GetAs<SemIR::VtableDecl>(class_info.vtable_decl_id)
.GetAs<SemIR::VtableDecl>(canonical_vtable_decl_id)
.vtable_id)
.virtual_functions_id);
for (auto vtable_entry_id : vtable_inst_block) {
+8 -5
View File
@@ -241,7 +241,7 @@ static auto BuildDefaultWitness(
query_specific_interface, {fn_id});
}
static auto BuildDestroyWitness(
static auto BuildCppDestroyWitness(
Context& context, SemIR::LocId loc_id,
SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterface query_specific_interface) -> SemIR::InstId {
@@ -264,8 +264,11 @@ static auto BuildDestroyWitness(
if (fn_id == SemIR::ErrorInst::InstId || fn_id == SemIR::InstId::None) {
return fn_id;
}
return BuildCustomWitness(context, loc_id, query_self_const_id,
query_specific_interface, {fn_id});
return BuildDestroyWitness(
context, loc_id,
GetFacetAccessType(
context, context.constant_values().GetInstId(query_self_const_id)),
query_self_const_id, query_specific_interface, {fn_id});
}
// Attempts to build a witness table entry for a C++ unary operator.
@@ -610,8 +613,8 @@ auto LookupCppImpl(Context& context, SemIR::LocId loc_id,
return BuildDefaultWitness(context, loc_id, query_self_const_id,
query_specific_interface);
case SemIR::CoreInterface::Destroy:
return BuildDestroyWitness(context, loc_id, query_self_const_id,
query_specific_interface);
return BuildCppDestroyWitness(context, loc_id, query_self_const_id,
query_specific_interface);
case SemIR::CoreInterface::CppRangeForIterate:
return BuildCppRangeForIterateWitness(
+6 -22
View File
@@ -362,28 +362,13 @@ auto ImportCppConstantFromFile(Context& context, SemIR::LocId loc_id,
return SemIR::ErrorInst::ConstantId;
}
auto const_inst_id = file.constant_values().GetConstantInstId(inst_id);
CARBON_KIND_SWITCH(file.insts().Get(const_inst_id)) {
case CARBON_KIND(SemIR::ClassType class_type): {
const auto& class_info = file.classes().Get(class_type.class_id);
CARBON_CHECK(class_info.scope_id.has_value());
return ImportCppDeclFromFile(
context, loc_id, file,
file.name_scopes().Get(class_info.scope_id).clang_decl_context_id());
}
case CARBON_KIND(SemIR::Namespace namespace_decl): {
return ImportCppDeclFromFile(context, loc_id, file,
file.name_scopes()
.Get(namespace_decl.name_scope_id)
.clang_decl_context_id());
}
default: {
context.TODO(loc_id, "indirect import of unsupported C++ declaration");
return SemIR::ErrorInst::ConstantId;
}
if (const auto* clang_decl = file.clang_decls().Lookup(inst_id)) {
auto clang_decl_id = file.clang_decls().LookupId(clang_decl->key);
return ImportCppDeclFromFile(context, loc_id, file, clang_decl_id);
}
context.TODO(loc_id, "indirect import of unsupported C++ declaration");
return SemIR::ErrorInst::ConstantId;
}
// Returns the Clang `DeclContext` for the given name scope. Return the
@@ -1982,7 +1967,6 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id,
.call_param_patterns_id =
function_params_insts->call_param_patterns_id,
.call_params_id = function_params_insts->call_params_id,
.call_param_default_values_id = SemIR::InstBlockId::None,
.call_param_ranges = function_params_insts->param_ranges,
.return_type_inst_id = function_params_insts->return_type_inst_id,
.return_form_inst_id = function_params_insts->return_form_inst_id,
+2
View File
@@ -39,6 +39,8 @@ static auto GetClangOperatorKind(Context& context, SemIR::LocId loc_id,
switch (interface_name) {
// Unary operators.
case CoreIdentifier::Destroy:
case CoreIdentifier::SubobjectDestroy:
case CoreIdentifier::SelfDestruct:
case CoreIdentifier::As:
case CoreIdentifier::ImplicitAs:
case CoreIdentifier::Iterate:
+1 -1
View File
@@ -122,7 +122,7 @@ auto CheckCppOverloadAccess(
auto name_scope_const_id = context.constant_values().Get(
context.name_scopes().Get(parent_scope_id).inst_id());
SemIR::AccessKind allowed_access_kind =
GetHighestAllowedAccess(context, loc_id, name_scope_const_id);
GetHighestAllowedAccess(context, name_scope_const_id);
CheckAccess(context, loc_id, SemIR::LocId(overload_inst_id), function.name_id,
member_access_kind,
/*is_parent_access=*/false,
+152 -50
View File
@@ -6,6 +6,7 @@
#include "llvm/ADT/APFloat.h"
#include "toolchain/base/kind_switch.h"
#include "toolchain/check/call.h"
#include "toolchain/check/convert.h"
#include "toolchain/check/eval.h"
#include "toolchain/check/facet_type.h"
@@ -15,37 +16,24 @@
#include "toolchain/check/impl_lookup.h"
#include "toolchain/check/import_ref.h"
#include "toolchain/check/inst.h"
#include "toolchain/check/member_access.h"
#include "toolchain/check/name_lookup.h"
#include "toolchain/check/operator.h"
#include "toolchain/check/return.h"
#include "toolchain/check/type.h"
#include "toolchain/check/type_completion.h"
#include "toolchain/diagnostics/format_providers.h"
#include "toolchain/sem_ir/associated_constant.h"
#include "toolchain/sem_ir/builtin_function_kind.h"
#include "toolchain/sem_ir/constant.h"
#include "toolchain/sem_ir/function.h"
#include "toolchain/sem_ir/generic.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/type_info.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::Check {
// Given a value whose type `IsFacetTypeOrError`, returns the corresponding
// type.
static auto GetFacetAsType(Context& context,
SemIR::ConstantId facet_or_type_const_id)
-> SemIR::TypeId {
auto facet_or_type_id =
context.constant_values().GetInstId(facet_or_type_const_id);
auto type_type_id = context.insts().Get(facet_or_type_id).type_id();
CARBON_CHECK(context.types().IsFacetTypeOrError(type_type_id));
if (context.types().Is<SemIR::FacetType>(type_type_id)) {
// It's a facet; access its type.
facet_or_type_id = context.types().GetTypeInstId(
GetFacetAccessType(context, facet_or_type_id));
}
return context.types().GetTypeIdForTypeInstId(facet_or_type_id);
}
// Make the CanonicalKey for a generated function `op_name_id` in the interface
// `core_specific_interface`.
static auto MakeGeneratedFunctionKey(
@@ -242,13 +230,13 @@ static auto CanDestroyType(Context& context, SemIR::LocId loc_id,
SemIR::SpecificInterface query_specific_interface)
-> DestroyFormat {
auto inst_id = context.constant_values().GetInstId(
GetCanonicalFacetOrTypeValue(context, query_self_const_id));
GetCanonicalFacet(context, query_self_const_id));
auto inst = context.insts().Get(inst_id);
if (context.types().Is<SemIR::FacetType>(inst.type_id())) {
// The value's type is a facet (whose type is a facet type). We don't
// provide a custom witness for symbolic values of type facet. The witness
// will be found from impl lookup.
if (context.types().IsConstrainedFacetType(inst.type_id())) {
// The value's type is a symbolic constrained facet. We don't provide a
// custom witness for constrained facets. The witness must be found in the
// constraints by impl lookup.
CARBON_CHECK(query_self_const_id.is_symbolic());
return DestroyFormat::NoDestroy;
}
@@ -360,7 +348,6 @@ static auto CanDestroyType(Context& context, SemIR::LocId loc_id,
case SemIR::IntLiteralType::Kind:
case SemIR::IntType::Kind:
case SemIR::PointerType::Kind:
case SemIR::TypeType::Kind:
// Trivially destructible.
return DestroyFormat::Trivial;
@@ -369,14 +356,14 @@ static auto CanDestroyType(Context& context, SemIR::LocId loc_id,
}
}
// Returns the body for `Destroy.Op`.
// Returns the body for `SubobjectDestroy.Op`.
//
// TODO: This is a placeholder still not actually destroying things, intended to
// maintain mostly-consistent behavior with current logic while working. That
// also means using `self`.
static auto MakeDestroyOpBody(Context& context, SemIR::LocId loc_id,
SemIR::TypeId self_type_id,
SemIR::InstId self_param_id)
static auto MakeSubobjectDestroyOpBody(Context& context, SemIR::LocId loc_id,
SemIR::TypeId self_type_id,
SemIR::InstId self_param_id)
-> SemIR::InstBlockId {
context.inst_block_stack().Push();
auto inst = context.types().GetAsInst(self_type_id);
@@ -393,7 +380,7 @@ static auto MakeDestroyOpBody(Context& context, SemIR::LocId loc_id,
// TODO: Implement destruction of the type.
break;
default:
CARBON_FATAL("Unexpected type for MakeDestroyOpBody: {0}", inst);
CARBON_FATAL("Unexpected type for MakeSubobjectDestroyOpBody: {0}", inst);
}
AddInst(context, loc_id, SemIR::Return{});
@@ -404,15 +391,48 @@ static auto MakeDestroyOpBody(Context& context, SemIR::LocId loc_id,
// to `self_type_id`.
static auto MakeDestroyOpFunction(Context& context, SemIR::LocId loc_id,
SemIR::TypeId self_type_id,
SemIR::InterfaceId interface_id,
DestroyFormat format) -> SemIR::InstId {
SemIR::InterfaceId interface_id)
-> SemIR::InstId {
auto name_id = context.core_identifiers().AddNameId(CoreIdentifier::Op);
const auto& interface = context.interfaces().Get(interface_id);
auto specific_interface = MakeCoreSpecificInterface(
context, loc_id, interface_id, interface.generic_id, {});
auto canonical_key = MakeGeneratedFunctionKey(context, specific_interface,
self_type_id, name_id);
auto [decl_id, function_id] = TryGetGeneratedFunction(context, canonical_key);
if (!decl_id.has_value()) {
std::tie(decl_id, function_id) = MakeGeneratedFunctionDecl(
context, loc_id,
{.parent_scope_id = interface.scope_with_self_id,
.name_id = name_id,
.self_type_id = self_type_id,
.self_kind = ParamPatternKind::Ref});
auto& function = context.functions().Get(function_id);
function.SetGenerated(context.generated_functions().Add(
{.canonical_key = canonical_key,
.function_id = function_id,
.decl_id = decl_id,
.builtin_function_kind = SemIR::BuiltinFunctionKind::NoOp}));
}
return decl_id;
}
static auto MakeSubobjectDestroyOpFunction(
Context& context, SemIR::LocId loc_id, SemIR::TypeId self_type_id,
SemIR::InterfaceId interface_id, DestroyFormat format) -> SemIR::InstId {
// TODO: replace with `CoreIdentifier::Op` when `require impls` adds a witness
// table entry.
auto name_id =
context.core_identifiers().AddNameId(CoreIdentifier::SubobjectDestroy);
const auto& interface = context.interfaces().Get(interface_id);
auto specific_interface = MakeCoreSpecificInterface(
context, loc_id, interface_id, interface.generic_id, {});
auto canonical_key = MakeGeneratedFunctionKey(context, specific_interface,
self_type_id, name_id);
auto [decl_id, function_id] = TryGetGeneratedFunction(context, canonical_key);
if (!decl_id.has_value()) {
std::tie(decl_id, function_id) = MakeGeneratedFunctionDecl(
context, loc_id,
@@ -429,8 +449,8 @@ static auto MakeDestroyOpFunction(Context& context, SemIR::LocId loc_id,
builtin_kind = SemIR::BuiltinFunctionKind::NoOp;
break;
case DestroyFormat::NonTrivial: {
auto body_id = MakeDestroyOpBody(context, loc_id, self_type_id,
function.self_param_id);
auto body_id = MakeSubobjectDestroyOpBody(context, loc_id, self_type_id,
function.self_param_id);
function.body_block_ids.push_back(body_id);
break;
}
@@ -444,7 +464,54 @@ static auto MakeDestroyOpFunction(Context& context, SemIR::LocId loc_id,
.decl_id = decl_id,
.builtin_function_kind = builtin_kind}));
}
return decl_id;
}
// Returns a manufactured `Destroy.SelfDestruct` function with the `self`
// parameter typed to `self_type_id`.
static auto MakeDestroySelfDestructFunction(
Context& context, SemIR::LocId loc_id, SemIR::TypeId self_type_id,
SemIR::InterfaceId interface_id, SemIR::InstId op_id,
[[maybe_unused]] SemIR::InstId subobject_destroy_id) -> SemIR::InstId {
auto name_id =
context.core_identifiers().AddNameId(CoreIdentifier::SelfDestruct);
const auto& interface = context.interfaces().Get(interface_id);
auto specific_interface = MakeCoreSpecificInterface(
context, loc_id, interface_id, interface.generic_id, {});
auto canonical_key = MakeGeneratedFunctionKey(context, specific_interface,
self_type_id, name_id);
auto [decl_id, function_id] = TryGetGeneratedFunction(context, canonical_key);
if (!decl_id.has_value()) {
std::tie(decl_id, function_id) = MakeGeneratedFunctionDecl(
context, loc_id,
{.parent_scope_id = interface.scope_with_self_id,
.name_id = name_id,
.self_type_id = self_type_id,
.self_kind = ParamPatternKind::Ref});
auto& function = context.functions().Get(function_id);
context.inst_block_stack().Push();
StartFunctionDefinition(context, decl_id, function_id);
auto params = context.inst_blocks().Get(function.call_params_id);
CARBON_CHECK(
params.size() == 1,
"`Core.Destroy.SelfDestruct` should only have `ref self` as its "
"parameter");
op_id = PerformCall(context, loc_id, op_id, {params[0]}, true);
DiscardExpr(context, op_id);
subobject_destroy_id =
PerformCall(context, loc_id, subobject_destroy_id, {params[0]}, true);
DiscardExpr(context, subobject_destroy_id);
BuildReturnWithNoExpr(context, loc_id);
FinishFunctionDefinition(context, function_id);
context.inst_block_stack().Pop();
function.SetGenerated(context.generated_functions().Add(
{.canonical_key = canonical_key,
.function_id = function_id,
.decl_id = decl_id,
.builtin_function_kind = SemIR::BuiltinFunctionKind::None}));
}
return decl_id;
}
@@ -481,7 +548,8 @@ static auto GetTypesForSelfFacet(
query_specific_interface));
// The Self facet needs to point to a type value. If it's not one already,
// convert to type.
auto query_self_as_type_id = GetFacetAsType(context, query_self_const_id);
auto query_self_as_type_id = GetFacetAccessType(
context, context.constant_values().GetInstId(query_self_const_id));
return {facet_type_for_query_specific_interface, query_self_as_type_id};
}
@@ -663,7 +731,8 @@ auto BuildPrimitiveCopyWitness(
Context& context, SemIR::LocId loc_id,
SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterface query_specific_interface) -> SemIR::InstId {
auto self_type_id = GetFacetAsType(context, query_self_const_id);
auto self_type_id = GetFacetAccessType(
context, context.constant_values().GetInstId(query_self_const_id));
auto op_id = MakeBuiltinOperatorFunction(
context, loc_id, {self_type_id}, self_type_id, CoreIdentifier::Op,
@@ -673,21 +742,49 @@ auto BuildPrimitiveCopyWitness(
query_specific_interface, {op_id});
}
auto BuildDestroyWitness(Context& context, SemIR::LocId loc_id,
SemIR::TypeId self_type_id,
SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterface query_specific_interface,
SemIR::InstId subobject_destroy_fn_id)
-> SemIR::InstId {
auto interface =
context.interfaces().Get(query_specific_interface.interface_id);
auto assoc_entities =
context.inst_blocks().Get(interface.associated_entities_id);
CARBON_CHECK(assoc_entities.size() == 3,
"{} only has {} associated functions",
context.names().GetAsStringIfIdentifier(interface.name_id),
assoc_entities.size());
auto op_id = MakeDestroyOpFunction(context, loc_id, self_type_id,
query_specific_interface.interface_id);
auto self_destruct_fn_id = MakeDestroySelfDestructFunction(
context, loc_id, self_type_id, query_specific_interface.interface_id,
op_id, subobject_destroy_fn_id);
return BuildCustomWitness(
context, loc_id, query_self_const_id, query_specific_interface,
{op_id, subobject_destroy_fn_id, self_destruct_fn_id});
}
// Builds and returns a custom witness that performs the specified kind of
// destruction for the given type.
static auto BuildDestroyWitness(
static auto BuildCarbonDestroyWitness(
Context& context, SemIR::LocId loc_id,
SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterface query_specific_interface, DestroyFormat format)
-> SemIR::InstId {
CARBON_CHECK(format != DestroyFormat::NoDestroy);
auto self_type_id = GetFacetAsType(context, query_self_const_id);
auto op_id =
MakeDestroyOpFunction(context, loc_id, self_type_id,
query_specific_interface.interface_id, format);
return BuildCustomWitness(context, loc_id, query_self_const_id,
query_specific_interface, {op_id});
auto self_type_id = GetFacetAccessType(
context, context.constant_values().GetInstId(query_self_const_id));
auto subobject_destroy_op_id = MakeSubobjectDestroyOpFunction(
context, loc_id, self_type_id, query_specific_interface.interface_id,
format);
return BuildDestroyWitness(context, loc_id, self_type_id, query_self_const_id,
query_specific_interface, subobject_destroy_op_id);
}
// Returns the custom witness to use for destruction of the given type. See
@@ -708,16 +805,17 @@ static auto LookupDestroyWitness(
return SemIR::InstId::None;
}
return BuildDestroyWitness(context, loc_id, query_self_const_id,
query_specific_interface, format);
return BuildCarbonDestroyWitness(context, loc_id, query_self_const_id,
query_specific_interface, format);
}
auto BuildTrivialDestroyWitness(
Context& context, SemIR::LocId loc_id,
SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterface query_specific_interface) -> SemIR::InstId {
return BuildDestroyWitness(context, loc_id, query_self_const_id,
query_specific_interface, DestroyFormat::Trivial);
return BuildCarbonDestroyWitness(context, loc_id, query_self_const_id,
query_specific_interface,
DestroyFormat::Trivial);
}
static auto MakeIntFitsInWitness(
@@ -740,8 +838,10 @@ static auto MakeIntFitsInWitness(
return std::nullopt;
}
auto src_type_id = GetFacetAsType(context, query_self_const_id);
auto dest_type_id = GetFacetAsType(context, dest_const_id);
auto src_type_id = GetFacetAccessType(
context, context.constant_values().GetInstId(query_self_const_id));
auto dest_type_id = GetFacetAccessType(
context, context.constant_values().GetInstId(dest_const_id));
auto context_fn = [](DiagnosticContextBuilder& /*builder*/) -> void {};
if (!RequireCompleteType(context, src_type_id, loc_id, context_fn) ||
@@ -836,8 +936,10 @@ static auto MakeFloatFitsInWitness(
return std::nullopt;
}
auto src_type_id = GetFacetAsType(context, query_self_const_id);
auto dest_type_id = GetFacetAsType(context, dest_const_id);
auto src_type_id = GetFacetAccessType(
context, context.constant_values().GetInstId(query_self_const_id));
auto dest_type_id = GetFacetAccessType(
context, context.constant_values().GetInstId(dest_const_id));
auto context_fn = [](DiagnosticContextBuilder& /*builder*/) -> void {};
if (!RequireCompleteType(context, src_type_id, loc_id, context_fn) ||
+11
View File
@@ -67,6 +67,17 @@ auto LookupCustomWitness(Context& context, SemIR::LocId loc_id,
SemIR::SpecificInterface query_specific_interface,
bool build_witness) -> std::optional<SemIR::InstId>;
// Builds a witness for the `Destroy` interface.
//
// `op_id` refers to the synthesised `Destroy.Op` and is generated differently
// based on whether the specific is a Carbon type or a C++ type.
auto BuildDestroyWitness(Context& context, SemIR::LocId loc_id,
SemIR::TypeId self_type_id,
SemIR::ConstantId query_self_const_id,
SemIR::SpecificInterface query_specific_interface,
SemIR::InstId subobject_destroy_fn_id)
-> SemIR::InstId;
} // namespace Carbon::Check
#endif // CARBON_TOOLCHAIN_CHECK_CUSTOM_WITNESS_H_
+18
View File
@@ -13,6 +13,7 @@
#include "toolchain/check/merge.h"
#include "toolchain/check/name_component.h"
#include "toolchain/check/name_lookup.h"
#include "toolchain/check/type.h"
#include "toolchain/check/type_completion.h"
#include "toolchain/check/unused.h"
#include "toolchain/diagnostics/diagnostic.h"
@@ -65,6 +66,8 @@ auto DeclNameStack::PushScopeAndStartName() -> void {
// Create a scope for any parameters introduced in this name.
context_->scope_stack().PushForDeclName();
UpdateAccessContext();
}
auto DeclNameStack::FinishName(const NameComponent& name) -> NameContext {
@@ -93,6 +96,8 @@ auto DeclNameStack::PopScope(bool check_unused) -> void {
context_->scope_stack().PopTo(decl_name_stack_.back().initial_scope_index,
check_unused);
decl_name_stack_.pop_back();
UpdateAccessContext();
}
auto DeclNameStack::Suspend() -> SuspendedName {
@@ -108,6 +113,9 @@ auto DeclNameStack::Suspend() -> SuspendedName {
CARBON_CHECK(scope_stack.PeekIndex() == scope_index,
"Scope index {0} does not enclose the current scope {1}",
scope_index, scope_stack.PeekIndex());
UpdateAccessContext();
return result;
}
@@ -131,6 +139,8 @@ auto DeclNameStack::Restore(SuspendedName&& sus) -> void {
context_->scope_stack().Restore(std::move(suspended_scope));
}
UpdateAccessContext();
}
auto DeclNameStack::AddName(NameContext name_context, SemIR::InstId target_id,
@@ -523,4 +533,12 @@ auto DeclNameStack::ResolveAsScope(const NameContext& name_context,
}
}
auto DeclNameStack::UpdateAccessContext() const -> void {
if (decl_name_stack_.empty()) {
context_->access_context() = SemIR::NameScopeId::None;
} else {
context_->access_context() = decl_name_stack_.back().parent_scope_id;
}
}
} // namespace Carbon::Check
+3
View File
@@ -279,6 +279,9 @@ class DeclNameStack {
const NameComponent& name) const
-> std::pair<SemIR::NameScopeId, SemIR::GenericId>;
// Update `Context.access_context` to the current NameContext's parent scope.
auto UpdateAccessContext() const -> void;
// The linked context.
Context* context_;
+3 -3
View File
@@ -306,11 +306,11 @@ auto DeductionContext::Deduce() -> bool {
if (context().types().Is<SemIR::PatternType>(param_type_id)) {
param_type_id =
SemIR::ExtractScrutineeType(context().sem_ir(), param_type_id);
} else if (context().types().IsFacetType(param_type_id)) {
} else if (context().types().Is<SemIR::FacetType>(param_type_id)) {
// Given `fn F[G: Interface](g: G)`, the type of `g` is `G as type`. For
// deduction, we want to ignore the `as type`, and check that the argument
// can convert to the FacetType of the canonical facet value.
param_id = GetCanonicalFacetOrTypeValue(context(), param_id);
// can convert to the FacetType of the canonical facet.
param_id = GetCanonicalFacet(context(), param_id);
param = context().insts().Get(param_id);
param_type_id = param.type_id();
}
+12 -6
View File
@@ -64,18 +64,24 @@ LLVM_DUMP_METHOD static auto Dump(const Context& context,
return SemIR::Dump(context.sem_ir(), const_id);
}
LLVM_DUMP_METHOD static auto Dump(const Context& context,
SemIR::EntityNameId entity_name_id)
-> std::string {
return SemIR::Dump(context.sem_ir(), entity_name_id);
}
LLVM_DUMP_METHOD static auto Dump(
const Context& context, SemIR::DeclaredFacetTypeId declared_facet_type_id)
-> std::string {
return SemIR::Dump(context.sem_ir(), declared_facet_type_id);
}
LLVM_DUMP_METHOD static auto Dump(const Context& context,
SemIR::DefaultValueId value_id)
-> std::string {
return SemIR::Dump(context.sem_ir(), value_id);
}
LLVM_DUMP_METHOD static auto Dump(const Context& context,
SemIR::EntityNameId entity_name_id)
-> std::string {
return SemIR::Dump(context.sem_ir(), entity_name_id);
}
LLVM_DUMP_METHOD static auto Dump(const Context& context,
SemIR::FunctionId function_id)
-> std::string {
+25 -4
View File
@@ -12,6 +12,7 @@
#include "common/raw_string_ostream.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/SaveAndRestore.h"
#include "toolchain/base/canonical_value_store.h"
#include "toolchain/base/int.h"
#include "toolchain/base/kind_switch.h"
@@ -3339,15 +3340,15 @@ static auto AddRequirementImpls(Context& context, SemIR::RequirementImpls impls,
llvm::append_range(declared_facet_type->self_impls_named_constraints,
rhs.extend_named_constraints);
} else {
auto lhs_facet_or_type = GetCanonicalFacetOrTypeValue(context, lhs_id);
auto lhs_facet = GetCanonicalFacet(context, lhs_id);
auto extends_interface = [=](SemIR::SpecificInterface si)
-> SemIR::DeclaredFacetType::TypeImplsInterface {
return {lhs_facet_or_type, si};
return {lhs_facet, si};
};
auto extends_constraint = [=](SemIR::SpecificNamedConstraint sc)
-> SemIR::DeclaredFacetType::TypeImplsNamedConstraint {
return {lhs_facet_or_type, sc};
return {lhs_facet, sc};
};
// Extend constraints are copied over without replacing anything, but are
@@ -3458,12 +3459,30 @@ auto TryEvalInstUnsafe(Context& context, SemIR::InstId inst_id,
return TryEvalInstInContext(eval_context, inst_id, inst);
}
// Update `context.access_context` to the type of the innermost enclosing type
// scope of the generic.
static auto SetAccessContext(Context& context, const SemIR::Generic& generic) {
auto function_decl =
context.insts().TryGetAs<SemIR::FunctionDecl>(generic.decl_id);
if (!function_decl || !function_decl->function_id.has_value()) {
return;
}
const auto& function = context.functions().Get(function_decl->function_id);
if (!function.parent_scope_id.has_value()) {
return;
}
context.access_context() = function.parent_scope_id;
}
auto TryEvalBlockForSpecific(Context& context, SemIR::LocId loc_id,
SemIR::SpecificId specific_id,
SemIR::GenericInstIndex::Region region) -> void {
auto generic_id = context.specifics().Get(specific_id).generic_id;
auto eval_block_id = context.generics().Get(generic_id).GetEvalBlock(region);
const auto& generic = context.generics().Get(generic_id);
auto eval_block_id = generic.GetEvalBlock(region);
auto eval_block = context.inst_blocks().Get(eval_block_id);
llvm::SaveAndRestore access_context(context.access_context());
// Allocate the value block and store it back onto the specific, so that our
// in-progress results are visible.
@@ -3476,6 +3495,8 @@ auto TryEvalBlockForSpecific(Context& context, SemIR::LocId loc_id,
}
specific.SetValueBlock(region, value_block_id);
SetAccessContext(context, generic);
EvalContext eval_context(&context, loc_id, specific_id);
Diagnostics::ContextScope diagnostic_context(
+22 -9
View File
@@ -214,24 +214,37 @@ auto EvalConstantInst(Context& context, SemIR::ExportDecl inst)
auto EvalConstantInst(Context& context, SemIR::FacetAccessType inst)
-> ConstantEvalResult {
if (auto facet_value = context.insts().TryGetAs<SemIR::FacetValue>(
inst.facet_value_inst_id)) {
auto facet = context.insts().Get(inst.facet_value_inst_id);
CARBON_CHECK(context.types().Is<SemIR::FacetType>(facet.type_id()));
// If the facet is a `type`, we can evaluate to the facet.
if (facet.type_id() == SemIR::TypeType::TypeId) {
return ConstantEvalResult::Existing(
context.constant_values().Get(inst.facet_value_inst_id));
}
// If the facet is a FacetValue, it wraps a `type`, and we can evaluate to
// that `type`.
if (auto facet_value = facet.TryAs<SemIR::FacetValue>()) {
return ConstantEvalResult::Existing(
context.constant_values().Get(facet_value->type_inst_id));
}
// The `facet_value_inst_id` is always a facet value (has type facet type).
CARBON_CHECK(context.types().Is<SemIR::FacetType>(
context.insts().Get(inst.facet_value_inst_id).type_id()));
// Other instructions (e.g. ImplWitnessAccess) of type FacetType can appear
// here, in which case the constant inst is a FacetAccessType until those
// Other instructions (e.g. ImplWitnessAccess) of type `FacetType` can appear
// here, in which case the constant inst is a `FacetAccessType` until those
// instructions resolve to one of the above.
return ConstantEvalResult::NewSamePhase(inst);
}
auto EvalConstantInst(Context& context, SemIR::FacetValue inst)
-> ConstantEvalResult {
// If the FacetValue is of type `type`, then it evaluates to the type inside
// it.
if (inst.type_id == SemIR::TypeType::TypeId) {
return ConstantEvalResult::Existing(
context.constant_values().Get(inst.type_inst_id));
}
// A FacetValue that just wraps a facet without adding/removing any witnesses
// (which means they have the same type) is evaluated to the facet itself.
if (auto access =
@@ -296,7 +309,7 @@ static auto TryFindValueInRewriteConstraints(
SemIR::ElementIndex interface_index, SemIR::InstId search_facet)
-> SemIR::ConstantId {
auto access_self_type_id = context.insts().Get(search_facet).type_id();
if (context.types().Is<SemIR::TypeType>(access_self_type_id)) {
if (access_self_type_id == SemIR::TypeType::TypeId) {
// A self facet of type `type` has no rewrite constraints to look in.
return SemIR::ConstantId::None;
}
+1 -24
View File
@@ -467,29 +467,6 @@ auto ResolveFacetTypeRewriteConstraints(
return true;
}
auto GetEmptyFacetType(Context& context) -> SemIR::TypeId {
SemIR::DeclaredFacetTypeId declared_facet_type_id =
context.declared_facet_types().Add(SemIR::DeclaredFacetType{});
auto const_id = EvalOrAddInst<SemIR::FacetType>(
context, SemIR::LocId::None,
{.type_id = SemIR::TypeType::TypeId,
.declared_facet_type_id = declared_facet_type_id});
return context.types().GetTypeIdForTypeConstantId(const_id);
}
auto GetConstantFacetValueForType(Context& context,
SemIR::TypeInstId type_inst_id)
-> SemIR::ConstantId {
// We use an empty facet type because values of type `type` do not provide any
// witnesses of their own.
auto type_facet_type = GetEmptyFacetType(context);
return EvalOrAddInst<SemIR::FacetValue>(
context, SemIR::LocId::None,
{.type_id = type_facet_type,
.type_inst_id = type_inst_id,
.witnesses_block_id = SemIR::InstBlockId::Empty});
}
auto GetConstantFacetValueForTypeAndInterface(
Context& context, SemIR::TypeInstId type_inst_id,
SemIR::SpecificInterface specific_interface, SemIR::InstId witness_id)
@@ -550,7 +527,7 @@ auto FindWhere(Context& context, SemIR::ConstantId const_id) -> bool {
private:
bool* found_;
Set<SemIR::InstId> searched_;
Set<SemIR::InstId, 16> searched_;
};
if (!const_id.is_constant()) {
-13
View File
@@ -58,19 +58,6 @@ auto ResolveFacetTypeRewriteConstraints(
llvm::SmallVector<SemIR::DeclaredFacetType::RewriteConstraint>& rewrites)
-> bool;
// Get a FacetType instruction for an empty FacetType. This is the facet
// equivalent to TypeType.
//
// TODO: We vaguely plan to replace TypeType with this FacetType in the future,
// though that's a big change.
auto GetEmptyFacetType(Context& context) -> SemIR::TypeId;
// Make a facet value for a type value, which has an empty FacetType as its
// type. Returns a constant value, whose instruction payload is a FacetValue.
auto GetConstantFacetValueForType(Context& context,
SemIR::TypeInstId type_inst_id)
-> SemIR::ConstantId;
// Make a facet value for a type value, which has a FacetType containing the
// `specific_interface` as its type. Returns a constant value, whose instruction
// payload is a FacetValue.
+15 -23
View File
@@ -81,6 +81,7 @@ class FullPatternStack {
bind_name_stack_.PushArray();
var_pattern_stack_.PushArray();
next_var_index_stack_.push_back(-1);
unspecified_default_values_stack_.PushArray();
}
// Marks the start of a new full-pattern for a name binding declaration.
@@ -89,6 +90,7 @@ class FullPatternStack {
bind_name_stack_.PushArray();
var_pattern_stack_.PushArray();
next_var_index_stack_.push_back(-1);
unspecified_default_values_stack_.PushArray();
}
// Marks the start of a new full-pattern for a class `var` declaration.
@@ -97,6 +99,7 @@ class FullPatternStack {
bind_name_stack_.PushArray();
var_pattern_stack_.PushArray();
next_var_index_stack_.push_back(-1);
unspecified_default_values_stack_.PushArray();
}
// Marks the start of the current parameterized entity's implicit parameter
@@ -121,7 +124,6 @@ class FullPatternStack {
CARBON_CHECK(kind_stack_.back() == Kind::NotInEitherParamList, "{0}",
kind_stack_.back());
kind_stack_.back() = Kind::ExplicitParamList;
default_values_stack_.PushArray();
}
// Marks the end of the current parameterized entity's explicit parameter
@@ -141,16 +143,14 @@ class FullPatternStack {
// Marks the end of checking and pattern matching for the current
// full-pattern.
auto PopFullPattern() -> void {
auto kind = kind_stack_.pop_back_val();
kind_stack_.pop_back();
bind_name_stack_.PopArray();
int index = next_var_index_stack_.pop_back_val();
CARBON_CHECK(index < 0 || static_cast<size_t>(index) ==
var_pattern_stack_.PeekArray().size(),
"`GetLocalVarStorage` not called for all var patterns");
var_pattern_stack_.PopArray();
if (kind == Kind::ExplicitParamList) {
default_values_stack_.PopArray();
}
unspecified_default_values_stack_.PopArray();
}
// Records that `name_id` was introduced by the current full-pattern.
@@ -205,23 +205,15 @@ class FullPatternStack {
kind_stack_.size());
}
// Adds the inst id for a constant value provided as a default value for
// any subpattern in the full-pattern. Returns the index of that element
// as a `DefaultValueId`. Note default values are only supported for
// explicit parameter lists.
auto AddDefaultValue(SemIR::InstId inst_id) -> SemIR::DefaultValueId {
auto index = SemIR::FromRaw<SemIR::DefaultValueId>(
static_cast<int32_t>(default_values_stack_.PeekArray().size()));
CARBON_CHECK(kind_stack_.back() == Kind::ExplicitParamList);
default_values_stack_.AppendToTop(inst_id);
return index;
// Adds an unspecified pattern default value to the array at the top of the
// full pattern stack. We track these for possible later use in diagnostics.
auto AddUnspecifiedDefaultValue(SemIR::InstId inst_id) -> void {
unspecified_default_values_stack_.AppendToTop(inst_id);
}
// Returns a reference to the array of default value inst ids at the top of
// the stack. Note default values are only supported for explicit parameter
// lists.
auto GetDefaultValues() -> llvm::ArrayRef<SemIR::InstId> {
return default_values_stack_.PeekArray();
// Returns the unspecified default values array at the top of the stack.
auto GetUnspecifiedDefaultValues() -> llvm::ArrayRef<SemIR::InstId> {
return unspecified_default_values_stack_.PeekArray();
}
private:
@@ -256,9 +248,9 @@ class FullPatternStack {
// of that frame are not ready for consumption.
llvm::SmallVector<int> next_var_index_stack_;
// The stack of instructions specifying default values for subpatterns
// within this full-pattern.
ArrayStack<SemIR::InstId> default_values_stack_;
// For each full pattern we maintain a list of the InstIds of any
// unspecified default values, for use in diagnostics.
ArrayStack<SemIR::InstId> unspecified_default_values_stack_;
};
} // namespace Carbon::Check
-103
View File
@@ -91,7 +91,6 @@ struct FunctionSignatureInsts {
SemIR::InstBlockId param_patterns_id = SemIR::InstBlockId::None;
SemIR::InstBlockId call_param_patterns_id = SemIR::InstBlockId::None;
SemIR::InstBlockId call_params_id = SemIR::InstBlockId::None;
SemIR::InstBlockId call_param_default_values_id = SemIR::InstBlockId::None;
SemIR::Function::CallParamIndexRanges call_param_ranges =
SemIR::Function::CallParamIndexRanges::Empty;
SemIR::TypeInstId return_type_inst_id = SemIR::TypeInstId::None;
@@ -191,8 +190,6 @@ auto MakeGeneratedFunctionDecl(Context& context, SemIR::LocId loc_id,
{
.call_param_patterns_id = insts.call_param_patterns_id,
.call_params_id = insts.call_params_id,
.call_param_default_values_id =
insts.call_param_default_values_id,
.call_param_ranges = insts.call_param_ranges,
.return_type_inst_id = insts.return_type_inst_id,
.return_form_inst_id = insts.return_form_inst_id,
@@ -305,102 +302,6 @@ static auto CheckFunctionEvaluationModeMatches(
return false;
}
// Given a parameter patterns block, extracts the locations of all
// `SemIR::DefaultValuePattern` instructions and returns them in an array.
static auto ExtractDefaultValueLocations(Context& context,
SemIR::InstBlockId param_patterns_id)
-> llvm::SmallVector<SemIR::LocId> {
llvm::SmallVector<SemIR::LocId> locations;
for (auto inst_id : context.inst_blocks().GetOrEmpty(param_patterns_id)) {
if (context.insts().Is<SemIR::DefaultValuePattern>(inst_id)) {
locations.push_back(SemIR::LocId(inst_id));
}
}
return locations;
}
// Checks every parameter in `prev_function` and `new_function`, that if they
// both specify a default value those values are identical, or that at most
// one has an unspecified default value. If `diagnose` is true, issues
// diagnostics where either condition is violated. Returns true if every
// parameter met both criteria.
static auto CheckDefaultValueConsistency(Context& context,
const SemIR::Function& new_function,
const SemIR::Function& prev_function,
bool diagnose) -> bool {
// Both functions must either have defaults or not.
CARBON_CHECK(prev_function.call_param_default_values_id.has_value() ==
new_function.call_param_default_values_id.has_value());
if (!prev_function.call_param_default_values_id.has_value()) {
return true;
}
auto prev_value_inst_ids =
context.inst_blocks().Get(prev_function.call_param_default_values_id);
auto new_value_inst_ids =
context.inst_blocks().Get(new_function.call_param_default_values_id);
CARBON_CHECK(prev_value_inst_ids.size() == new_value_inst_ids.size());
llvm::SmallVector<size_t> indices_without_values;
llvm::SmallVector<size_t> indices_with_different_values;
for (size_t i = 0; i < prev_value_inst_ids.size(); ++i) {
bool prev_value_specified =
!context.insts().Is<SemIR::UnspecifiedValue>(prev_value_inst_ids[i]);
bool new_value_specified =
!context.insts().Is<SemIR::UnspecifiedValue>(new_value_inst_ids[i]);
if (!prev_value_specified && !new_value_specified) {
indices_without_values.push_back(i);
} else if (prev_value_specified && new_value_specified) {
auto prev_constant_id = TryEvalInst(context, prev_value_inst_ids[i]);
CARBON_CHECK(prev_constant_id != SemIR::ConstantId::NotConstant);
auto new_constant_id = TryEvalInst(context, new_value_inst_ids[i]);
CARBON_CHECK(new_constant_id != SemIR::ConstantId::NotConstant);
if (prev_constant_id != new_constant_id) {
indices_with_different_values.push_back(i);
}
}
}
bool check_ok =
indices_without_values.empty() && indices_with_different_values.empty();
if (check_ok || !diagnose) {
return check_ok;
}
// TODO: for imported functions we don't seem to have the previous parameter
// pattern block, so we can't add their locations to the diagnostic.
auto prev_param_locations =
ExtractDefaultValueLocations(context, prev_function.param_patterns_id);
auto new_param_locations =
ExtractDefaultValueLocations(context, new_function.param_patterns_id);
for (auto index : indices_without_values) {
CARBON_DIAGNOSTIC(PatternDefaultValueNeverSpecified, Error,
"no value for default number {0} is ever specified.",
size_t);
CARBON_DIAGNOSTIC(PatternDefaultValueNeverSpecifiedNote, Note,
"previous declaration here.");
auto builder = context.emitter().Build(
new_param_locations[index], PatternDefaultValueNeverSpecified, index);
if (index < prev_param_locations.size()) {
builder.Note(prev_param_locations[index],
PatternDefaultValueNeverSpecifiedNote);
}
builder.Emit();
}
for (auto index : indices_with_different_values) {
CARBON_DIAGNOSTIC(PatternDefaultValueDiffers, Error,
"default value differs from the previous declaration.");
context.emitter().Emit(new_param_locations[index],
PatternDefaultValueDiffers);
}
return false;
}
auto CheckFunctionTypeMatches(Context& context,
const SemIR::Function& new_function,
const SemIR::Function& prev_function,
@@ -419,10 +320,6 @@ auto CheckFunctionTypeMatches(Context& context,
diagnose)) {
return false;
}
if (!CheckDefaultValueConsistency(context, new_function, prev_function,
diagnose)) {
return false;
}
return true;
}
+3 -1
View File
@@ -858,7 +858,9 @@ auto MakeSpecificWithInnerSelf(Context& context, SemIR::LocId loc_id,
if (self_facet == SemIR::ErrorInst::ConstantId) {
args.push_back(SemIR::ErrorInst::InstId);
} else {
auto self_facet_inst_id = context.constant_values().GetInstId(self_facet);
// Use the canonical facet for self in order to produce fewer specifics.
auto self_facet_inst_id = context.constant_values().GetInstId(
GetCanonicalFacet(context, self_facet));
CARBON_CHECK(context.types().Is<SemIR::FacetType>(
context.insts().Get(self_facet_inst_id).type_id()));
args.push_back(self_facet_inst_id);
-1
View File
@@ -51,7 +51,6 @@ auto GlobalInit::Finalize() -> void {
.first_owning_decl_id = SemIR::InstId::None},
{.call_param_patterns_id = SemIR::InstBlockId::Empty,
.call_params_id = SemIR::InstBlockId::Empty,
.call_param_default_values_id = SemIR::InstBlockId::None,
.call_param_ranges = SemIR::Function::CallParamIndexRanges::Empty,
.return_type_inst_id = SemIR::TypeInstId::None,
.return_form_inst_id = SemIR::InstId::None,
+14 -6
View File
@@ -132,6 +132,10 @@ struct BindingPatternTypeInfo {
// For a `:?` binding this is the type component of the form denoted by
// `inst_id`. Otherwise this is the type denoted by `inst_id`.
SemIR::TypeId type_component_id;
// The instruction describing `type_component_id` as it was written. For a
// `:?` binding this is a `TypeComponentOf` the form; otherwise it is
// `inst_id` itself.
SemIR::TypeInstId type_component_inst_id;
};
} // namespace
@@ -211,7 +215,8 @@ static auto HandleAnyBindingPatternType(
auto as_type = ExprAsType(context, binding_node_id, *self_type_inst_id);
return {.node_id = binding_node_id,
.inst_id = as_type.inst_id,
.type_component_id = as_type.type_id};
.type_component_id = as_type.type_id,
.type_component_inst_id = as_type.inst_id};
}
auto [node_id, original_inst_id] = context.node_stack().PopExprWithNodeId();
@@ -277,12 +282,14 @@ static auto HandleAnyBindingPatternType(
auto as_form = FormExprAsForm(context, node_id, original_inst_id);
return {.node_id = node_id,
.inst_id = as_form.form_inst_id,
.type_component_id = as_form.type_component_id};
.type_component_id = as_form.type_component_id,
.type_component_inst_id = as_form.type_component_inst_id};
} else {
auto as_type = ExprAsType(context, node_id, original_inst_id);
return {.node_id = node_id,
.inst_id = as_type.inst_id,
.type_component_id = as_type.type_id};
.type_component_id = as_type.type_id,
.type_component_inst_id = as_type.inst_id};
}
}
@@ -339,8 +346,9 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id,
context, node_id, type_expr_region_id, type_expr.type_component_id,
{.kind = kind,
.type_id = GetPatternType(context, type_expr.type_component_id),
.entity_name_id = AddBindingEntityName(context, name_id, form_id,
/*is_unused=*/false, phase),
.entity_name_id = AddBindingEntityName(
context, name_id, type_expr.type_component_inst_id, form_id,
/*is_unused=*/false, phase),
.subpattern_id = subpattern_id});
// TODO: If `is_generic`, then `binding.bind_id is a SymbolicBinding. Subst
@@ -605,7 +613,7 @@ auto HandleParseNode(Context& context,
// compile time binding. This is popped when handling the
// CompileTimeBindingPatternId.
context.scope_stack().PushForSameRegion();
MakePeriodSelfFacetValue(context, node_id, GetEmptyFacetType(context));
MakePeriodSelfFacetValue(context, node_id, SemIR::TypeType::TypeId);
context.node_stack().Push(
node_id, SemIR::ElementIndex(context.binding_type_where_count()));
return true;
+182 -151
View File
@@ -373,6 +373,166 @@ static auto DiagnosePositionalParams(Context& context,
function_info.param_patterns_id = SemIR::InstBlockId::Empty;
}
// Diagnoses any default values for function parameters that have not been
// completely specified, which is a requirement on the first owning declaration
// of a function.
static auto DiagnoseDefaultValuesNotSpecified(
Context& context, llvm::ArrayRef<SemIR::InstId> unspecified_inst_ids)
-> void {
for (auto inst_id : unspecified_inst_ids) {
CARBON_DIAGNOSTIC(PatternDefaultValueNotSpecified, Error,
"found unspecified default parameter value in the "
"function's first owning declaration");
context.emitter().Emit(inst_id, PatternDefaultValueNotSpecified);
}
}
// For the top-level parameter patterns list, and for any level of nested tuple
// patterns, ensure that if a subpattern provides a default value, all
// subsequent patterns at that level of nesting must provide a default value as
// well. Returns the number of default values provided at the top level of the
// function parameter, useful for efficient arity checking in callers later on.
//
// TODO: per https://github.com/carbon-language/carbon-lang/issues/7529, this
// should also consider automatically supplied defaults for fully-specified
// tuple subpatterns, and consider them as having a default for the purposes
// of the out-of-order detection. It will also need to detect the error
// condition when a default is also specified for those fully-specified tuple
// subpatterns.
static auto CheckDefaults(Context& context, SemIR::Function& function)
-> int32_t {
if (!function.param_patterns_id.has_value()) {
return 0;
}
struct PatternLevelState {
// The inst ids of the subpatterns on this level of tuple subpattern
// nesting, treated as a work list, so in reverse order of declaration.
llvm::SmallVector<SemIR::InstId> subpattern_ids;
// If patterns at this level of nesting have default values, this refers
// to the first instruction to specify a default, useful for diagnostics.
SemIR::InstId first_pattern_with_default = SemIR::InstId::None;
// If we encounter a tuple-pattern during processing, we suspend processing
// of this pattern level, in the middle of processing a single pattern from
// root to leaves. So we record the current state of processing of a single
// pattern to return to it after processing any tuple subpatterns.
// True if the current pattern being processed has a default value
// specified.
bool current_pattern_has_default = false;
// The current pattern we are processing, stored separately since it's been
// popped from the `pattern_work_list` and already processed, just may need
// subsequent processing.
SemIR::InstId current_id = SemIR::InstId::None;
// A work list of patterns to be processed at this level of nesting.
llvm::SmallVector<SemIR::InstId> pattern_work_list;
// A list of subpatterns missing required defaults, to coalesce error
// reporting into a single diagnostic.
llvm::SmallVector<SemIR::InstId> patterns_missing_defaults;
// A count of the number of patterns on this level that have defaults.
int32_t default_count = 0;
};
llvm::SmallVector<PatternLevelState> level_state_stack;
size_t default_count = 0;
level_state_stack.push_back({});
llvm::append_range(
level_state_stack.back().subpattern_ids,
llvm::reverse(context.inst_blocks().Get(function.param_patterns_id)));
while (!level_state_stack.empty()) {
PatternLevelState* state = &level_state_stack.back();
while (!state->subpattern_ids.empty() ||
!state->pattern_work_list.empty() || state->current_id.has_value()) {
// If we're not resuming processing a pattern from a nested state, start
// processing the next subpattern.
if (!state->current_id.has_value()) {
state->pattern_work_list.push_back(
state->subpattern_ids.pop_back_val());
state->current_pattern_has_default = false;
}
while (!state->pattern_work_list.empty()) {
state->current_id = state->pattern_work_list.pop_back_val();
auto inst = context.insts().Get(state->current_id);
CARBON_KIND_SWITCH(inst) {
case CARBON_KIND(SemIR::DefaultValuePattern default_value_pattern): {
state->current_pattern_has_default = true;
state->default_count += 1;
state->pattern_work_list.push_back(
default_value_pattern.subpattern_id);
break;
}
case CARBON_KIND(
SemIR::WrapperBindingPattern wrapper_binding_pattern): {
state->pattern_work_list.push_back(
wrapper_binding_pattern.subpattern_id);
break;
}
case CARBON_KIND(SemIR::TuplePattern tuple_pattern): {
auto elements =
context.inst_blocks().Get(tuple_pattern.elements_id);
if (!elements.empty()) {
// Start a new state for the nested tuple pattern elements.
level_state_stack.push_back({});
state = &level_state_stack.back();
llvm::append_range(state->subpattern_ids,
llvm::reverse(elements));
}
break;
}
default:
// We only process patterns containing subpatterns, so this is an
// intentional no-op.
break;
}
}
// Finished processing this subpattern, detect a missing default if
// required.
if (state->current_pattern_has_default &&
!state->first_pattern_with_default.has_value()) {
state->first_pattern_with_default = state->current_id;
} else if (!state->current_pattern_has_default &&
state->first_pattern_with_default.has_value()) {
state->patterns_missing_defaults.push_back(state->current_id);
}
state->current_id = SemIR::InstId::None;
}
// Finished processing this tuple-pattern, emit diagnostics if any.
if (!state->patterns_missing_defaults.empty()) {
CARBON_DIAGNOSTIC(RequiredPatternDefaultValueMissing, Error,
"this pattern is missing a required default value.");
CARBON_DIAGNOSTIC(RequiredPatternDefaultValueFirstDefault, Note,
"all patterns to the right of this first pattern with "
"a default value must also specify a default value.");
CARBON_DIAGNOSTIC(
RequiredPatternDefaultValueMissingAdditional, Note,
"this pattern is also missing a required default value.");
auto inst_ref = llvm::ArrayRef(state->patterns_missing_defaults);
auto builder = context.emitter().Build(
inst_ref.consume_front(), RequiredPatternDefaultValueMissing);
for (auto inst_id : inst_ref) {
builder.Note(inst_id, RequiredPatternDefaultValueMissingAdditional);
}
builder.Note(state->first_pattern_with_default,
RequiredPatternDefaultValueFirstDefault);
builder.Emit();
}
// Extract the count from the level we just completed, overwriting any
// nested level value extracted previously.
default_count = level_state_stack.back().default_count;
level_state_stack.pop_back();
}
return default_count;
}
// Build a FunctionDecl describing the signature of a function. This
// handles the common logic shared by function declaration syntax and function
// definition syntax.
@@ -436,27 +596,33 @@ static auto BuildFunctionDecl(Context& context,
// Build the function entity. This will be merged into an existing function if
// there is one, or otherwise added to the function store.
auto function_info = SemIR::Function{
name_context.MakeEntityWithParamsBase(name, decl_id, is_extern,
introducer.extern_library),
{
.call_param_patterns_id = name.call_param_patterns_id,
.call_params_id = name.call_params_id,
.call_param_default_values_id = name.call_param_default_values_id,
.call_param_ranges = name.param_ranges,
.return_type_inst_id = return_type_inst_id,
.return_form_inst_id = return_form_inst_id,
.return_pattern_id = return_pattern_id,
.virtual_modifier = virtual_modifier,
.evaluation_mode = evaluation_mode,
.interface_modifier = interface_modifier,
.self_param_id = self_param_id,
}};
auto function_info =
SemIR::Function{name_context.MakeEntityWithParamsBase(
name, decl_id, is_extern, introducer.extern_library),
{
.call_param_patterns_id = name.call_param_patterns_id,
.call_params_id = name.call_params_id,
.call_param_ranges = name.param_ranges,
.return_type_inst_id = return_type_inst_id,
.return_form_inst_id = return_form_inst_id,
.return_pattern_id = return_pattern_id,
.virtual_modifier = virtual_modifier,
.evaluation_mode = evaluation_mode,
.interface_modifier = interface_modifier,
.self_param_id = self_param_id,
}};
if (is_definition) {
function_info.definition_id = decl_id;
}
function_info.default_value_arity = CheckDefaults(context, function_info);
DiagnosePositionalParams(context, function_info);
if (name_context.state != DeclNameStack::NameContext::State::Poisoned &&
!name_context.prev_inst_id().has_value()) {
DiagnoseDefaultValuesNotSpecified(
context, context.inst_blocks().Get(name.unspecified_values_block_id));
}
TryMergeRedecl(
context, name_context, std::nullopt,
@@ -579,145 +745,10 @@ static auto DiagnoseUnusedMarkersWithoutDefinition(
}
}
// For the top-level parameter patterns list, and for any level of nested tuple
// patterns, ensure that if a subpattern provides a default value, all
// subsequent patterns at that level of nesting must provide a default value as
// well.
// TODO: per https://github.com/carbon-language/carbon-lang/issues/7529, this
// should also consider automatically supplied defaults for fully-specified
// tuple subpatterns, and consider them as having a default for the purposes
// of the out-of-order detection. It will also need to detect the error
// condition when a default is also specified for those fully-specified tuple
// subpatterns.
static auto DiagnoseOutOfOrderDefaults(Context& context,
SemIR::FunctionId function_id) -> void {
const auto& function = context.functions().Get(function_id);
if (!function.param_patterns_id.has_value()) {
return;
}
struct PatternLevelState {
// The inst ids of the subpatterns on this level of tuple subpattern
// nesting, treated as a work list, so in reverse order of declaration.
llvm::SmallVector<SemIR::InstId> subpattern_ids;
// If patterns at this level of nesting have default values, this refers
// to the first instruction to specify a default, useful for diagnostics.
SemIR::InstId first_pattern_with_default = SemIR::InstId::None;
// If we encounter a tuple-pattern during processing, we suspend processing
// of this pattern level, in the middle of processing a single pattern from
// root to leaves. So we record the current state of processing of a single
// pattern to return to it after processing any tuple subpatterns.
// True if the current pattern being processed has a default value
// specified.
bool current_pattern_has_default = false;
// The current pattern we are processing, stored separately since it's been
// popped from the `pattern_work_list` and already processed, just may need
// subsequent processing.
SemIR::InstId current_id = SemIR::InstId::None;
// A work list of patterns to be processed at this level of nesting.
llvm::SmallVector<SemIR::InstId> pattern_work_list;
// A list of subpatterns missing required defaults, to coalesce error
// reporting into a single diagnostic.
llvm::SmallVector<SemIR::InstId> patterns_missing_defaults;
};
llvm::SmallVector<PatternLevelState> level_state_stack;
level_state_stack.push_back({});
llvm::append_range(
level_state_stack.back().subpattern_ids,
llvm::reverse(context.inst_blocks().Get(function.param_patterns_id)));
while (!level_state_stack.empty()) {
PatternLevelState* state = &level_state_stack.back();
while (!state->subpattern_ids.empty() ||
!state->pattern_work_list.empty() || state->current_id.has_value()) {
// If we're not resuming processing a pattern from a nested state, start
// processing the next subpattern.
if (!state->current_id.has_value()) {
state->pattern_work_list.push_back(
state->subpattern_ids.pop_back_val());
state->current_pattern_has_default = false;
}
while (!state->pattern_work_list.empty()) {
state->current_id = state->pattern_work_list.pop_back_val();
auto inst = context.insts().Get(state->current_id);
CARBON_KIND_SWITCH(inst) {
case CARBON_KIND(SemIR::DefaultValuePattern default_value_pattern): {
state->current_pattern_has_default = true;
state->pattern_work_list.push_back(
default_value_pattern.subpattern_id);
break;
}
case CARBON_KIND(
SemIR::WrapperBindingPattern wrapper_binding_pattern): {
state->pattern_work_list.push_back(
wrapper_binding_pattern.subpattern_id);
break;
}
case CARBON_KIND(SemIR::TuplePattern tuple_pattern): {
auto elements =
context.inst_blocks().Get(tuple_pattern.elements_id);
if (!elements.empty()) {
// Start a new state for the nested tuple pattern elements.
level_state_stack.push_back({});
state = &level_state_stack.back();
llvm::append_range(state->subpattern_ids,
llvm::reverse(elements));
}
break;
}
default:
// We only process patterns containing subpatterns, so this is an
// intentional no-op.
break;
}
}
// Finished processing this subpattern, detect a missing default if
// required.
if (state->current_pattern_has_default &&
!state->first_pattern_with_default.has_value()) {
state->first_pattern_with_default = state->current_id;
} else if (!state->current_pattern_has_default &&
state->first_pattern_with_default.has_value()) {
state->patterns_missing_defaults.push_back(state->current_id);
}
state->current_id = SemIR::InstId::None;
}
// Finished processing this tuple-pattern, emit diagnostics if any.
if (!state->patterns_missing_defaults.empty()) {
CARBON_DIAGNOSTIC(RequiredPatternDefaultValueMissing, Error,
"this pattern is missing a required default value.");
CARBON_DIAGNOSTIC(RequiredPatternDefaultValueFirstDefault, Note,
"all patterns to the right of this first pattern with "
"a default value must also specify a default value.");
CARBON_DIAGNOSTIC(
RequiredPatternDefaultValueMissingAdditional, Note,
"this pattern is also missing a required default value.");
auto inst_ref = llvm::ArrayRef(state->patterns_missing_defaults);
auto builder = context.emitter().Build(
inst_ref.consume_front(), RequiredPatternDefaultValueMissing);
for (auto inst_id : inst_ref) {
builder.Note(inst_id, RequiredPatternDefaultValueMissingAdditional);
}
builder.Note(state->first_pattern_with_default,
RequiredPatternDefaultValueFirstDefault);
builder.Emit();
}
level_state_stack.pop_back();
}
}
auto HandleParseNode(Context& context, Parse::FunctionDeclId node_id) -> bool {
auto [function_id, decl_id] =
BuildFunctionDecl(context, node_id, /*is_definition=*/false);
DiagnoseUnusedMarkersWithoutDefinition(context, function_id);
DiagnoseOutOfOrderDefaults(context, function_id);
context.decl_name_stack().PopScope();
return true;
}
+2 -2
View File
@@ -200,9 +200,9 @@ static auto PopImplIntroducerAndParamsAsNameComponent(
.param_patterns_id = SemIR::InstBlockId::None,
.call_param_patterns_id = SemIR::InstBlockId::None,
.call_params_id = SemIR::InstBlockId::None,
.call_param_default_values_id = SemIR::InstBlockId::None,
.param_ranges = SemIR::Function::CallParamIndexRanges::Empty,
.pattern_block_id = pattern_block_id};
.pattern_block_id = pattern_block_id,
.unspecified_values_block_id = SemIR::InstBlockId::Empty};
}
// Build an ImplDecl describing the signature of an impl. This handles the
+119 -1
View File
@@ -40,6 +40,92 @@ auto HandleParseNode(Context& context, Parse::InterfaceIntroducerId node_id)
return true;
}
static auto ValidateCoreInterfaceAssociatedFunction(
Context& context, SemIR::InstId decl_id, int index, CoreIdentifier name_id,
SemIR::Function::InterfaceModifier interface_modifier =
SemIR::Function::InterfaceModifier::None) -> bool {
auto loc_id = context.insts().GetCanonicalLocId(decl_id);
auto decl = context.insts().TryGetAs<SemIR::FunctionDecl>(decl_id);
if (!decl) {
context.TODO(loc_id, "associated entity must be a method");
return false;
}
auto fn = context.functions().Get(decl->function_id);
if (fn.name_id != context.core_identifiers().AddNameId(name_id)) {
context.TODO(loc_id,
llvm::formatv("associated function #{} must be named `{}`",
index, name_id));
return false;
}
auto call_params = context.inst_blocks().Get(fn.call_param_patterns_id);
// TODO: extend to support arbitrary parameters.
if (call_params.size() != 1) {
context.TODO(loc_id, "associated function must have exactly 1 parameter");
return false;
}
auto self = context.insts().TryGetAs<SemIR::RefParamPattern>(call_params[0]);
if (!self.has_value() || self->pretty_name_id != SemIR::NameId::SelfValue) {
context.TODO(loc_id,
"associated function must take `ref self` as its parameter");
return false;
}
// TODO: extend to support arbitrary return types.
if (fn.return_type_inst_id.has_value()) {
context.TODO(loc_id, "associated function must not have a return type");
return false;
}
if (fn.interface_modifier != interface_modifier) {
context.TODO(
loc_id,
interface_modifier == SemIR::Function::InterfaceModifier::None
? std::string(
"associated function must not have an interface modifier")
: llvm::formatv("associated function must be `{}`",
interface_modifier));
return false;
}
return true;
}
static auto ValidateCoreDestroy(Context& context, SemIR::LocId loc_id,
SemIR::InstBlockId associated_entities_id)
-> bool {
auto assoc_entities = context.inst_blocks().Get(associated_entities_id);
if (assoc_entities.size() != 3) {
context.TODO(
loc_id,
"interface `Core.Destroy` needs exactly 3 associated functions");
return false;
}
if (!ValidateCoreInterfaceAssociatedFunction(context, assoc_entities[0], 1,
CoreIdentifier::Op)) {
return false;
}
if (!ValidateCoreInterfaceAssociatedFunction(
context, assoc_entities[1], 2, CoreIdentifier::SubobjectDestroy,
SemIR::Function::InterfaceModifier::Final)) {
return false;
}
if (!ValidateCoreInterfaceAssociatedFunction(
context, assoc_entities[2], 3, CoreIdentifier::SelfDestruct,
SemIR::Function::InterfaceModifier::Final)) {
return false;
}
return true;
}
static auto BuildInterfaceDecl(Context& context,
Parse::AnyInterfaceDeclId node_id,
bool is_definition)
@@ -218,7 +304,7 @@ auto HandleParseNode(Context& context,
return true;
}
auto HandleParseNode(Context& context, Parse::InterfaceDefinitionId /*node_id*/)
auto HandleParseNode(Context& context, Parse::InterfaceDefinitionId node_id)
-> bool {
auto interface_id =
context.node_stack().Pop<Parse::NodeKind::InterfaceDefinitionStart>();
@@ -251,6 +337,38 @@ auto HandleParseNode(Context& context, Parse::InterfaceDefinitionId /*node_id*/)
// Finish the definition of interface-without-self.
FinishGenericDefinition(context, interface_info.generic_id);
if (context.sem_ir().package_id() == PackageNameId::Core) {
switch (interface_info.core_interface) {
case SemIR::CoreInterface::Destroy:
return ValidateCoreDestroy(context, node_id,
interface_info.associated_entities_id);
case SemIR::CoreInterface::AddAssignWith:
case SemIR::CoreInterface::AddWith:
case SemIR::CoreInterface::Copy:
case SemIR::CoreInterface::CppRangeForIterate:
case SemIR::CoreInterface::CppUnsafeDeref:
case SemIR::CoreInterface::Dec:
case SemIR::CoreInterface::Default:
case SemIR::CoreInterface::FloatFitsIn:
case SemIR::CoreInterface::DivAssignWith:
case SemIR::CoreInterface::DivWith:
case SemIR::CoreInterface::EqWith:
case SemIR::CoreInterface::Inc:
case SemIR::CoreInterface::IntFitsIn:
case SemIR::CoreInterface::ModAssignWith:
case SemIR::CoreInterface::ModWith:
case SemIR::CoreInterface::MulAssignWith:
case SemIR::CoreInterface::MulWith:
case SemIR::CoreInterface::Negate:
case SemIR::CoreInterface::OrderedWith:
case SemIR::CoreInterface::SubAssignWith:
case SemIR::CoreInterface::SubWith:
case SemIR::CoreInterface::Unknown:
// TODO: validate other core interfaces
return true;
}
}
// The decl_name_stack and scopes are popped by `ProcessNodeIds`.
return true;
}
+4 -6
View File
@@ -58,9 +58,8 @@ auto HandleParseNode(Context& context, Parse::ObserveEqualEqualId node_id)
context.args_type_info_stack().AddInstId(
AddInstInNoBlock<SemIR::ObserveEquivalent>(
context, node_id,
{.lhs_id = GetCanonicalFacetOrTypeValue(context, lhs_as_type.inst_id),
.rhs_id =
GetCanonicalFacetOrTypeValue(context, rhs_as_type.inst_id)}));
{.lhs_id = GetCanonicalFacet(context, lhs_as_type.inst_id),
.rhs_id = GetCanonicalFacet(context, rhs_as_type.inst_id)}));
return true;
}
@@ -90,9 +89,8 @@ auto HandleParseNode(Context& context, Parse::ObserveImplsId node_id) -> bool {
context.args_type_info_stack().AddInstId(
AddInstInNoBlock<SemIR::ObserveImpls>(
context, node_id,
{.lhs_id = GetCanonicalFacetOrTypeValue(context, lhs_as_type.inst_id),
.rhs_id =
GetCanonicalFacetOrTypeValue(context, rhs_as_type.inst_id)}));
{.lhs_id = GetCanonicalFacet(context, lhs_as_type.inst_id),
.rhs_id = GetCanonicalFacet(context, rhs_as_type.inst_id)}));
return true;
}
+17 -16
View File
@@ -154,11 +154,15 @@ auto HandleParseNode(Context& context, Parse::PatternListCommaId /*node_id*/)
auto HandleParseNode(Context& context, Parse::DefaultValueUnspecifiedId node_id)
-> bool {
context.node_stack().Push(
node_id, AddInst<SemIR::UnspecifiedValue>(
context, node_id,
{.type_id = GetSingletonType(
context, SemIR::UnspecifiedValueType::TypeInstId)}));
auto inst_id = AddInst<SemIR::UnspecifiedValue>(
context, node_id,
{.type_id =
GetSingletonType(context, SemIR::UnspecifiedValueType::TypeInstId)});
// Add the unspecified default value for later diagnostics checks.
context.full_pattern_stack().AddUnspecifiedDefaultValue(inst_id);
context.node_stack().Push(node_id, inst_id);
return true;
}
@@ -172,8 +176,6 @@ auto HandleParseNode(Context& context,
auto HandleParseNode(Context& context, Parse::DefaultValuePatternId node_id)
-> bool {
// On entry, the top of the node stack should have an expression for the
// default value. We evaluate it to get a constant.
auto [expr_node_id, expr_inst_id] = context.node_stack().PopExprWithNodeId();
// Ensure we are in an explicit parameter list, otherwise issue a diagnostic.
@@ -186,24 +188,23 @@ auto HandleParseNode(Context& context, Parse::DefaultValuePatternId node_id)
return false;
}
// Evaluate the default value constant.
auto expr_const_id = TryEvalInst(context, expr_inst_id);
if (expr_const_id == SemIR::ConstantId::NotConstant) {
CARBON_DIAGNOSTIC(PatternDefaultValueNotConstant, Error,
"default value for pattern must be constant");
"default value is not a constant");
context.emitter().Emit(
LocIdForDiagnostics(context.insts().GetCanonicalLocId(expr_inst_id)),
PatternDefaultValueNotConstant);
return false;
}
auto value_inst_id = context.constant_values().GetInstId(expr_const_id);
CARBON_CHECK(value_inst_id.has_value());
// Add the value to the default values array in the full pattern stack, for
// recovery later in the NameComponent.
auto default_value_id =
context.full_pattern_stack().AddDefaultValue(value_inst_id);
// Add the value to the default values store. We store the raw value here for
// conversion during pattern matching once the type of the pattern is known.
auto default_value_id = context.default_values().Add(
{.raw_id = expr_inst_id,
.value_id = SemIR::InstId::None,
.is_unspecified =
context.insts().Is<SemIR::UnspecifiedValue>(expr_inst_id)});
// Next on the node stack should be the pattern for which this default was
// specified. We pop that so we can issue the DefaultValuePattern in its
+1 -6
View File
@@ -42,10 +42,6 @@ static auto GetPeriodSelfType(Context& context,
auto frozen_const_id =
FreezePeriodSelf(context, extended_id.AsConstantId());
return context.types().GetTypeIdForTypeConstantId(frozen_const_id);
} else if (facet_type_type_id == SemIR::TypeType::TypeId) {
// The self may be `TypeType` in `type where X impls Y`, so we use an empty
// facet type.
return GetEmptyFacetType(context);
} else {
CARBON_CHECK(facet_type_type_id == SemIR::ErrorInst::TypeId,
"unexpected .Self type {0}", facet_type_type_id);
@@ -435,8 +431,7 @@ auto HandleParseNode(Context& context, Parse::RequirementImplsId node_id)
// Check lhs is a facet and rhs is a facet type.
auto lhs_as_type = ExprAsType(context, lhs_node, lhs_id);
auto rhs_as_type = ExprAsType(context, rhs_node, rhs_id);
if (rhs_as_type.type_id != SemIR::ErrorInst::TypeId &&
!context.types().IsFacetType(rhs_as_type.type_id)) {
if (!context.types().IsFacetTypeOrError(rhs_as_type.type_id)) {
DiagnoseImplsOnNonFacetType(context, rhs_node);
rhs_as_type.type_id = SemIR::ErrorInst::TypeId;
rhs_as_type.inst_id = SemIR::ErrorInst::TypeInstId;
+10 -8
View File
@@ -528,10 +528,10 @@ auto AddImplWitnessForDeclaration(Context& context, SemIR::LocId loc_id,
// value to that type now we know the value of `Self`.
SemIR::TypeId assoc_const_type_id = assoc_constant_decl->type_id;
if (assoc_const_type_id.is_symbolic()) {
auto self_facet = GetConstantFacetValueForType(context, impl.self_id);
auto interface_with_self_specific_id = MakeSpecificWithInnerSelf(
context, loc_id, interface.generic_id, interface.generic_with_self_id,
impl.interface.specific_id, self_facet);
impl.interface.specific_id,
context.constant_values().Get(impl.self_id));
// Get the type of the associated constant in this interface with this
// value for `Self`.
@@ -736,7 +736,11 @@ auto FinishImplWitness(Context& context, const SemIR::Impl& impl) -> void {
}
if (fn.interface_modifier != InterfaceModifier::None) {
witness_value = decl_id;
// We are updating the impl witness table in-place, and we pulled this
// instruction out of a constant value in a different generic, so
// manually ensure the new value gets added to the eval block.
witness_value =
GetOrAddInstWithSpecificConstantValue(context, decl_id);
break;
} else {
CARBON_DIAGNOSTIC(
@@ -861,8 +865,8 @@ auto CheckRequireDeclsSatisfied(Context& context, SemIR::LocId loc_id,
// The IdentifiedFacetType canonicalizes the self facets, so we do the same
// for comparing with it.
auto self_const_id = GetCanonicalFacetOrTypeValue(
context, context.constant_values().Get(impl.self_id));
auto self_const_id =
GetCanonicalFacet(context, context.constant_values().Get(impl.self_id));
// We already identified the `impl.self_id` as the canonical
// `full_constraint_id`, so this should just be a cache lookup and can't fail.
@@ -932,11 +936,9 @@ auto CheckRequireDeclsSatisfied(Context& context, SemIR::LocId loc_id,
return;
}
// Make a facet value for the self type.
auto self_facet = GetConstantFacetValueForType(context, impl.self_id);
auto interface_with_self_specific_id = MakeSpecificWithInnerSelf(
context, loc_id, interface.generic_id, interface.generic_with_self_id,
impl.interface.specific_id, self_facet);
impl.interface.specific_id, context.constant_values().Get(impl.self_id));
for (auto require_id : require_ids) {
const auto& require = context.require_impls().Get(require_id);
+6 -8
View File
@@ -270,7 +270,7 @@ static auto TryGetSpecificWitnessIdForImpl(
// to the facet value here, and if the query was a FacetAccessType we did the
// same there so they still match.
auto deduced_self_const_id =
GetCanonicalFacetOrTypeValue(context, noncanonical_deduced_self_const_id);
GetCanonicalFacet(context, noncanonical_deduced_self_const_id);
if (query_self_const_id != deduced_self_const_id) {
return SemIR::ConstantId::None;
}
@@ -431,8 +431,7 @@ static auto CollectFacetWitnessSources(
// constraints.
const auto& impls = context.where_stack().back().impls;
for (auto [self_const_id, facet_type_const_id] : impls) {
auto canon_self_const_id =
GetCanonicalFacetOrTypeValue(context, self_const_id);
auto canon_self_const_id = GetCanonicalFacet(context, self_const_id);
// TypeType (and ErrorInst) is never stored in the impls stack, so we
// always have a FacetType in `facet_type_const_id`.
auto identified_id = TryToIdentifyFacetType(
@@ -983,8 +982,7 @@ auto LookupImplWitness(Context& context, SemIR::LocId loc_id,
context.insts()
.Get(context.constant_values().GetInstId(query_self_const_id))
.type_id();
CARBON_CHECK((context.types().IsOneOf<SemIR::TypeType, SemIR::FacetType>(
query_self_type_id)));
CARBON_CHECK(context.types().Is<SemIR::FacetType>(query_self_type_id));
// The query facet type value is indeed a facet type.
CARBON_CHECK(context.constant_values().InstIs<SemIR::FacetType>(
query_facet_type_const_id));
@@ -1122,8 +1120,8 @@ auto GetCanonicalQuerySelfForLookupImplWitness(Context& context,
// LookupImplWitness instruction, avoiding multiple constant values for
// `<facet value>` and `<facet value> as type`, which always have the same
// lookup result.
return GetCanonicalFacetOrTypeValue(
context, context.constant_values().Get(self_inst_id));
return GetCanonicalFacet(context,
context.constant_values().Get(self_inst_id));
}
// Record the query which found a final impl witness. It's illegal to
@@ -1171,7 +1169,7 @@ auto EvalLookupSingleFinalWitness(Context& context, SemIR::LocId loc_id,
context.specific_interfaces().Get(eval_query.query_specific_interface_id);
// Ensure specifics don't substitute in weird things for the query self.
CARBON_CHECK(context.types().IsFacetType(
CARBON_CHECK(context.types().Is<SemIR::FacetType>(
context.insts().Get(eval_query.query_self_inst_id).type_id()));
SemIR::ConstantId query_self_const_id =
context.constant_values().Get(eval_query.query_self_inst_id);
+44 -26
View File
@@ -38,6 +38,7 @@
#include "toolchain/sem_ir/inst_kind.h"
#include "toolchain/sem_ir/name_scope.h"
#include "toolchain/sem_ir/observe.h"
#include "toolchain/sem_ir/singleton_insts.h"
#include "toolchain/sem_ir/specific_interface.h"
#include "toolchain/sem_ir/specific_named_constraint.h"
#include "toolchain/sem_ir/type_info.h"
@@ -179,6 +180,9 @@ class ImportContext {
auto import_constant_values() -> const SemIR::ConstantValueStore& {
return import_ir().constant_values();
}
auto import_default_values() -> const SemIR::DefaultValueStore& {
return import_ir().default_values();
}
auto import_entity_names() -> const SemIR::EntityNameStore& {
return import_ir().entity_names();
}
@@ -267,6 +271,9 @@ class ImportContext {
auto local_constant_values() -> SemIR::ConstantValueStore& {
return local_ir().constant_values();
}
auto local_default_values() -> SemIR::DefaultValueStore& {
return local_ir().default_values();
}
auto local_entity_names() -> SemIR::EntityNameStore& {
return local_ir().entity_names();
}
@@ -2306,6 +2313,12 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
SemIR::DefaultValuePattern inst)
-> ResolveResult {
auto subpattern = GetLocalImportRefInfo(resolver, inst.subpattern_id);
const auto& import_default_value =
resolver.import_default_values().Get(inst.default_value_id);
// We import the first owning declaration of a function, which must always
// have default values completely specified.
CARBON_CHECK(!import_default_value.is_unspecified);
auto value = GetLocalImportRefInfo(resolver, import_default_value.value_id);
if (resolver.HasNewWork()) {
return ResolveResult::Retry();
}
@@ -2316,7 +2329,10 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
.type_id = resolver.local_types().GetTypeIdForTypeConstantId(
subpattern.local_type_const_id),
.subpattern_id = AddLoadedImportRef(resolver, subpattern),
.default_value_id = inst.default_value_id,
.default_value_id = resolver.local_default_values().Add(
{.raw_id = SemIR::InstId::None,
.value_id = AddLoadedImportRef(resolver, value),
.is_unspecified = false}),
});
}
@@ -2425,7 +2441,6 @@ static auto ImportFunctionDecl(
{GetIncompleteLocalEntityBase(context, function_decl_id, import_function),
{.call_param_patterns_id = SemIR::InstBlockId::None,
.call_params_id = SemIR::InstBlockId::None,
.call_param_default_values_id = SemIR::InstBlockId::None,
.call_param_ranges = import_function.call_param_ranges,
.return_type_inst_id = SemIR::TypeInstId::None,
.return_form_inst_id = SemIR::InstId::None,
@@ -2571,17 +2586,6 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
auto call_param_patterns = GetLocalBlockImportRefInfo(
resolver, import_function.call_param_patterns_id);
auto call_param_default_values = GetLocalBlockImportRefInfo(
resolver, import_function.call_param_default_values_id);
llvm::SmallVector<SemIR::InstId> imported_default_values;
if (call_param_default_values.has_value()) {
auto import_fn = [&resolver](const auto& import_info) {
return GetLocalConstantInstId(resolver, import_info.import_inst_id);
};
llvm::append_range(imported_default_values,
llvm::map_range(*call_param_default_values, import_fn));
}
auto return_type_const_id = SemIR::ConstantId::None;
if (import_function.return_type_inst_id.has_value()) {
return_type_const_id =
@@ -2621,6 +2625,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
auto thunk_specific_data = GetLocalSpecificData(
resolver, import_thunk_info ? import_thunk_info->specific_id
: SemIR::SpecificId::None);
auto thunk_override_self_type_const_id = SemIR::ConstantId::None;
if (import_thunk_info) {
thunk_override_self_type_const_id =
GetLocalConstantId(resolver, import_thunk_info->override_self_type_id);
}
auto& new_function = resolver.local_functions().Get(function_id);
if (resolver.HasNewWork()) {
@@ -2631,10 +2640,6 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
// Add the function declaration.
new_function.call_param_patterns_id =
AddLoadedImportRefBlock(resolver, call_param_patterns);
if (call_param_default_values.has_value()) {
new_function.call_param_default_values_id =
resolver.local_inst_blocks().Add(imported_default_values);
}
new_function.parent_scope_id = parent_scope_id;
new_function.implicit_param_patterns_id =
AddLoadedImportRefBlock(resolver, implicit_param_patterns);
@@ -2656,6 +2661,7 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
if (import_function.definition_id.has_value()) {
new_function.definition_id = new_function.first_owning_decl_id;
}
new_function.default_value_arity = import_function.default_value_arity;
switch (import_function.special_function_kind) {
case SemIR::Function::SpecialFunctionKind::CppThunk:
@@ -2689,6 +2695,11 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
local_thunk_info.specific_id = GetOrAddLocalSpecific(
resolver, import_thunk_info->specific_id, thunk_specific_data);
}
if (thunk_override_self_type_const_id.has_value()) {
local_thunk_info.override_self_type_id =
resolver.local_types().GetTypeIdForTypeConstantId(
thunk_override_self_type_const_id);
}
new_function.SetThunk(resolver.local_ir().thunks().Add(local_thunk_info));
break;
}
@@ -4569,8 +4580,9 @@ static auto TryResolveInstCanonical(ImportRefResolver& resolver,
"Constant value of constant instruction should refer to "
"the same instruction");
if (SemIR::IsSingletonInstId(constant_inst_id)) {
// Constants for builtins can be directly copied.
if (SemIR::IsSingletonInstId(constant_inst_id) ||
constant_inst_id == SemIR::TypeType::TypeInstId) {
// Constants for singletons and TypeType can be directly copied.
return ResolveResult::Done(
resolver.local_constant_values().Get(constant_inst_id));
}
@@ -4976,15 +4988,21 @@ auto ImportRefResolver::ResolveType(SemIR::TypeId import_type_id)
auto import_type_const_id = import_ir().types().GetConstantId(import_type_id);
CARBON_CHECK(import_type_const_id.has_value());
if (auto import_type_inst_id = import_ir().types().GetAsTypeInstId(
import_ir().constant_values().GetInstId(import_type_const_id));
SemIR::IsSingletonInstId(import_type_inst_id)) {
// Builtins don't require constant resolution; we can use them directly.
auto import_type_inst_id = import_ir().types().GetAsTypeInstId(
import_ir().constant_values().GetInstId(import_type_const_id));
// Builtin types don't require constant resolution; we can use them directly.
if (SemIR::IsSingletonInstId(import_type_inst_id)) {
// Singletons are all types, and need to go through GetSingletonType to
// complete them.
return GetSingletonType(local_context(), import_type_inst_id);
} else {
return local_types().GetTypeIdForTypeConstantId(
ResolveConstant(import_type_id.AsConstantId()));
} else if (import_type_inst_id == SemIR::TypeType::TypeInstId) {
// TypeType is the other builtin type, and is already complete.
return SemIR::TypeType::TypeId;
}
return local_types().GetTypeIdForTypeConstantId(
ResolveConstant(import_type_id.AsConstantId()));
}
auto ImportRefResolver::HasNewWork() -> bool {
+42 -56
View File
@@ -99,54 +99,41 @@ static auto IsInstanceType(Context& context, SemIR::TypeId type_id) -> bool {
return false;
}
auto GetHighestAllowedAccess(Context& context, SemIR::LocId loc_id,
auto GetHighestAllowedAccess(Context& context,
SemIR::ConstantId name_scope_const_id)
-> SemIR::AccessKind {
SemIR::ScopeLookupResult lookup_result =
LookupUnqualifiedName(context, loc_id, SemIR::NameId::SelfType,
/*required=*/false)
.scope_result;
CARBON_CHECK(!lookup_result.is_poisoned());
if (!lookup_result.is_found()) {
SemIR::NameScopeId access_context_scope_id = context.access_context();
if (!access_context_scope_id.has_value()) {
return SemIR::AccessKind::Public;
}
// TODO: Support other types for `Self`.
auto self_class_type = context.insts().TryGetAs<SemIR::ClassType>(
lookup_result.target_inst_id());
if (!self_class_type) {
return SemIR::AccessKind::Public;
}
auto self_class_info = context.classes().Get(self_class_type->class_id);
// TODO: Support other types.
if (auto class_type =
context.constant_values().TryGetInstAs<SemIR::ClassType>(
name_scope_const_id)) {
auto class_info = context.classes().Get(class_type->class_id);
if (self_class_info.self_type_id == class_info.self_type_id) {
return SemIR::AccessKind::Private;
// Check if private access is allowed.
while (access_context_scope_id.has_value()) {
if (class_info.scope_id == access_context_scope_id) {
return SemIR::AccessKind::Private;
}
const auto& scope = context.name_scopes().Get(access_context_scope_id);
access_context_scope_id = scope.parent_scope_id();
}
// If the `type_id` of `Self` does not match with the one we're currently
// accessing, try checking if this class is of the parent type of `Self`.
if (auto base_type_id = self_class_info.GetBaseType(
context.sem_ir(), self_class_type->specific_id);
base_type_id.has_value()) {
if (context.types().GetConstantId(base_type_id) == name_scope_const_id) {
return SemIR::AccessKind::Protected;
}
// TODO: Also check whether this base class has a base class of its own.
} else if (auto adapt_type_id = self_class_info.GetAdaptedType(
context.sem_ir(), self_class_type->specific_id);
adapt_type_id.has_value()) {
if (context.types().GetConstantId(adapt_type_id) == name_scope_const_id) {
// TODO: Should we be allowed to access protected fields of a type we
// are adapting? The design doesn't allow this.
// Check if protected access is allowed.
access_context_scope_id = context.access_context();
const auto& scope = context.name_scopes().Get(access_context_scope_id);
for (auto extended_scope_id : scope.extended_scopes()) {
auto const_id = context.constant_values().Get(extended_scope_id);
if (const_id == name_scope_const_id) {
return SemIR::AccessKind::Protected;
}
// TODO: also check indirectly-extended scopes, as well as extended
// scopes of parent scopes of the access context.
}
}
@@ -161,22 +148,21 @@ static auto ScopeNeedsImplLookup(Context& context,
SemIR::InstId inst_id =
context.constant_values().GetInstId(name_scope_const_id);
CARBON_CHECK(inst_id.has_value());
SemIR::Inst inst = context.insts().Get(inst_id);
if (inst.Is<SemIR::FacetType>()) {
// Don't perform impl lookup if an associated entity is named as a member of
// a facet type.
return false;
}
if (inst.Is<SemIR::Namespace>()) {
if (context.insts().Is<SemIR::Namespace>(inst_id)) {
// Don't perform impl lookup if an associated entity is named as a namespace
// member.
// TODO: This case is not yet listed in the design.
return false;
}
auto type_id = context.types().GetTypeIdForTypeInstId(inst_id);
// Don't perform impl lookup if an associated entity is named as a member of
// a constrained facet type.
//
// Any other kind of scope is assumed to be a type that implements the
// interface containing the associated entity, and impl lookup is performed.
return true;
return !context.types().IsConstrainedFacetType(type_id);
}
static auto PerformImplWitnessAccessAndSubstitute(
@@ -318,7 +304,7 @@ static auto LookupMemberNameInScope(Context& context, SemIR::LocId loc_id,
AccessInfo access_info = {
.constant_id = name_scope_const_id,
.highest_allowed_access =
GetHighestAllowedAccess(context, loc_id, name_scope_const_id),
GetHighestAllowedAccess(context, name_scope_const_id),
};
LookupResult result = LookupQualifiedName(
context, loc_id, name_id, lookup_scopes, required, access_info);
@@ -503,9 +489,8 @@ static auto PerformActionHelper(Context& context, SemIR::LocId loc_id,
SemIR::InstId base_id, SemIR::NameId name_id,
bool required) -> SemIR::InstId {
// Unwrap the facet value in `base_id` if possible.
if (auto facet_value = TryGetCanonicalFacetValue(context, base_id);
facet_value.has_value()) {
base_id = facet_value;
if (auto facet = TryGetCanonicalFacet(context, base_id); facet.has_value()) {
base_id = facet;
}
// If the base is a name scope, such as a class or namespace, perform lookup
@@ -528,7 +513,7 @@ static auto PerformActionHelper(Context& context, SemIR::LocId loc_id,
// `base_id` (as part the class case above), as the `base_id` facet should
// have member names that directly name members of the `impl`.
auto base_type_id = context.insts().Get(base_id).type_id();
if (context.types().Is<SemIR::FacetType>(base_type_id)) {
if (context.types().IsConstrainedFacetType(base_type_id)) {
// Name lookup into a facet requires the facet type to be complete, so
// that any names available through the facet type are known for the
// facet.
@@ -602,15 +587,15 @@ static auto PerformActionHelper(Context& context, SemIR::LocId loc_id,
auto lookup_const_id =
context.types().GetConstantId(unqualified_base_type_id);
// TODO: If the type is a facet, we look through it into the facet's type (a
// FacetType) for names. According to the design, we shouldn't need to do
// this, as the facet should have member names that directly name members of
// the `impl`.
auto base_type_as_facet = GetCanonicalFacetOrTypeValue(
context, context.types().GetTypeInstId(base_type_id));
// TODO: If the type is a constrained facet, we look through it into the
// facet's type (a FacetType) for names. According to the design, we shouldn't
// need to do this, as the facet should have member names that directly name
// members of the `impl`.
auto base_type_as_facet =
GetCanonicalFacet(context, context.types().GetTypeInstId(base_type_id));
auto base_type_facet_type_id =
context.insts().Get(base_type_as_facet).type_id();
if (context.types().Is<SemIR::FacetType>(base_type_facet_type_id)) {
if (context.types().IsConstrainedFacetType(base_type_facet_type_id)) {
lookup_const_id = context.types().GetConstantId(base_type_facet_type_id);
}
@@ -619,11 +604,12 @@ static auto PerformActionHelper(Context& context, SemIR::LocId loc_id,
if (AppendLookupScopesForConstant(
context, loc_id, lookup_const_id,
// The `self_type_const_id` should be the type of `base_id` even if
// it's a facet.
// it's a constrained facet.
//
// TODO: This can be replaced with `lookup_const_id` once we stop
// having to look through the facet at its type for the scope.
context.types().GetConstantId(base_type_id), /*extended_scope=*/false,
// having to look through the constrained facet at its type for the
// scope.
base_type_id.AsConstantId(), /*extended_scope=*/false,
&lookup_scopes)) {
auto member_id = LookupMemberNameInScope(
context, loc_id, base_id, name_id, lookup_const_id, lookup_scopes,
+1 -1
View File
@@ -13,7 +13,7 @@ namespace Carbon::Check {
// Returns the highest allowed access for members of `name_scope_const_id`. For
// example, if this returns `Protected` then only `Public` and `Protected`
// accesses are allowed -- not `Private`.
auto GetHighestAllowedAccess(Context& context, SemIR::LocId loc_id,
auto GetHighestAllowedAccess(Context& context,
SemIR::ConstantId name_scope_const_id)
-> SemIR::AccessKind;
+24 -46
View File
@@ -339,14 +339,33 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param,
auto prev_default_value_pattern =
prev_param_pattern.As<SemIR::DefaultValuePattern>();
// If the new pattern specified a default value, it must match the
// previously declared default value.
auto& new_default_value = context.default_values().Get(
new_default_value_pattern.default_value_id);
const auto& prev_default_value = context.default_values().Get(
prev_default_value_pattern.default_value_id);
if (!new_default_value.is_unspecified) {
// We require first owning declaration to always specify a default
// value.
CARBON_CHECK(!prev_default_value.is_unspecified);
auto new_constant_id =
context.constant_values().Get(new_default_value.value_id);
auto prev_constant_id =
context.constant_values().Get(prev_default_value.value_id);
if (new_constant_id != prev_constant_id) {
emit_general_diagnostic();
return false;
}
} else {
// If the new default value was left unspecified, we copy the previous
// processed default value into the new default value.
new_default_value.value_id = prev_default_value.value_id;
}
pattern_stack.push_back(
{.prev_id = prev_default_value_pattern.subpattern_id,
.new_id = new_default_value_pattern.subpattern_id});
// The node kind comparison should catch this on the mismatched patterns
// prior to this, so the indices should never mismatch.
CARBON_CHECK(prev_default_value_pattern.default_value_id.index ==
new_default_value_pattern.default_value_id.index);
break;
}
default: {
@@ -693,43 +712,6 @@ static auto FillPrevEntityInfo(Context& context,
}
}
// Updates the default values in `prev_function` to include any of those not
// previously specified and that are now specified in `new_function`.
static auto MergeFunctionParamDefaultValues(Context& context,
SemIR::Function& prev_function,
const SemIR::Function& new_function)
-> void {
CARBON_CHECK(prev_function.call_param_default_values_id.has_value() ==
new_function.call_param_default_values_id.has_value());
if (!prev_function.call_param_default_values_id.has_value()) {
return;
}
auto prev_value_inst_ids =
context.inst_blocks().Get(prev_function.call_param_default_values_id);
auto new_value_inst_ids =
context.inst_blocks().Get(new_function.call_param_default_values_id);
CARBON_CHECK(prev_value_inst_ids.size() == new_value_inst_ids.size());
llvm::SmallVector<SemIR::InstId> merged_value_inst_ids;
bool merge_has_new_info = false;
merged_value_inst_ids.reserve(prev_value_inst_ids.size());
for (size_t i = 0; i < prev_value_inst_ids.size(); ++i) {
bool had_value =
!context.insts().Is<SemIR::UnspecifiedValue>(prev_value_inst_ids[i]);
auto merged_id = had_value ? prev_value_inst_ids[i] : new_value_inst_ids[i];
merge_has_new_info |=
!had_value && !context.insts().Is<SemIR::UnspecifiedValue>(merged_id);
merged_value_inst_ids.push_back(merged_id);
}
if (merge_has_new_info) {
auto merged_block_id = context.inst_blocks().Add(merged_value_inst_ids);
prev_function.call_param_default_values_id = merged_block_id;
}
}
template <typename EntityT>
auto TryMergeRedecl(Context& context,
const DeclNameStack::NameContext& name_context,
@@ -904,10 +886,6 @@ auto TryMergeRedecl(Context& context,
if (is_definition) {
prev_entity.MergeDefinition(entity_info.new_entity);
if constexpr (IsFunction) {
MergeFunctionParamDefaultValues(context, prev_entity,
entity_info.new_entity);
}
}
auto replace_prev_inst = prev_import_ir_id.has_value();
+16 -20
View File
@@ -13,7 +13,6 @@ auto PopNameComponent(Context& context, SemIR::InstId return_pattern_id)
-> NameComponent {
Parse::NodeId first_param_node_id = Parse::NoneNodeId();
Parse::NodeId last_param_node_id = Parse::NoneNodeId();
auto call_param_default_values_id = SemIR::InstBlockId::None;
// Explicit params.
auto [params_node_id, param_patterns_id] =
@@ -24,10 +23,6 @@ auto PopNameComponent(Context& context, SemIR::InstId return_pattern_id)
context.node_stack()
.PopForSoloNodeId<Parse::NodeKind::ExplicitParamListStart>();
last_param_node_id = params_node_id;
if (!context.full_pattern_stack().GetDefaultValues().empty()) {
call_param_default_values_id = context.inst_blocks().Add(
context.full_pattern_stack().GetDefaultValues());
}
} else {
param_patterns_id = SemIR::InstBlockId::None;
}
@@ -53,6 +48,7 @@ auto PopNameComponent(Context& context, SemIR::InstId return_pattern_id)
auto call_params_id = SemIR::InstBlockId::None;
auto param_ranges = SemIR::Function::CallParamIndexRanges::Empty;
auto pattern_block_id = SemIR::InstBlockId::None;
auto unspecified_values_block_id = SemIR::InstBlockId::Empty;
if (param_patterns_id->has_value() ||
implicit_param_patterns_id->has_value() ||
return_pattern_id.has_value()) {
@@ -61,6 +57,8 @@ auto PopNameComponent(Context& context, SemIR::InstId return_pattern_id)
call_param_patterns_id = results.call_param_patterns_id;
call_params_id = results.call_params_id;
param_ranges = results.param_ranges;
unspecified_values_block_id = context.inst_blocks().Add(
context.full_pattern_stack().GetUnspecifiedDefaultValues());
pattern_block_id = context.pattern_block_stack().Pop();
context.full_pattern_stack().PopFullPattern();
}
@@ -68,21 +66,19 @@ auto PopNameComponent(Context& context, SemIR::InstId return_pattern_id)
auto [name_loc_id, name_id] =
context.node_stack().PopWithNodeId<Parse::NodeCategory::NonExprName>();
return {
.name_loc_id = name_loc_id,
.name_id = name_id,
.first_param_node_id = first_param_node_id,
.last_param_node_id = last_param_node_id,
.implicit_params_loc_id = implicit_params_node_id,
.implicit_param_patterns_id = *implicit_param_patterns_id,
.params_loc_id = params_node_id,
.param_patterns_id = *param_patterns_id,
.call_param_patterns_id = call_param_patterns_id,
.call_params_id = call_params_id,
.call_param_default_values_id = call_param_default_values_id,
.param_ranges = param_ranges,
.pattern_block_id = pattern_block_id,
};
return {.name_loc_id = name_loc_id,
.name_id = name_id,
.first_param_node_id = first_param_node_id,
.last_param_node_id = last_param_node_id,
.implicit_params_loc_id = implicit_params_node_id,
.implicit_param_patterns_id = *implicit_param_patterns_id,
.params_loc_id = params_node_id,
.param_patterns_id = *param_patterns_id,
.call_param_patterns_id = call_param_patterns_id,
.call_params_id = call_params_id,
.param_ranges = param_ranges,
.pattern_block_id = pattern_block_id,
.unspecified_values_block_id = unspecified_values_block_id};
}
// Pop the name of a declaration from the node stack, and diagnose if it has
+3 -2
View File
@@ -40,12 +40,13 @@ struct NameComponent {
// SemIR::EntityWithParamsBase).
SemIR::InstBlockId call_param_patterns_id;
SemIR::InstBlockId call_params_id;
// The pattern default values as extracted from the parameter list.
SemIR::InstBlockId call_param_default_values_id;
SemIR::Function::CallParamIndexRanges param_ranges;
// The pattern block.
SemIR::InstBlockId pattern_block_id;
// The `UnspecifiedValue` insts from the parameter default values, if any.
SemIR::InstBlockId unspecified_values_block_id;
};
// Pops a name component from the node stack (and pattern block stack, if it has
+32 -49
View File
@@ -9,7 +9,6 @@
#include "common/raw_string_ostream.h"
#include "toolchain/check/control_flow.h"
#include "toolchain/check/cpp/import.h"
#include "toolchain/check/facet_type.h"
#include "toolchain/check/generic.h"
#include "toolchain/check/import.h"
#include "toolchain/check/import_ref.h"
@@ -22,6 +21,7 @@
#include "toolchain/sem_ir/generic.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/name_scope.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::Check {
@@ -297,50 +297,24 @@ struct ProhibitedAccessInfo {
static auto GetSelfFacetForInterfaceFromLookupSelfType(
Context& context, const SemIR::GenericId generic_with_self_id,
SemIR::ConstantId self_type_const_id) -> SemIR::ConstantId {
if (!self_type_const_id.has_value()) {
// In a lookup into a non-lexical scope, there is no self-type from the
// lookup for the interface-with-self specific. So the self-type we use is
// the abstract symbolic Self from the self specific of the
// interface-with-self.
auto self_specific_args_id = context.specifics().GetArgsOrEmpty(
context.generics().GetSelfSpecific(generic_with_self_id));
auto self_specific_args = context.inst_blocks().Get(self_specific_args_id);
return context.constant_values().Get(self_specific_args.back());
if (self_type_const_id.has_value() &&
!context.constant_values().InstIs<SemIR::FacetType>(self_type_const_id)) {
// Extended name lookup into the type of `x`, such as in a member access
// `x.F`. We can find a facet type extended scope from the type of `x`.
return self_type_const_id;
}
if (context.constant_values().InstIs<SemIR::FacetType>(self_type_const_id)) {
// We are looking directly in a facet type, like `I.F` for an interface `I`,
// which means there is no self-type from the lookup for the
// interface-with-self specific. So the self-type we use is the abstract
// symbolic Self from the self specific of the interface-with-self.
auto self_specific_args_id = context.specifics().GetArgsOrEmpty(
context.generics().GetSelfSpecific(generic_with_self_id));
auto self_specific_args = context.inst_blocks().Get(self_specific_args_id);
return context.constant_values().Get(self_specific_args.back());
}
// Extended name lookup into a type, like `x.F`, can find a facet
// type extended scope from the type of `x`. The type of `x` maybe a
// facet converted to a type, so drop the `as type` conversion if
// so.
auto canonical_facet_or_type =
GetCanonicalFacetOrTypeValue(context, self_type_const_id);
auto type_of_canonical_facet_or_type =
context.insts()
.Get(context.constant_values().GetInstId(canonical_facet_or_type))
.type_id();
if (type_of_canonical_facet_or_type == SemIR::TypeType::TypeId) {
// If we still have a type, turn it into a facet for use in the
// interface-with-self specific.
return GetConstantFacetValueForType(
context, context.types().GetAsTypeInstId(
context.constant_values().GetInstId(self_type_const_id)));
}
// We have a facet for the self-type (or perhaps an ErrorInst), which we can
// use directly in the interface-with-self specific.
return canonical_facet_or_type;
// If `self_type_const_id` is None, we are doing lookup into a non-lexical
// scope. If it is a `FacetType`, then we are doing lookup directly on a facet
// type, such as `I.F` on an interface `I`.
//
// In these cases, there is no self-type from the lookup for the
// interface-with-self specific. So the self-type we use is the abstract
// symbolic Self from the self specific of the interface-with-self.
auto self_specific_args_id = context.specifics().GetArgsOrEmpty(
context.generics().GetSelfSpecific(generic_with_self_id));
auto self_specific_args = context.inst_blocks().Get(self_specific_args_id);
return context.constant_values().Get(self_specific_args.back());
}
auto AppendLookupScopesForConstant(Context& context, SemIR::LocId loc_id,
@@ -350,15 +324,16 @@ auto AppendLookupScopesForConstant(Context& context, SemIR::LocId loc_id,
llvm::SmallVector<LookupScope>* scopes)
-> bool {
auto lookup_inst_id = context.constant_values().GetInstId(lookup_const_id);
auto lookup = context.insts().Get(lookup_inst_id);
if (auto ns = lookup.TryAs<SemIR::Namespace>()) {
if (auto ns = context.insts().TryGetAs<SemIR::Namespace>(lookup_inst_id)) {
scopes->push_back(LookupScope{.name_scope_id = ns->name_scope_id,
.specific_id = SemIR::SpecificId::None,
.self_const_id = SemIR::ConstantId::None});
return true;
}
if (auto class_ty = lookup.TryAs<SemIR::ClassType>()) {
if (auto class_ty =
context.insts().TryGetAs<SemIR::ClassType>(lookup_inst_id)) {
if (!extended_scope) {
// TODO: Allow name lookup into classes that are being defined even if
// they are not complete.
@@ -378,8 +353,14 @@ auto AppendLookupScopesForConstant(Context& context, SemIR::LocId loc_id,
.self_const_id = self_type_const_id});
return true;
}
// Extended scopes may point to a FacetType.
if (auto facet_type = lookup.TryAs<SemIR::FacetType>()) {
// Extended scopes may point to a FacetType. If it has constraints, collect
// the extended ones as scopes.
auto lookup_type_id =
context.types().TryGetTypeIdForTypeInstId(lookup_inst_id);
if (lookup_type_id.has_value() &&
context.types().IsConstrainedFacetType(lookup_type_id)) {
auto facet_type = context.types().GetAs<SemIR::FacetType>(lookup_type_id);
if (!extended_scope) {
// TODO: Allow name lookup into facet types that are being defined even if
// they are not complete.
@@ -405,7 +386,7 @@ auto AppendLookupScopesForConstant(Context& context, SemIR::LocId loc_id,
}
auto declared_facet_type =
context.declared_facet_types().Get(facet_type->declared_facet_type_id);
context.declared_facet_types().Get(facet_type.declared_facet_type_id);
// Name lookup into "extend" constraints but not "self impls" constraints.
for (const auto& extend : declared_facet_type.extend_constraints) {
auto& interface = context.interfaces().Get(extend.interface_id);
@@ -440,6 +421,7 @@ auto AppendLookupScopesForConstant(Context& context, SemIR::LocId loc_id,
}
return true;
}
if (lookup_const_id == SemIR::ErrorInst::ConstantId) {
// Lookup into this scope should fail without producing an error.
scopes->push_back(LookupScope{.name_scope_id = SemIR::NameScopeId::None,
@@ -447,6 +429,7 @@ auto AppendLookupScopesForConstant(Context& context, SemIR::LocId loc_id,
.self_const_id = SemIR::ConstantId::None});
return true;
}
// TODO: Per the design, if `base_id` is any kind of type, then lookup should
// treat it as a name scope, even if it doesn't have members. For example,
// `(i32*).X` should fail because there's no name `X` in `i32*`, not because
+12 -6
View File
@@ -92,8 +92,9 @@ auto MakeEmptyRegion(Context& context, SemIR::InstId result_id)
}
auto AddBindingEntityName(Context& context, SemIR::NameId name_id,
SemIR::InstId form_id, bool is_unused,
BindingPhase phase) -> SemIR::EntityNameId {
SemIR::TypeInstId type_inst_id, SemIR::InstId form_id,
bool is_unused, BindingPhase phase)
-> SemIR::EntityNameId {
SemIR::EntityName entity_name = {
.name_id = name_id,
.parent_scope_id = context.scope_stack().PeekNameScopeId(),
@@ -104,6 +105,7 @@ auto AddBindingEntityName(Context& context, SemIR::NameId name_id,
entity_name.is_template = phase == BindingPhase::Template;
}
entity_name.form_id = form_id;
entity_name.type_inst_id = type_inst_id;
return context.entity_names().Add(entity_name);
}
@@ -243,10 +245,14 @@ auto AddParamPattern(Context& context, SemIR::LocId loc_id,
}
}();
auto entity_name_id = AddBindingEntityName(context, name_id,
/*form_id=*/SemIR::InstId::None,
/*is_unused=*/false,
/*phase=*/BindingPhase::Runtime);
// This pattern is synthesized rather than written in the source, so there is
// no spelling to record for its type.
auto entity_name_id =
AddBindingEntityName(context, name_id,
/*type_inst_id=*/SemIR::TypeInstId::None,
/*form_id=*/SemIR::InstId::None,
/*is_unused=*/false,
/*phase=*/BindingPhase::Runtime);
auto pattern_type_id = GetPatternType(context, type_id);
if (kind == ParamPatternKind::Var) {
+5 -2
View File
@@ -58,9 +58,12 @@ struct BindingPatternInfo {
enum class BindingPhase { Template, Symbolic, Runtime };
// Creates an entity name for a binding pattern with the given properties.
// `type_inst_id` is the declared type of the binding as written, if known; see
// `SemIR::EntityName::type_inst_id`.
auto AddBindingEntityName(Context& context, SemIR::NameId name_id,
SemIR::InstId form_id, bool is_unused,
BindingPhase phase) -> SemIR::EntityNameId;
SemIR::TypeInstId type_inst_id, SemIR::InstId form_id,
bool is_unused, BindingPhase phase)
-> SemIR::EntityNameId;
// Creates a binding pattern and the associated binding inst, and returns their
// IDs. `scrutinee_type_id` is the type of the binding, and `type_region_id` is
+46 -30
View File
@@ -119,7 +119,9 @@ using State =
class MatchContext {
public:
struct PreWork : Printable<PreWork> {
// `None` when processing the callee side.
// `None` when processing the callee side, or when processing the caller
// side and no value was supplied, in expectation of using a default value
// from the corresponding callee pattern.
SemIR::InstId scrutinee_id;
auto Print(llvm::raw_ostream& out) const -> void {
@@ -936,13 +938,31 @@ auto MatchContext::DoPreWork(State state,
SemIR::DefaultValuePattern default_value_pattern,
SemIR::InstId scrutinee_id, WorkItem entry)
-> void {
if (!std::holds_alternative<CalleeState*>(state)) {
CARBON_FATAL("Unhandled state kind in DefaultValuePattern pre-work");
CARBON_KIND_SWITCH(state) {
case CARBON_KIND(CallerState* _): {
// If there's no scrutinee supplied, supply the default value instead.
if (!scrutinee_id.has_value()) {
const auto& default_value = context_.default_values().Get(
default_value_pattern.default_value_id);
CARBON_CHECK(default_value.value_id.has_value());
auto [inst_id, _] = WrapInstForSpecific(
context_, SemIR::LocId(default_value.value_id),
default_value.value_id, specific_id_stack_.back());
scrutinee_id = inst_id;
}
break;
}
case CARBON_KIND(CalleeState* _): {
// We will need to check the type of the parameter to make sure it
// matches the provided default, so add ourselves to the post-work list.
results_stack_.PushArray();
AddAsPostWork(entry);
break;
}
default: {
CARBON_FATAL("Unhandled state kind in DefaultValuePattern pre-work");
}
}
// We will need to check the type of the parameter to make sure it
// matches the provided default, so add ourselves to the post-work list.
results_stack_.PushArray();
AddAsPostWork(entry);
// Process the subpattern for the default.
AddWork({.pattern_id = default_value_pattern.subpattern_id,
@@ -952,7 +972,7 @@ auto MatchContext::DoPreWork(State state,
auto MatchContext::DoPostWork(State state,
SemIR::DefaultValuePattern default_value_pattern,
WorkItem entry) -> void {
WorkItem /*entry*/) -> void {
if (!std::holds_alternative<CalleeState*>(state)) {
CARBON_FATAL("Unhandled state kind in DefaultValuePattern post-work");
}
@@ -960,30 +980,16 @@ auto MatchContext::DoPostWork(State state,
auto param_inst_id = results_stack_.PeekArray().back();
auto param_type_id = context_.insts().Get(param_inst_id).type_id();
auto default_value_inst_id =
context_.full_pattern_stack()
.GetDefaultValues()[default_value_pattern.default_value_id.index];
// If a constant was specified, we should be able to convert it into the
// type of the parameter.
if (!context_.insts().Is<SemIR::UnspecifiedValue>(default_value_inst_id)) {
// We should be able to convert the supplied constant into the type of
// the parameter.
auto converted_id =
TryConvertToValueOfType(context_, SemIR::LocId(default_value_inst_id),
default_value_inst_id, param_type_id);
if (converted_id == SemIR::ErrorInst::InstId) {
CARBON_DIAGNOSTIC(
PatternDefaultValueTypeMismatch, Error,
"default value expression type {0} doesn't match pattern type {1}",
TypeOfInstId, TypeOfInstId);
// TODO: should be able to provide precise locations for both default
// value expression and the type of the pattern, but we can't because
// they are both constants.
context_.emitter().Emit(entry.pattern_id, PatternDefaultValueTypeMismatch,
default_value_inst_id, param_inst_id);
}
auto& default_value =
context_.default_values().Get(default_value_pattern.default_value_id);
if (!default_value.is_unspecified) {
default_value.value_id =
ConvertToValueOfType(context_, SemIR::LocId(default_value.raw_id),
default_value.raw_id, param_type_id);
}
results_stack_.PopArray();
// If something at a higher level in the stack needed these results, bubble
@@ -1240,9 +1246,19 @@ auto CallerPatternMatch(Context& context, SemIR::SpecificId specific_id,
CARBON_CHECK(self_pattern_id.has_value());
}
for (const auto& [arg_id, param_pattern_id] : llvm::zip_equal(
// `arg_refs` may have a smaller arity than `param_patterns_id` due to the
// possible presence of default values for some parameters. We use
// `zip_longest` here to allow for that size disparity. But we presume we
// always have a parameter pattern, so test that presumption here.
CARBON_CHECK(self_arg_refs.size() + arg_refs.size() <=
context.inst_blocks().GetOrEmpty(param_patterns_id).size());
for (const auto& [maybe_arg_id, maybe_param_pattern_id] : llvm::zip_longest(
llvm::concat<const SemIR::InstId>(self_arg_refs, arg_refs),
context.inst_blocks().GetOrEmpty(param_patterns_id))) {
CARBON_CHECK(maybe_param_pattern_id.has_value());
const auto& param_pattern_id = *maybe_param_pattern_id;
const auto& arg_id =
maybe_arg_id.has_value() ? *maybe_arg_id : SemIR::InstId::None;
match.Match(&state,
{.pattern_id = param_pattern_id,
.work = MatchContext::PreWork{.scrutinee_id = arg_id},
+15 -28
View File
@@ -22,8 +22,7 @@ namespace Carbon::Check {
auto MakePeriodSelfFacetValue(Context& context, SemIR::LocId loc_id,
SemIR::TypeId self_type_id) -> SemIR::InstId {
CARBON_CHECK(self_type_id == SemIR::ErrorInst::TypeId ||
context.types().Is<SemIR::FacetType>(self_type_id));
CARBON_CHECK(context.types().IsFacetTypeOrError(self_type_id));
auto entity_name_id = context.entity_names().AddCanonical(
{.name_id = SemIR::NameId::PeriodSelf,
.parent_scope_id = context.scope_stack().PeekNameScopeId(),
@@ -57,8 +56,7 @@ static auto TryGetAsPeriodSelf(Context& context, SemIR::InstId inst_id,
return std::nullopt;
}
auto query_inst_id =
canonicalize ? GetCanonicalFacetOrTypeValue(context, const_inst_id)
: inst_id;
canonicalize ? GetCanonicalFacet(context, const_inst_id) : inst_id;
if (auto bind =
context.insts().TryGetAs<SemIR::SymbolicBinding>(query_inst_id)) {
const auto& entity_name = context.entity_names().Get(bind->entity_name_id);
@@ -122,7 +120,7 @@ class SubstPeriodSelfCallbacks : public SubstInstCallbacks {
context().constant_values().GetInstId(period_self_replacement_id_);
auto replacement_type_id =
context().insts().Get(replacement_self_inst_id).type_id();
CARBON_CHECK(context().types().IsFacetType(replacement_type_id));
CARBON_CHECK(context().types().Is<SemIR::FacetType>(replacement_type_id));
// If the replacement has the same type as `.Self`, use it directly.
if (replacement_type_id == period_self_type_id) {
@@ -136,42 +134,32 @@ class SubstPeriodSelfCallbacks : public SubstInstCallbacks {
}
// Convert the replacement facet to the type of `.Self`.
cached_replacement_id_ =
ConvertReplacement(replacement_self_inst_id, replacement_type_id,
period_self, period_self_type_id);
cached_replacement_id_ = ConvertReplacement(
replacement_self_inst_id, period_self, period_self_type_id);
cached_replacement_type_id_ = period_self_type_id;
return cached_replacement_id_;
}
auto ConvertReplacement(SemIR::InstId replacement_self_inst_id,
SemIR::TypeId replacement_type_id,
SemIR::InstId period_self_inst_id,
SemIR::TypeId period_self_type_id) -> SemIR::InstId {
// TODO: Replace all empty facet types with TypeType.
if (period_self_type_id == GetEmptyFacetType(context())) {
// Convert to an empty facet type (representing TypeType); we don't need
// any witnesses.
return ConvertToValueOfType(context(), loc_id_, replacement_self_inst_id,
period_self_type_id);
// Ensure the replacement is a type, which we will need for the return or to
// construct FacetValue.
auto replacement_self_type_inst_id = context().types().GetTypeInstId(
GetFacetAccessType(context(), replacement_self_inst_id));
if (period_self_type_id == SemIR::TypeType::TypeId) {
return replacement_self_type_inst_id;
}
// We have a facet or a type, but we need more interfaces in the facet type.
// We will have to synthesize a symbolic witness for each interface.
// We have a replacement facet (converted to `type`), but we need different
// interfaces than we had in the facet's type. We will have to synthesize a
// symbolic witness for each interface.
//
// Why is this okay? The type of `.Self` comes from interfaces that are
// before it (to the left of it) in the facet type. The replacement for
// `.Self` will have to impl those interfaces in order to match the facet
// type, so we know that it is valid to construct these witnesses.
// Make the replacement into a type, which we will need for the FacetValue.
if (context().types().Is<SemIR::FacetType>(replacement_type_id)) {
replacement_self_inst_id = context().constant_values().GetInstId(
EvalOrAddInst<SemIR::FacetAccessType>(
context(), loc_id_,
{.type_id = SemIR::TypeType::TypeId,
.facet_value_inst_id = replacement_self_inst_id}));
}
auto witnesses = MakeWitnessesForPeriodSelfTypeWithoutLookup(
context(), loc_id_,
context().constant_values().Get(replacement_self_inst_id),
@@ -184,8 +172,7 @@ class SubstPeriodSelfCallbacks : public SubstInstCallbacks {
context(), loc_id_,
{
.type_id = period_self_type_id,
.type_inst_id =
context().types().GetAsTypeInstId(replacement_self_inst_id),
.type_inst_id = replacement_self_type_inst_id,
.witnesses_block_id = witnesses.inst_block_id(),
}));
}
+1 -1
View File
@@ -72,7 +72,7 @@ auto SubstPeriodSelfInFacetType(Context& context, SemIR::LocId loc_id,
// Returns whether the constant value of `inst_id` is a reference to `.Self`.
//
// If `canonicalize` is true, look at the constant value of `inst_id` and get
// the canonicalized facet or type to look through FacetAccessType.
// the canonicalized facet to look through FacetAccessType.
auto IsPeriodSelf(Context& context, SemIR::InstId inst_id,
bool canonicalize = true) -> bool;
+4 -4
View File
@@ -143,7 +143,7 @@ auto ScopeStack::Pop(bool check_unused) -> void {
// TODO: Multiple diagnostics on same line has non-deterministic order.
// Add second sort key in diagnostics sorting.
scope.names.ForEach([&, check_unused](SemIR::NameId name_id) {
for (SemIR::NameId name_id : scope.names.entries()) {
auto& lexical_results = lexical_lookup_.Get(name_id);
CARBON_CHECK(lexical_results.back().scope_index == scope.index,
"Inconsistent scope index for name {0}", name_id);
@@ -151,7 +151,7 @@ auto ScopeStack::Pop(bool check_unused) -> void {
CheckUnusedBinding(*context_, name_id, lexical_results.back());
}
lexical_results.pop_back();
});
}
if (!scope.is_lexical_scope()) {
CARBON_CHECK(non_lexical_scope_stack_.back().scope_index == scope.index);
@@ -375,7 +375,7 @@ auto ScopeStack::Suspend() -> SuspendedScope {
result.suspended_items.reserve(result.entry.num_names +
peek_compile_time_bindings.size());
result.entry.names.ForEach([&](SemIR::NameId name_id) {
for (SemIR::NameId name_id : result.entry.names.entries()) {
auto suspended = lexical_lookup_.Suspend(name_id);
CARBON_CHECK(suspended.index !=
SuspendedScope::ScopeItem::IndexForCompileTimeBinding);
@@ -384,7 +384,7 @@ auto ScopeStack::Suspend() -> SuspendedScope {
.inst_id = suspended.inst_id,
.is_decl_reachable = suspended.is_decl_reachable,
.use_loc_id = suspended.use_loc_id});
});
}
CARBON_CHECK(static_cast<int>(result.suspended_items.size()) ==
result.entry.num_names);
+1 -1
View File
@@ -359,7 +359,7 @@ class ScopeStack {
// Names which are registered with lexical_lookup_, and will need to be
// unregistered when the scope ends.
Set<SemIR::NameId> names = {};
Set<SemIR::NameId, 16> names = {};
};
// A scope in which `return` can be used.
+3
View File
@@ -158,6 +158,7 @@ extern alias C = Class;
// CHECK:STDOUT: --- alias.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -174,6 +175,7 @@ extern alias C = Class;
// CHECK:STDOUT: --- alias_of_alias.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %pattern_type: type = pattern_type %C [concrete]
@@ -211,6 +213,7 @@ extern alias C = Class;
// CHECK:STDOUT: --- alias_to_generic.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %A.type: type = generic_class_type @A [concrete]
// CHECK:STDOUT: %A.generic: %A.type = struct_value () [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
+6
View File
@@ -39,6 +39,7 @@ let a_test: bool = a;
// CHECK:STDOUT: --- i32.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: }
@@ -50,6 +51,10 @@ let a_test: bool = a;
// CHECK:STDOUT:
// CHECK:STDOUT: --- bool.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: %.loc5: type = type_literal bool [concrete = bool]
// CHECK:STDOUT: %b: type = alias_binding b, %.loc5 [concrete = bool]
@@ -58,6 +63,7 @@ let a_test: bool = a;
// CHECK:STDOUT: --- bool_value.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %false: bool = bool_literal false [concrete]
// CHECK:STDOUT: %pattern_type: type = pattern_type bool [concrete]
// CHECK:STDOUT: %a_test.patt: %pattern_type = value_binding_pattern a_test [concrete]
+6
View File
@@ -75,6 +75,7 @@ var d: D* = &c;
// CHECK:STDOUT: --- base.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
@@ -110,6 +111,7 @@ var d: D* = &c;
// CHECK:STDOUT: --- export.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
@@ -147,6 +149,7 @@ var d: D* = &c;
// CHECK:STDOUT: --- export_orig.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
@@ -184,6 +187,7 @@ var d: D* = &c;
// CHECK:STDOUT: --- use_export.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
@@ -240,6 +244,7 @@ var d: D* = &c;
// CHECK:STDOUT: --- fail_orig_name_not_in_export.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete]
// CHECK:STDOUT: }
@@ -280,6 +285,7 @@ var d: D* = &c;
// CHECK:STDOUT: --- indirect_compat.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
+6
View File
@@ -68,6 +68,7 @@ var c: () = a_alias_alias;
// CHECK:STDOUT: --- class1.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
@@ -143,6 +144,7 @@ var c: () = a_alias_alias;
// CHECK:STDOUT: --- class2.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
@@ -224,6 +226,7 @@ var c: () = a_alias_alias;
// CHECK:STDOUT: --- class3.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
@@ -300,6 +303,7 @@ var c: () = a_alias_alias;
// CHECK:STDOUT: --- var1.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
// CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete]
@@ -347,6 +351,7 @@ var c: () = a_alias_alias;
// CHECK:STDOUT: --- var2.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete]
// CHECK:STDOUT: %a.patt: %pattern_type = ref_binding_pattern a [concrete]
@@ -402,6 +407,7 @@ var c: () = a_alias_alias;
// CHECK:STDOUT: --- var3.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
// CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete]
+4
View File
@@ -59,6 +59,7 @@ var inst: Test.A = {};
// CHECK:STDOUT: --- def.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
@@ -85,6 +86,7 @@ var inst: Test.A = {};
// CHECK:STDOUT: --- def.impl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
@@ -138,6 +140,7 @@ var inst: Test.A = {};
// CHECK:STDOUT: --- fail_local_def.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete]
// CHECK:STDOUT: }
@@ -172,6 +175,7 @@ var inst: Test.A = {};
// CHECK:STDOUT: --- fail_other_def.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete]
// CHECK:STDOUT: }
+2
View File
@@ -36,6 +36,7 @@ var a_val: a = {.v = b_val.v};
// CHECK:STDOUT: --- a.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
@@ -79,6 +80,7 @@ var a_val: a = {.v = b_val.v};
// CHECK:STDOUT: --- b.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %struct_type.v: type = struct_type {.v: %empty_tuple.type} [concrete]
+1
View File
@@ -49,6 +49,7 @@ fn F() -> {} {
// CHECK:STDOUT: --- in_namespace.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
+2
View File
@@ -46,6 +46,7 @@ fn F() -> () {
// CHECK:STDOUT: --- global.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -64,6 +65,7 @@ fn F() -> () {
// CHECK:STDOUT: --- fail_local.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
+330 -20
View File
@@ -108,6 +108,7 @@ var a: array(1, 1);
// CHECK:STDOUT: --- assign_var.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
// CHECK:STDOUT: %tuple.type: type = tuple_type (%empty_tuple.type, %empty_tuple.type, %empty_tuple.type) [concrete]
@@ -171,11 +172,13 @@ var a: array(1, 1);
// CHECK:STDOUT: --- array_in_place.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %tuple.type.ff9: type = tuple_type (type, type, type) [concrete]
// CHECK:STDOUT: %tuple: %tuple.type.ff9 = tuple_value (%C, %C, %C) [concrete]
// CHECK:STDOUT: %tuple.type.531: type = tuple_type (type, type, type) [concrete]
// CHECK:STDOUT: %tuple: %tuple.type.531 = tuple_value (%C, %C, %C) [concrete]
// CHECK:STDOUT: %tuple.type.a8c: type = tuple_type (%C, %C, %C) [concrete]
// CHECK:STDOUT: %pattern_type.730: type = pattern_type %tuple.type.a8c [concrete]
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
@@ -187,8 +190,93 @@ var a: array(1, 1);
// CHECK:STDOUT: %tuple.type.8c7: type = tuple_type (%tuple.type.a8c, %tuple.type.a8c) [concrete]
// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete]
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete]
// CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete]
// CHECK:STDOUT: %self.param_patt.52f: %pattern_type.a96 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.4b1: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt.52f [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc10_3.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.1: type = fn_type @Destroy.WithSelf.Op.loc10_3.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.1: %Destroy.WithSelf.Op.type.ef016f.1 = struct_value () [concrete]
// CHECK:STDOUT: %pattern_type.98b: type = pattern_type %C [concrete]
// CHECK:STDOUT: %self.param_patt.99a: %pattern_type.98b = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.cbd: %pattern_type.98b = wrapper_binding_pattern self, %self.param_patt.99a [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc10_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.2: type = fn_type @Destroy.WithSelf.Op.loc10_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.2: %Destroy.WithSelf.Op.type.ef016f.2 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.bed: %pattern_type.730 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.d67: %pattern_type.730 = wrapper_binding_pattern self, %self.param_patt.bed [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc10_3.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.3: type = fn_type @Destroy.WithSelf.Op.loc10_3.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.3: %Destroy.WithSelf.Op.type.ef016f.3 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.e5f: %pattern_type.c3e = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.1ad: %pattern_type.c3e = wrapper_binding_pattern self, %self.param_patt.e5f [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.4: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc10_3.4 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.4: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.4 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.4: type = fn_type @Destroy.WithSelf.Op.loc10_3.4 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.4: %Destroy.WithSelf.Op.type.ef016f.4 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.4: type = fn_type @Destroy.WithSelf.SelfDestruct.loc10_3.4 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.4: %Destroy.WithSelf.SelfDestruct.type.fbceb5.4 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc10_3.1 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.a96 = ref_param_pattern [concrete = constants.%self.param_patt.52f]
// CHECK:STDOUT: %self.patt: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.4b1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_struct_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_struct_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.1: %Destroy.WithSelf.Op.type.ef016f.1 = fn_decl @Destroy.WithSelf.Op.loc10_3.1 [concrete = constants.%Destroy.WithSelf.Op.403171.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.a96 = ref_param_pattern [concrete = constants.%self.param_patt.52f]
// CHECK:STDOUT: %self.patt: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.4b1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_struct_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_struct_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc10_3.2 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.98b = ref_param_pattern [concrete = constants.%self.param_patt.99a]
// CHECK:STDOUT: %self.patt: %pattern_type.98b = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.cbd]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %C = ref_param call_param0
// CHECK:STDOUT: %self: ref %C = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.2: %Destroy.WithSelf.Op.type.ef016f.2 = fn_decl @Destroy.WithSelf.Op.loc10_3.2 [concrete = constants.%Destroy.WithSelf.Op.403171.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.98b = ref_param_pattern [concrete = constants.%self.param_patt.99a]
// CHECK:STDOUT: %self.patt: %pattern_type.98b = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.cbd]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %C = ref_param call_param0
// CHECK:STDOUT: %self: ref %C = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc10_3.3 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.730 = ref_param_pattern [concrete = constants.%self.param_patt.bed]
// CHECK:STDOUT: %self.patt: %pattern_type.730 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.d67]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %tuple.type.a8c = ref_param call_param0
// CHECK:STDOUT: %self: ref %tuple.type.a8c = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.3: %Destroy.WithSelf.Op.type.ef016f.3 = fn_decl @Destroy.WithSelf.Op.loc10_3.3 [concrete = constants.%Destroy.WithSelf.Op.403171.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.730 = ref_param_pattern [concrete = constants.%self.param_patt.bed]
// CHECK:STDOUT: %self.patt: %pattern_type.730 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.d67]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %tuple.type.a8c = ref_param call_param0
// CHECK:STDOUT: %self: ref %tuple.type.a8c = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.4: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.4 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc10_3.4 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.4] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.c3e = ref_param_pattern [concrete = constants.%self.param_patt.e5f]
// CHECK:STDOUT: %self.patt: %pattern_type.c3e = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.1ad]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.4: %Destroy.WithSelf.Op.type.ef016f.4 = fn_decl @Destroy.WithSelf.Op.loc10_3.4 [concrete = constants.%Destroy.WithSelf.Op.403171.4] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.c3e = ref_param_pattern [concrete = constants.%self.param_patt.e5f]
// CHECK:STDOUT: %self.patt: %pattern_type.c3e = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.1ad]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @G() {
@@ -214,7 +302,7 @@ var a: array(1, 1);
// CHECK:STDOUT: %C.ref.loc10_24: type = name_ref C, file.%C.decl [concrete = constants.%C]
// CHECK:STDOUT: %C.ref.loc10_27: type = name_ref C, file.%C.decl [concrete = constants.%C]
// CHECK:STDOUT: %C.ref.loc10_30: type = name_ref C, file.%C.decl [concrete = constants.%C]
// CHECK:STDOUT: %.loc10_31.1: %tuple.type.ff9 = tuple_literal (%C.ref.loc10_24, %C.ref.loc10_27, %C.ref.loc10_30) [concrete = constants.%tuple]
// CHECK:STDOUT: %.loc10_31.1: %tuple.type.531 = tuple_literal (%C.ref.loc10_24, %C.ref.loc10_27, %C.ref.loc10_30) [concrete = constants.%tuple]
// CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2]
// CHECK:STDOUT: %.loc10_31.2: type = converted %.loc10_31.1, constants.%tuple.type.a8c [concrete = constants.%tuple.type.a8c]
// CHECK:STDOUT: %array_type: type = array_type %int_2, %.loc10_31.2 [concrete = constants.%array_type]
@@ -224,31 +312,68 @@ var a: array(1, 1);
// CHECK:STDOUT: %v.patt: %pattern_type.c3e = ref_binding_pattern v [concrete = constants.%v.patt]
// CHECK:STDOUT: %v.var_patt: %pattern_type.c3e = var_pattern %v.patt [concrete = constants.%v.var_patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.bound: <bound method> = bound_method %v.var, constants.%Destroy.WithSelf.Op.403171.4
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %Destroy.WithSelf.Op.bound(%v.var)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %v.var, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.4
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%v.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc10_3.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc10_3.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc10_3.2(%self.param: ref %C) {
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc10_3.1(%self.param: ref %empty_struct_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.1(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.1(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc10_3.2(%self.param: ref %C) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc10_3.3(%self.param: ref %tuple.type.a8c) {
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc10_3.2(%self.param: ref %C) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc10_3.2(%self.param: ref %C) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.2(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.2(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc10_3.3(%self.param: ref %tuple.type.a8c) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc10_3.4(%self.param: ref %array_type) {
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc10_3.3(%self.param: ref %tuple.type.a8c) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc10_3.3(%self.param: ref %tuple.type.a8c) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.3(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.3(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc10_3.4(%self.param: ref %array_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc10_3.4(%self.param: ref %array_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc10_3.4(%self.param: ref %array_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.4(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.4(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- array_vs_tuple.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
// CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete]
@@ -269,10 +394,29 @@ var a: array(1, 1);
// CHECK:STDOUT: %b.var_patt: %pattern_type.8c1 = var_pattern %b.patt [concrete]
// CHECK:STDOUT: %DefaultOrUnformed.impl_witness.b33: <witness> = impl_witness imports.%DefaultOrUnformed.impl_witness_table.856, @T.as.DefaultOrUnformed.impl(%tuple.type) [concrete]
// CHECK:STDOUT: %DefaultOrUnformed.facet.f59: %DefaultOrUnformed.type = facet_value %tuple.type, (%DefaultOrUnformed.impl_witness.b33) [concrete]
// CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete]
// CHECK:STDOUT: %self.param_patt.65a: %pattern_type.cb1 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.df1: %pattern_type.cb1 = wrapper_binding_pattern self, %self.param_patt.65a [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc8_3.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.1: type = fn_type @Destroy.WithSelf.Op.loc8_3.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.1: %Destroy.WithSelf.Op.type.ef016f.1 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.327: %pattern_type.8c1 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.570: %pattern_type.8c1 = wrapper_binding_pattern self, %self.param_patt.327 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc8_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.2: type = fn_type @Destroy.WithSelf.Op.loc8_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.2: %Destroy.WithSelf.Op.type.ef016f.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2: type = fn_type @Destroy.WithSelf.SelfDestruct.loc8_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.2: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.279: %pattern_type.035 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.06d: %pattern_type.035 = wrapper_binding_pattern self, %self.param_patt.279 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc7 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.3: type = fn_type @Destroy.WithSelf.Op.loc7 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.3: %Destroy.WithSelf.Op.type.ef016f.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3: type = fn_type @Destroy.WithSelf.SelfDestruct.loc7 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.3: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
@@ -280,6 +424,51 @@ var a: array(1, 1);
// CHECK:STDOUT: %DefaultOrUnformed.impl_witness_table.856 = impl_witness_table (%Core.import_ref.cc8), @T.as.DefaultOrUnformed.impl [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc8_3.1 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.cb1 = ref_param_pattern [concrete = constants.%self.param_patt.65a]
// CHECK:STDOUT: %self.patt: %pattern_type.cb1 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.df1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_tuple.type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_tuple.type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.1: %Destroy.WithSelf.Op.type.ef016f.1 = fn_decl @Destroy.WithSelf.Op.loc8_3.1 [concrete = constants.%Destroy.WithSelf.Op.403171.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.cb1 = ref_param_pattern [concrete = constants.%self.param_patt.65a]
// CHECK:STDOUT: %self.patt: %pattern_type.cb1 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.df1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_tuple.type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_tuple.type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc8_3.2 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.8c1 = ref_param_pattern [concrete = constants.%self.param_patt.327]
// CHECK:STDOUT: %self.patt: %pattern_type.8c1 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.570]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %tuple.type = ref_param call_param0
// CHECK:STDOUT: %self: ref %tuple.type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.2: %Destroy.WithSelf.Op.type.ef016f.2 = fn_decl @Destroy.WithSelf.Op.loc8_3.2 [concrete = constants.%Destroy.WithSelf.Op.403171.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.8c1 = ref_param_pattern [concrete = constants.%self.param_patt.327]
// CHECK:STDOUT: %self.patt: %pattern_type.8c1 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.570]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %tuple.type = ref_param call_param0
// CHECK:STDOUT: %self: ref %tuple.type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc7 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.035 = ref_param_pattern [concrete = constants.%self.param_patt.279]
// CHECK:STDOUT: %self.patt: %pattern_type.035 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.06d]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.3: %Destroy.WithSelf.Op.type.ef016f.3 = fn_decl @Destroy.WithSelf.Op.loc7 [concrete = constants.%Destroy.WithSelf.Op.403171.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.035 = ref_param_pattern [concrete = constants.%self.param_patt.279]
// CHECK:STDOUT: %self.patt: %pattern_type.035 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.06d]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @G() {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %a.var: ref %array_type = var_storage %a.var_patt
@@ -326,31 +515,60 @@ var a: array(1, 1);
// CHECK:STDOUT: %b.patt: %pattern_type.8c1 = ref_binding_pattern b [concrete = constants.%b.patt]
// CHECK:STDOUT: %b.var_patt: %pattern_type.8c1 = var_pattern %b.patt [concrete = constants.%b.var_patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.bound.loc8: <bound method> = bound_method %b.var, constants.%Destroy.WithSelf.Op.403171.2
// CHECK:STDOUT: %Destroy.WithSelf.Op.call.loc8: init %empty_tuple.type = call %Destroy.WithSelf.Op.bound.loc8(%b.var)
// CHECK:STDOUT: %Destroy.WithSelf.Op.bound.loc7: <bound method> = bound_method %a.var, constants.%Destroy.WithSelf.Op.403171.3
// CHECK:STDOUT: %Destroy.WithSelf.Op.call.loc7: init %empty_tuple.type = call %Destroy.WithSelf.Op.bound.loc7(%a.var)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound.loc8: <bound method> = bound_method %b.var, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.2
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call.loc8: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound.loc8(%b.var)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound.loc7: <bound method> = bound_method %a.var, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.3
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call.loc7: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound.loc7(%a.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc8_3.1(%self.param: ref %empty_tuple.type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc8_3.1(%self.param: ref %empty_tuple.type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc8_3.2(%self.param: ref %tuple.type) {
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc8_3.1(%self.param: ref %empty_tuple.type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.1(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.1(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc8_3.2(%self.param: ref %tuple.type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc7(%self.param: ref %array_type) {
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc8_3.2(%self.param: ref %tuple.type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc8_3.2(%self.param: ref %tuple.type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.2(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.2(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc7(%self.param: ref %array_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc7(%self.param: ref %array_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc7(%self.param: ref %array_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.3(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.3(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- assign_return_value.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
// CHECK:STDOUT: %tuple.type: type = tuple_type (%empty_tuple.type) [concrete]
// CHECK:STDOUT: %pattern_type.559: type = pattern_type %tuple.type [concrete]
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete]
@@ -360,10 +578,74 @@ var a: array(1, 1);
// CHECK:STDOUT: %t.var_patt: %pattern_type.fe8 = var_pattern %t.patt [concrete]
// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete]
// CHECK:STDOUT: %array: %array_type = tuple_value (%empty_tuple) [concrete]
// CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete]
// CHECK:STDOUT: %self.param_patt.65a: %pattern_type.cb1 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.df1: %pattern_type.cb1 = wrapper_binding_pattern self, %self.param_patt.65a [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc8_34.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.1: type = fn_type @Destroy.WithSelf.Op.loc8_34.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.1: %Destroy.WithSelf.Op.type.ef016f.1 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.383: %pattern_type.559 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.363: %pattern_type.559 = wrapper_binding_pattern self, %self.param_patt.383 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc8_34.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.2: type = fn_type @Destroy.WithSelf.Op.loc8_34.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.2: %Destroy.WithSelf.Op.type.ef016f.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2: type = fn_type @Destroy.WithSelf.SelfDestruct.loc8_34.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.2: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.f73: %pattern_type.fe8 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.741: %pattern_type.fe8 = wrapper_binding_pattern self, %self.param_patt.f73 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc8_3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.3: type = fn_type @Destroy.WithSelf.Op.loc8_3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.3: %Destroy.WithSelf.Op.type.ef016f.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3: type = fn_type @Destroy.WithSelf.SelfDestruct.loc8_3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.3: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc8_34.1 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.cb1 = ref_param_pattern [concrete = constants.%self.param_patt.65a]
// CHECK:STDOUT: %self.patt: %pattern_type.cb1 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.df1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_tuple.type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_tuple.type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.1: %Destroy.WithSelf.Op.type.ef016f.1 = fn_decl @Destroy.WithSelf.Op.loc8_34.1 [concrete = constants.%Destroy.WithSelf.Op.403171.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.cb1 = ref_param_pattern [concrete = constants.%self.param_patt.65a]
// CHECK:STDOUT: %self.patt: %pattern_type.cb1 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.df1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_tuple.type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_tuple.type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc8_34.2 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.559 = ref_param_pattern [concrete = constants.%self.param_patt.383]
// CHECK:STDOUT: %self.patt: %pattern_type.559 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.363]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %tuple.type = ref_param call_param0
// CHECK:STDOUT: %self: ref %tuple.type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.2: %Destroy.WithSelf.Op.type.ef016f.2 = fn_decl @Destroy.WithSelf.Op.loc8_34.2 [concrete = constants.%Destroy.WithSelf.Op.403171.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.559 = ref_param_pattern [concrete = constants.%self.param_patt.383]
// CHECK:STDOUT: %self.patt: %pattern_type.559 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.363]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %tuple.type = ref_param call_param0
// CHECK:STDOUT: %self: ref %tuple.type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc8_3 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.fe8 = ref_param_pattern [concrete = constants.%self.param_patt.f73]
// CHECK:STDOUT: %self.patt: %pattern_type.fe8 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.741]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.3: %Destroy.WithSelf.Op.type.ef016f.3 = fn_decl @Destroy.WithSelf.Op.loc8_3 [concrete = constants.%Destroy.WithSelf.Op.403171.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.fe8 = ref_param_pattern [concrete = constants.%self.param_patt.f73]
// CHECK:STDOUT: %self.patt: %pattern_type.fe8 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.741]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Run() {
@@ -393,28 +675,56 @@ var a: array(1, 1);
// CHECK:STDOUT: %t.patt: %pattern_type.fe8 = ref_binding_pattern t [concrete = constants.%t.patt]
// CHECK:STDOUT: %t.var_patt: %pattern_type.fe8 = var_pattern %t.patt [concrete = constants.%t.var_patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.bound.loc8_34: <bound method> = bound_method %.loc8_34.2, constants.%Destroy.WithSelf.Op.403171.2
// CHECK:STDOUT: %Destroy.WithSelf.Op.call.loc8_34: init %empty_tuple.type = call %Destroy.WithSelf.Op.bound.loc8_34(%.loc8_34.2)
// CHECK:STDOUT: %Destroy.WithSelf.Op.bound.loc8_3: <bound method> = bound_method %t.var, constants.%Destroy.WithSelf.Op.403171.3
// CHECK:STDOUT: %Destroy.WithSelf.Op.call.loc8_3: init %empty_tuple.type = call %Destroy.WithSelf.Op.bound.loc8_3(%t.var)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound.loc8_34: <bound method> = bound_method %.loc8_34.2, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.2
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call.loc8_34: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound.loc8_34(%.loc8_34.2)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound.loc8_3: <bound method> = bound_method %t.var, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.3
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call.loc8_3: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound.loc8_3(%t.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc8_34.1(%self.param: ref %empty_tuple.type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc8_34.1(%self.param: ref %empty_tuple.type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc8_34.2(%self.param: ref %tuple.type) {
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc8_34.1(%self.param: ref %empty_tuple.type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.1(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.1(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc8_34.2(%self.param: ref %tuple.type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc8_3(%self.param: ref %array_type) {
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc8_34.2(%self.param: ref %tuple.type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc8_34.2(%self.param: ref %tuple.type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.2(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.2(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc8_3(%self.param: ref %array_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc8_3(%self.param: ref %array_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc8_3(%self.param: ref %array_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.3(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.3(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- nine_elements.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
// CHECK:STDOUT: %tuple.type: type = tuple_type (%empty_tuple.type, %empty_tuple.type, %empty_tuple.type, %empty_tuple.type, %empty_tuple.type, %empty_tuple.type, %empty_tuple.type, %empty_tuple.type, %empty_tuple.type) [concrete]
+2
View File
@@ -68,6 +68,7 @@ var b: array(1, 39999999999999999993);
// CHECK:STDOUT: --- addition.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
@@ -112,6 +113,7 @@ var b: array(1, 39999999999999999993);
// CHECK:STDOUT: --- unsigned.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete]
+99 -4
View File
@@ -53,15 +53,18 @@ fn F() -> i32 {
// CHECK:STDOUT: --- user.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %pattern_type.6b6: type = pattern_type %i32 [concrete]
// CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete]
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
// CHECK:STDOUT: %int_42: Core.IntLiteral = int_value 42 [concrete]
// CHECK:STDOUT: %array_type: type = array_type %int_42, %i32 [concrete]
// CHECK:STDOUT: %pattern_type.c07: type = pattern_type %array_type [concrete]
// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.ac8: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.5e0: %Int.as.Copy.impl.Op.type.ac8 = struct_value () [symbolic]
@@ -72,8 +75,27 @@ fn F() -> i32 {
// CHECK:STDOUT: %Copy.WithSelf.Op.type.381: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete]
// CHECK:STDOUT: %.737: type = fn_type_with_self_type %Copy.WithSelf.Op.type.381, %Copy.facet [concrete]
// CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: <specific function> = specific_function %Int.as.Copy.impl.Op.4f6, @Int.as.Copy.impl.Op(%int_32) [concrete]
// CHECK:STDOUT: %pattern_type.956: type = pattern_type %i32.builtin [concrete]
// CHECK:STDOUT: %self.param_patt.331: %pattern_type.956 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.319: %pattern_type.956 = wrapper_binding_pattern self, %self.param_patt.331 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc6_12.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.1: type = fn_type @Destroy.WithSelf.Op.loc6_12.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.1: %Destroy.WithSelf.Op.type.ef016f.1 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.705: %pattern_type.6b6 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.70d: %pattern_type.6b6 = wrapper_binding_pattern self, %self.param_patt.705 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc6_12.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.2: type = fn_type @Destroy.WithSelf.Op.loc6_12.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.2: %Destroy.WithSelf.Op.type.ef016f.2 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.087: %pattern_type.c07 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.b22: %pattern_type.c07 = wrapper_binding_pattern self, %self.param_patt.087 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc6_12.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.3: type = fn_type @Destroy.WithSelf.Op.loc6_12.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.3: %Destroy.WithSelf.Op.type.ef016f.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3: type = fn_type @Destroy.WithSelf.SelfDestruct.loc6_12.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.3: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
@@ -82,6 +104,51 @@ fn F() -> i32 {
// CHECK:STDOUT: %Copy.impl_witness_table.8d2 = impl_witness_table (%Core.import_ref.cd6), @Int.as.Copy.impl [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc6_12.1 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.956 = ref_param_pattern [concrete = constants.%self.param_patt.331]
// CHECK:STDOUT: %self.patt: %pattern_type.956 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.319]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32.builtin = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32.builtin = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.1: %Destroy.WithSelf.Op.type.ef016f.1 = fn_decl @Destroy.WithSelf.Op.loc6_12.1 [concrete = constants.%Destroy.WithSelf.Op.403171.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.956 = ref_param_pattern [concrete = constants.%self.param_patt.331]
// CHECK:STDOUT: %self.patt: %pattern_type.956 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.319]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32.builtin = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32.builtin = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc6_12.2 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6b6 = ref_param_pattern [concrete = constants.%self.param_patt.705]
// CHECK:STDOUT: %self.patt: %pattern_type.6b6 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.70d]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.2: %Destroy.WithSelf.Op.type.ef016f.2 = fn_decl @Destroy.WithSelf.Op.loc6_12.2 [concrete = constants.%Destroy.WithSelf.Op.403171.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6b6 = ref_param_pattern [concrete = constants.%self.param_patt.705]
// CHECK:STDOUT: %self.patt: %pattern_type.6b6 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.70d]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc6_12.3 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.c07 = ref_param_pattern [concrete = constants.%self.param_patt.087]
// CHECK:STDOUT: %self.patt: %pattern_type.c07 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.b22]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.3: %Destroy.WithSelf.Op.type.ef016f.3 = fn_decl @Destroy.WithSelf.Op.loc6_12.3 [concrete = constants.%Destroy.WithSelf.Op.403171.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.c07 = ref_param_pattern [concrete = constants.%self.param_patt.087]
// CHECK:STDOUT: %self.patt: %pattern_type.c07 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.b22]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @G(%n.param: %i32) -> out %return.param: %i32 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%Main.F [concrete = constants.%F]
@@ -96,26 +163,54 @@ fn F() -> i32 {
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn]
// CHECK:STDOUT: %bound_method.loc6_15.2: <bound method> = bound_method %.loc6_15.2, %specific_fn
// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc6_15.2(%.loc6_15.2)
// CHECK:STDOUT: %Destroy.WithSelf.Op.bound: <bound method> = bound_method %.loc6_12.2, constants.%Destroy.WithSelf.Op.403171.3
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %Destroy.WithSelf.Op.bound(%.loc6_12.2)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %.loc6_12.2, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.3
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%.loc6_12.2)
// CHECK:STDOUT: return %Int.as.Copy.impl.Op.call
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc6_12.1(%self.param: ref %i32.builtin) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc6_12.1(%self.param: ref %i32.builtin) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc6_12.2(%self.param: ref %i32) {
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc6_12.1(%self.param: ref %i32.builtin) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.1(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.1(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc6_12.2(%self.param: ref %i32) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc6_12.3(%self.param: ref %array_type) {
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc6_12.2(%self.param: ref %i32) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc6_12.2(%self.param: ref %i32) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.2(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.2(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc6_12.3(%self.param: ref %array_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc6_12.3(%self.param: ref %array_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc6_12.3(%self.param: ref %array_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.3(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.3(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- import_symbolic_decl.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
+99 -4
View File
@@ -49,12 +49,15 @@ fn F(a: array({}, 3)) -> {} {
// CHECK:STDOUT: --- function_param.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %int_3.1ba: Core.IntLiteral = int_value 3 [concrete]
// CHECK:STDOUT: %array_type: type = array_type %int_3.1ba, %i32 [concrete]
// CHECK:STDOUT: %pattern_type.771: type = pattern_type %array_type [concrete]
// CHECK:STDOUT: %pattern_type.6b6: type = pattern_type %i32 [concrete]
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
// CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete]
@@ -95,9 +98,28 @@ fn F(a: array({}, 3)) -> {} {
// CHECK:STDOUT: %int_3.410: %i32 = int_value 3 [concrete]
// CHECK:STDOUT: %array: %array_type = tuple_value (%int_1.0c6, %int_2.295, %int_3.410) [concrete]
// CHECK:STDOUT: %.981: ref %array_type = temporary invalid, %array [concrete]
// CHECK:STDOUT: %pattern_type.956: type = pattern_type %i32.builtin [concrete]
// CHECK:STDOUT: %self.param_patt.331: %pattern_type.956 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.319: %pattern_type.956 = wrapper_binding_pattern self, %self.param_patt.331 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc10_20.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.1: type = fn_type @Destroy.WithSelf.Op.loc10_20.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.1: %Destroy.WithSelf.Op.type.ef016f.1 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.705: %pattern_type.6b6 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.70d: %pattern_type.6b6 = wrapper_binding_pattern self, %self.param_patt.705 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc10_20.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.2: type = fn_type @Destroy.WithSelf.Op.loc10_20.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.2: %Destroy.WithSelf.Op.type.ef016f.2 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.c42: %pattern_type.771 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.3bc: %pattern_type.771 = wrapper_binding_pattern self, %self.param_patt.c42 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc10_20.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.3: type = fn_type @Destroy.WithSelf.Op.loc10_20.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.3: %Destroy.WithSelf.Op.type.ef016f.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.bound: <bound method> = bound_method %.981, %Destroy.WithSelf.Op.403171.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3: type = fn_type @Destroy.WithSelf.SelfDestruct.loc10_20.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.3: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %.981, %Destroy.WithSelf.SelfDestruct.db3fdb.3 [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
@@ -107,6 +129,51 @@ fn F(a: array({}, 3)) -> {} {
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.1aa = impl_witness_table (%Core.import_ref.edf), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc10_20.1 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.956 = ref_param_pattern [concrete = constants.%self.param_patt.331]
// CHECK:STDOUT: %self.patt: %pattern_type.956 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.319]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32.builtin = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32.builtin = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.1: %Destroy.WithSelf.Op.type.ef016f.1 = fn_decl @Destroy.WithSelf.Op.loc10_20.1 [concrete = constants.%Destroy.WithSelf.Op.403171.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.956 = ref_param_pattern [concrete = constants.%self.param_patt.331]
// CHECK:STDOUT: %self.patt: %pattern_type.956 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.319]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32.builtin = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32.builtin = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc10_20.2 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6b6 = ref_param_pattern [concrete = constants.%self.param_patt.705]
// CHECK:STDOUT: %self.patt: %pattern_type.6b6 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.70d]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.2: %Destroy.WithSelf.Op.type.ef016f.2 = fn_decl @Destroy.WithSelf.Op.loc10_20.2 [concrete = constants.%Destroy.WithSelf.Op.403171.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6b6 = ref_param_pattern [concrete = constants.%self.param_patt.705]
// CHECK:STDOUT: %self.patt: %pattern_type.6b6 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.70d]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc10_20.3 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.771 = ref_param_pattern [concrete = constants.%self.param_patt.c42]
// CHECK:STDOUT: %self.patt: %pattern_type.771 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.3bc]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.3: %Destroy.WithSelf.Op.type.ef016f.3 = fn_decl @Destroy.WithSelf.Op.loc10_20.3 [concrete = constants.%Destroy.WithSelf.Op.403171.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.771 = ref_param_pattern [concrete = constants.%self.param_patt.c42]
// CHECK:STDOUT: %self.patt: %pattern_type.771 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.3bc]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F(%arr.param: %array_type, %i.param: %i32) -> out %return.param: %i32 {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %arr.ref: %array_type = name_ref arr, %arr
@@ -170,25 +237,53 @@ fn F(a: array({}, 3)) -> {} {
// CHECK:STDOUT: %.loc10_23.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_23 [concrete = constants.%int_1.0c6]
// CHECK:STDOUT: %.loc10_23.2: %i32 = converted %int_1.loc10_23, %.loc10_23.1 [concrete = constants.%int_1.0c6]
// CHECK:STDOUT: %F.call: init %i32 = call %F.ref(%.loc10_20.15, %.loc10_23.2)
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call constants.%Destroy.WithSelf.Op.bound(constants.%.981)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call constants.%Destroy.WithSelf.SelfDestruct.bound(constants.%.981)
// CHECK:STDOUT: return %F.call
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc10_20.1(%self.param: ref %i32.builtin) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc10_20.1(%self.param: ref %i32.builtin) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc10_20.2(%self.param: ref %i32) {
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc10_20.1(%self.param: ref %i32.builtin) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.1(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.1(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc10_20.2(%self.param: ref %i32) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc10_20.3(%self.param: ref %array_type) {
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc10_20.2(%self.param: ref %i32) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc10_20.2(%self.param: ref %i32) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.2(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.2(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc10_20.3(%self.param: ref %array_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc10_20.3(%self.param: ref %array_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc10_20.3(%self.param: ref %array_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.3(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.3(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- index_non_literal.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete]
// CHECK:STDOUT: %int_3: Core.IntLiteral = int_value 3 [concrete]
+185 -60
View File
@@ -59,25 +59,26 @@ fn H() { G(3); }
// CHECK:STDOUT: --- generic_empty.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %pattern_type.98f: type = pattern_type type [concrete]
// CHECK:STDOUT: %T.patt: %pattern_type.98f = symbolic_binding_pattern T, 0 [symbolic]
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %pattern_type.9a5: type = pattern_type type [concrete]
// CHECK:STDOUT: %T.patt: %pattern_type.9a5 = symbolic_binding_pattern T, 0 [symbolic]
// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete]
// CHECK:STDOUT: %array_type.1b3: type = array_type %int_0, %T [symbolic]
// CHECK:STDOUT: %require_complete: <witness> = require_complete_type %array_type.1b3 [symbolic]
// CHECK:STDOUT: %require_complete.cc5: <witness> = require_complete_type %array_type.1b3 [symbolic]
// CHECK:STDOUT: %pattern_type.bd6: type = pattern_type %array_type.1b3 [symbolic]
// CHECK:STDOUT: %arr.patt.770: %pattern_type.bd6 = ref_binding_pattern arr [symbolic]
// CHECK:STDOUT: %arr.var_patt.5fc: %pattern_type.bd6 = var_pattern %arr.patt.770 [symbolic]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
// CHECK:STDOUT: %array.ca4: %array_type.1b3 = tuple_value () [symbolic]
// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete]
// CHECK:STDOUT: %Destroy.lookup_impl_witness: <witness> = lookup_impl_witness %array_type.1b3, @Destroy [symbolic]
// CHECK:STDOUT: %Destroy.facet.a52: %Destroy.type = facet_value %array_type.1b3, (%Destroy.lookup_impl_witness) [symbolic]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.a67: type = fn_type @Destroy.WithSelf.Op.1, @Destroy.WithSelf(%Destroy.facet.a52) [symbolic]
// CHECK:STDOUT: %.1e5: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.a67, %Destroy.facet.a52 [symbolic]
// CHECK:STDOUT: %impl.elem0: %.1e5 = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic]
// CHECK:STDOUT: %specific_impl_fn: <specific function> = specific_impl_function %impl.elem0, @Destroy.WithSelf.Op.1(%Destroy.facet.a52) [symbolic]
// CHECK:STDOUT: %Destroy.lookup_impl_witness.ac5: <witness> = lookup_impl_witness %array_type.1b3, @Destroy [symbolic]
// CHECK:STDOUT: %Destroy.facet.a52: %Destroy.type = facet_value %array_type.1b3, (%Destroy.lookup_impl_witness.ac5) [symbolic]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.e30: type = fn_type @Destroy.WithSelf.SelfDestruct.1, @Destroy.WithSelf(%Destroy.facet.a52) [symbolic]
// CHECK:STDOUT: %.c54: type = fn_type_with_self_type %Destroy.WithSelf.SelfDestruct.type.e30, %Destroy.facet.a52 [symbolic]
// CHECK:STDOUT: %impl.elem2: %.c54 = impl_witness_access %Destroy.lookup_impl_witness.ac5, element2 [symbolic]
// CHECK:STDOUT: %specific_impl_fn.0cb: <specific function> = specific_impl_function %impl.elem2, @Destroy.WithSelf.SelfDestruct.1(%Destroy.facet.a52) [symbolic]
// CHECK:STDOUT: %C: type = class_type @C [concrete]
// CHECK:STDOUT: %array_type.988: type = array_type %int_0, %C [concrete]
// CHECK:STDOUT: %complete_type.e80: <witness> = complete_type_witness %array_type.988 [concrete]
@@ -85,12 +86,35 @@ fn H() { G(3); }
// CHECK:STDOUT: %arr.patt.c91: %pattern_type.6be = ref_binding_pattern arr [concrete]
// CHECK:STDOUT: %arr.var_patt.9dd: %pattern_type.6be = var_pattern %arr.patt.c91 [concrete]
// CHECK:STDOUT: %array.497: %array_type.988 = tuple_value () [concrete]
// CHECK:STDOUT: %self.param_patt.5c1: %pattern_type.6be = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.1a6: %pattern_type.6be = wrapper_binding_pattern self, %self.param_patt.5c1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfc: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc7 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01: %Destroy.WithSelf.SubobjectDestroy.type.dfc = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef0: type = fn_type @Destroy.WithSelf.Op.loc7 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403: %Destroy.WithSelf.Op.type.ef0 = struct_value () [concrete]
// CHECK:STDOUT: %custom_witness.6c4: <witness> = custom_witness (%Destroy.WithSelf.Op.403), @Destroy [concrete]
// CHECK:STDOUT: %Destroy.facet.a8f: %Destroy.type = facet_value %array_type.988, (%custom_witness.6c4) [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.3eb: type = fn_type @Destroy.WithSelf.Op.1, @Destroy.WithSelf(%Destroy.facet.a8f) [concrete]
// CHECK:STDOUT: %.2c2: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.3eb, %Destroy.facet.a8f [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbc: type = fn_type @Destroy.WithSelf.SelfDestruct.loc7 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3: %Destroy.WithSelf.SelfDestruct.type.fbc = struct_value () [concrete]
// CHECK:STDOUT: %custom_witness.f8f: <witness> = custom_witness (%Destroy.WithSelf.Op.403, %Destroy.WithSelf.SubobjectDestroy.d01, %Destroy.WithSelf.SelfDestruct.db3), @Destroy [concrete]
// CHECK:STDOUT: %Destroy.facet.23c: %Destroy.type = facet_value %array_type.988, (%custom_witness.f8f) [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.180: type = fn_type @Destroy.WithSelf.SelfDestruct.1, @Destroy.WithSelf(%Destroy.facet.23c) [concrete]
// CHECK:STDOUT: %.a26: type = fn_type_with_self_type %Destroy.WithSelf.SelfDestruct.type.180, %Destroy.facet.23c [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl: %Destroy.WithSelf.SubobjectDestroy.type.dfc = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc7 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6be = ref_param_pattern [concrete = constants.%self.param_patt.5c1]
// CHECK:STDOUT: %self.patt: %pattern_type.6be = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.1a6]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type.988 = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type.988 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl: %Destroy.WithSelf.Op.type.ef0 = fn_decl @Destroy.WithSelf.Op.loc7 [concrete = constants.%Destroy.WithSelf.Op.403] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6be = ref_param_pattern [concrete = constants.%self.param_patt.5c1]
// CHECK:STDOUT: %self.patt: %pattern_type.6be = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.1a6]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type.988 = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type.988 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic fn @G(%T.loc4_15.2: type) {
@@ -98,17 +122,17 @@ fn H() { G(3); }
// CHECK:STDOUT:
// CHECK:STDOUT: !definition:
// CHECK:STDOUT: %array_type.loc7_29.2: type = array_type constants.%int_0, %T.loc4_15.1 [symbolic = %array_type.loc7_29.2 (constants.%array_type.1b3)]
// CHECK:STDOUT: %require_complete: <witness> = require_complete_type %array_type.loc7_29.2 [symbolic = %require_complete (constants.%require_complete)]
// CHECK:STDOUT: %require_complete: <witness> = require_complete_type %array_type.loc7_29.2 [symbolic = %require_complete (constants.%require_complete.cc5)]
// CHECK:STDOUT: %pattern_type: type = pattern_type %array_type.loc7_29.2 [symbolic = %pattern_type (constants.%pattern_type.bd6)]
// CHECK:STDOUT: %arr.patt.loc7_17.2: @G.%pattern_type (%pattern_type.bd6) = ref_binding_pattern arr [symbolic = %arr.patt.loc7_17.2 (constants.%arr.patt.770)]
// CHECK:STDOUT: %arr.var_patt.loc7_3.2: @G.%pattern_type (%pattern_type.bd6) = var_pattern %arr.patt.loc7_17.2 [symbolic = %arr.var_patt.loc7_3.2 (constants.%arr.var_patt.5fc)]
// CHECK:STDOUT: %array: @G.%array_type.loc7_29.2 (%array_type.1b3) = tuple_value () [symbolic = %array (constants.%array.ca4)]
// CHECK:STDOUT: %Destroy.lookup_impl_witness: <witness> = lookup_impl_witness %array_type.loc7_29.2, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness)]
// CHECK:STDOUT: %Destroy.lookup_impl_witness: <witness> = lookup_impl_witness %array_type.loc7_29.2, @Destroy [symbolic = %Destroy.lookup_impl_witness (constants.%Destroy.lookup_impl_witness.ac5)]
// CHECK:STDOUT: %Destroy.facet.loc7_3.3: %Destroy.type = facet_value %array_type.loc7_29.2, (%Destroy.lookup_impl_witness) [symbolic = %Destroy.facet.loc7_3.3 (constants.%Destroy.facet.a52)]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type: type = fn_type @Destroy.WithSelf.Op.1, @Destroy.WithSelf(%Destroy.facet.loc7_3.3) [symbolic = %Destroy.WithSelf.Op.type (constants.%Destroy.WithSelf.Op.type.a67)]
// CHECK:STDOUT: %.loc7_3.4: type = fn_type_with_self_type %Destroy.WithSelf.Op.type, %Destroy.facet.loc7_3.3 [symbolic = %.loc7_3.4 (constants.%.1e5)]
// CHECK:STDOUT: %impl.elem0.loc7_3.2: @G.%.loc7_3.4 (%.1e5) = impl_witness_access %Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc7_3.2 (constants.%impl.elem0)]
// CHECK:STDOUT: %specific_impl_fn.loc7_3.2: <specific function> = specific_impl_function %impl.elem0.loc7_3.2, @Destroy.WithSelf.Op.1(%Destroy.facet.loc7_3.3) [symbolic = %specific_impl_fn.loc7_3.2 (constants.%specific_impl_fn)]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type: type = fn_type @Destroy.WithSelf.SelfDestruct.1, @Destroy.WithSelf(%Destroy.facet.loc7_3.3) [symbolic = %Destroy.WithSelf.SelfDestruct.type (constants.%Destroy.WithSelf.SelfDestruct.type.e30)]
// CHECK:STDOUT: %.loc7_3.4: type = fn_type_with_self_type %Destroy.WithSelf.SelfDestruct.type, %Destroy.facet.loc7_3.3 [symbolic = %.loc7_3.4 (constants.%.c54)]
// CHECK:STDOUT: %impl.elem2.loc7_3.2: @G.%.loc7_3.4 (%.c54) = impl_witness_access %Destroy.lookup_impl_witness, element2 [symbolic = %impl.elem2.loc7_3.2 (constants.%impl.elem2)]
// CHECK:STDOUT: %specific_impl_fn.loc7_3.2: <specific function> = specific_impl_function %impl.elem2.loc7_3.2, @Destroy.WithSelf.SelfDestruct.1(%Destroy.facet.loc7_3.3) [symbolic = %specific_impl_fn.loc7_3.2 (constants.%specific_impl_fn.0cb)]
// CHECK:STDOUT:
// CHECK:STDOUT: fn() {
// CHECK:STDOUT: !entry:
@@ -127,21 +151,30 @@ fn H() { G(3); }
// CHECK:STDOUT: %arr.patt.loc7_17.1: @G.%pattern_type (%pattern_type.bd6) = ref_binding_pattern arr [symbolic = %arr.patt.loc7_17.2 (constants.%arr.patt.770)]
// CHECK:STDOUT: %arr.var_patt.loc7_3.1: @G.%pattern_type (%pattern_type.bd6) = var_pattern %arr.patt.loc7_17.1 [symbolic = %arr.var_patt.loc7_3.2 (constants.%arr.var_patt.5fc)]
// CHECK:STDOUT: }
// CHECK:STDOUT: %impl.elem0.loc7_3.1: @G.%.loc7_3.4 (%.1e5) = impl_witness_access constants.%Destroy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc7_3.2 (constants.%impl.elem0)]
// CHECK:STDOUT: %bound_method.loc7_3.1: <bound method> = bound_method %arr.var, %impl.elem0.loc7_3.1
// CHECK:STDOUT: %Destroy.facet.loc7_3.1: %Destroy.type = facet_value constants.%array_type.1b3, (constants.%Destroy.lookup_impl_witness) [symbolic = %Destroy.facet.loc7_3.3 (constants.%Destroy.facet.a52)]
// CHECK:STDOUT: %impl.elem2.loc7_3.1: @G.%.loc7_3.4 (%.c54) = impl_witness_access constants.%Destroy.lookup_impl_witness.ac5, element2 [symbolic = %impl.elem2.loc7_3.2 (constants.%impl.elem2)]
// CHECK:STDOUT: %bound_method.loc7_3.1: <bound method> = bound_method %arr.var, %impl.elem2.loc7_3.1
// CHECK:STDOUT: %Destroy.facet.loc7_3.1: %Destroy.type = facet_value constants.%array_type.1b3, (constants.%Destroy.lookup_impl_witness.ac5) [symbolic = %Destroy.facet.loc7_3.3 (constants.%Destroy.facet.a52)]
// CHECK:STDOUT: %.loc7_3.2: %Destroy.type = converted constants.%array_type.1b3, %Destroy.facet.loc7_3.1 [symbolic = %Destroy.facet.loc7_3.3 (constants.%Destroy.facet.a52)]
// CHECK:STDOUT: %Destroy.facet.loc7_3.2: %Destroy.type = facet_value constants.%array_type.1b3, (constants.%Destroy.lookup_impl_witness) [symbolic = %Destroy.facet.loc7_3.3 (constants.%Destroy.facet.a52)]
// CHECK:STDOUT: %Destroy.facet.loc7_3.2: %Destroy.type = facet_value constants.%array_type.1b3, (constants.%Destroy.lookup_impl_witness.ac5) [symbolic = %Destroy.facet.loc7_3.3 (constants.%Destroy.facet.a52)]
// CHECK:STDOUT: %.loc7_3.3: %Destroy.type = converted constants.%array_type.1b3, %Destroy.facet.loc7_3.2 [symbolic = %Destroy.facet.loc7_3.3 (constants.%Destroy.facet.a52)]
// CHECK:STDOUT: %specific_impl_fn.loc7_3.1: <specific function> = specific_impl_function %impl.elem0.loc7_3.1, @Destroy.WithSelf.Op.1(constants.%Destroy.facet.a52) [symbolic = %specific_impl_fn.loc7_3.2 (constants.%specific_impl_fn)]
// CHECK:STDOUT: %specific_impl_fn.loc7_3.1: <specific function> = specific_impl_function %impl.elem2.loc7_3.1, @Destroy.WithSelf.SelfDestruct.1(constants.%Destroy.facet.a52) [symbolic = %specific_impl_fn.loc7_3.2 (constants.%specific_impl_fn.0cb)]
// CHECK:STDOUT: %bound_method.loc7_3.2: <bound method> = bound_method %arr.var, %specific_impl_fn.loc7_3.1
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %bound_method.loc7_3.2(%arr.var)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %bound_method.loc7_3.2(%arr.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc7(%self.param: ref %array_type.988) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc7(%self.param: ref %array_type.988) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc7(%self.param: ref %array_type.988) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: specific @G(constants.%T) {
// CHECK:STDOUT: %T.patt.loc4_15.2 => constants.%T.patt
// CHECK:STDOUT: %T.loc4_15.1 => constants.%T
@@ -158,17 +191,18 @@ fn H() { G(3); }
// CHECK:STDOUT: %arr.patt.loc7_17.2 => constants.%arr.patt.c91
// CHECK:STDOUT: %arr.var_patt.loc7_3.2 => constants.%arr.var_patt.9dd
// CHECK:STDOUT: %array => constants.%array.497
// CHECK:STDOUT: %Destroy.lookup_impl_witness => constants.%custom_witness.6c4
// CHECK:STDOUT: %Destroy.facet.loc7_3.3 => constants.%Destroy.facet.a8f
// CHECK:STDOUT: %Destroy.WithSelf.Op.type => constants.%Destroy.WithSelf.Op.type.3eb
// CHECK:STDOUT: %.loc7_3.4 => constants.%.2c2
// CHECK:STDOUT: %impl.elem0.loc7_3.2 => constants.%Destroy.WithSelf.Op.403
// CHECK:STDOUT: %specific_impl_fn.loc7_3.2 => constants.%Destroy.WithSelf.Op.403
// CHECK:STDOUT: %Destroy.lookup_impl_witness => constants.%custom_witness.f8f
// CHECK:STDOUT: %Destroy.facet.loc7_3.3 => constants.%Destroy.facet.23c
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type => constants.%Destroy.WithSelf.SelfDestruct.type.180
// CHECK:STDOUT: %.loc7_3.4 => constants.%.a26
// CHECK:STDOUT: %impl.elem2.loc7_3.2 => constants.%Destroy.WithSelf.SelfDestruct.db3
// CHECK:STDOUT: %specific_impl_fn.loc7_3.2 => constants.%Destroy.WithSelf.SelfDestruct.db3
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_init_template_dependent_bound.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
@@ -189,14 +223,14 @@ fn H() { G(3); }
// CHECK:STDOUT: %tuple: %tuple.type = tuple_value (%int_1, %int_2, %int_3.1ba) [concrete]
// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete]
// CHECK:STDOUT: %Self.0e7: %Destroy.type = symbolic_binding Self, 0 [symbolic]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.d3e: type = fn_type @Destroy.WithSelf.Op.1, @Destroy.WithSelf(%Self.0e7) [symbolic]
// CHECK:STDOUT: %Destroy.WithSelf.Op.42b: %Destroy.WithSelf.Op.type.d3e = struct_value () [symbolic]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.4e5: type = fn_type @Destroy.WithSelf.SelfDestruct.1, @Destroy.WithSelf(%Self.0e7) [symbolic]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.4a0: %Destroy.WithSelf.SelfDestruct.type.4e5 = struct_value () [symbolic]
// CHECK:STDOUT: %Destroy.assoc_type: type = assoc_entity_type @Destroy [concrete]
// CHECK:STDOUT: %assoc0.ae8: %Destroy.assoc_type = assoc_entity element0, imports.%Core.import_ref.918 [concrete]
// CHECK:STDOUT: %.0be: type = type_of_inst @G.%.loc11_3.5 [template]
// CHECK:STDOUT: %.db2: %.0be = splice_inst @G.%.loc11_3.5 [template]
// CHECK:STDOUT: %.76e: type = type_of_inst @G.%.loc11_3.8 [template]
// CHECK:STDOUT: %.772: %.76e = splice_inst @G.%.loc11_3.8 [template]
// CHECK:STDOUT: %assoc2: %Destroy.assoc_type = assoc_entity element2, imports.%Core.import_ref.71d [concrete]
// CHECK:STDOUT: %.19c: type = type_of_inst @G.%.loc11_3.5 [template]
// CHECK:STDOUT: %.11f: %.19c = splice_inst @G.%.loc11_3.5 [template]
// CHECK:STDOUT: %.e86: type = type_of_inst @G.%.loc11_3.8 [template]
// CHECK:STDOUT: %.9d4: %.e86 = splice_inst @G.%.loc11_3.8 [template]
// CHECK:STDOUT: %From: Core.IntLiteral = symbolic_binding From, 0 [symbolic]
// CHECK:STDOUT: %ImplicitAs.type.7cb: type = facet_type <@ImplicitAs, @ImplicitAs(Core.IntLiteral)> [concrete]
// CHECK:STDOUT: %Int.as.ImplicitAs.impl.Convert.type.48d: type = fn_type @Int.as.ImplicitAs.impl.Convert, @Int.as.ImplicitAs.impl(%From) [symbolic]
@@ -226,30 +260,94 @@ fn H() { G(3); }
// CHECK:STDOUT: %pattern_type.771: type = pattern_type %array_type.dc7 [concrete]
// CHECK:STDOUT: %arr.patt.316: %pattern_type.771 = ref_binding_pattern arr [concrete]
// CHECK:STDOUT: %arr.var_patt.c63: %pattern_type.771 = var_pattern %arr.patt.316 [concrete]
// CHECK:STDOUT: %pattern_type.956: type = pattern_type %i32.builtin [concrete]
// CHECK:STDOUT: %self.param_patt.331: %pattern_type.956 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.319: %pattern_type.956 = wrapper_binding_pattern self, %self.param_patt.331 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc11_3.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.1: type = fn_type @Destroy.WithSelf.Op.loc11_3.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.1: %Destroy.WithSelf.Op.type.ef016f.1 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.705: %pattern_type.6b6 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.70d: %pattern_type.6b6 = wrapper_binding_pattern self, %self.param_patt.705 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc11_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.2: type = fn_type @Destroy.WithSelf.Op.loc11_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.2: %Destroy.WithSelf.Op.type.ef016f.2 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.c42: %pattern_type.771 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.3bc: %pattern_type.771 = wrapper_binding_pattern self, %self.param_patt.c42 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc11_3.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.3: type = fn_type @Destroy.WithSelf.Op.loc11_3.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.3: %Destroy.WithSelf.Op.type.ef016f.3 = struct_value () [concrete]
// CHECK:STDOUT: %custom_witness.6c4ec3.3: <witness> = custom_witness (%Destroy.WithSelf.Op.403171.3), @Destroy [concrete]
// CHECK:STDOUT: %Destroy.facet.87b: %Destroy.type = facet_value %array_type.dc7, (%custom_witness.6c4ec3.3) [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.d31: type = fn_type @Destroy.WithSelf.Op.1, @Destroy.WithSelf(%Destroy.facet.87b) [concrete]
// CHECK:STDOUT: %.6e0: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.d31, %Destroy.facet.87b [concrete]
// CHECK:STDOUT: %inst.splice_block.c0d: <instruction> = inst_value [concrete] {
// CHECK:STDOUT: %.729: <bound method> = splice_block %bound_method.7de {
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3: type = fn_type @Destroy.WithSelf.SelfDestruct.loc11_3.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.3: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3 = struct_value () [concrete]
// CHECK:STDOUT: %custom_witness.f8f19d.3: <witness> = custom_witness (%Destroy.WithSelf.Op.403171.3, %Destroy.WithSelf.SubobjectDestroy.d01daf.3, %Destroy.WithSelf.SelfDestruct.db3fdb.3), @Destroy [concrete]
// CHECK:STDOUT: %Destroy.facet.149: %Destroy.type = facet_value %array_type.dc7, (%custom_witness.f8f19d.3) [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.95e: type = fn_type @Destroy.WithSelf.SelfDestruct.1, @Destroy.WithSelf(%Destroy.facet.149) [concrete]
// CHECK:STDOUT: %.7f7: type = fn_type_with_self_type %Destroy.WithSelf.SelfDestruct.type.95e, %Destroy.facet.149 [concrete]
// CHECK:STDOUT: %inst.splice_block.11c: <instruction> = inst_value [concrete] {
// CHECK:STDOUT: %.214: <bound method> = splice_block %bound_method.e27 {
// CHECK:STDOUT: %.875: ref %array_type.dc7 = specific_inst @G.%arr.var, @G(%int_3.410)
// CHECK:STDOUT: %impl.elem0.6b3: %.6e0 = impl_witness_access %custom_witness.6c4ec3.3, element0 [concrete = %Destroy.WithSelf.Op.403171.3]
// CHECK:STDOUT: %bound_method.7de: <bound method> = bound_method %.875, %impl.elem0.6b3
// CHECK:STDOUT: %impl.elem2: %.7f7 = impl_witness_access %custom_witness.f8f19d.3, element2 [concrete = %Destroy.WithSelf.SelfDestruct.db3fdb.3]
// CHECK:STDOUT: %bound_method.e27: <bound method> = bound_method %.875, %impl.elem2
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT: %inst.call: <instruction> = inst_value [concrete] {
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %.729(%.875)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %.214(%.875)
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core.import_ref.918: @Destroy.WithSelf.%Destroy.WithSelf.Op.type (%Destroy.WithSelf.Op.type.d3e) = import_ref Core//prelude/parts/destroy, loc{{\d+_\d+}}, loaded [symbolic = @Destroy.WithSelf.%Destroy.WithSelf.Op (constants.%Destroy.WithSelf.Op.42b)]
// CHECK:STDOUT: %Core.import_ref.71d: @Destroy.WithSelf.%Destroy.WithSelf.SelfDestruct.type (%Destroy.WithSelf.SelfDestruct.type.4e5) = import_ref Core//prelude/parts/destroy, loc{{\d+_\d+}}, loaded [symbolic = @Destroy.WithSelf.%Destroy.WithSelf.SelfDestruct (constants.%Destroy.WithSelf.SelfDestruct.4a0)]
// CHECK:STDOUT: %Core.import_ref.32e: @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert.type (%Int.as.ImplicitAs.impl.Convert.type.48d) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.ImplicitAs.impl.%Int.as.ImplicitAs.impl.Convert (constants.%Int.as.ImplicitAs.impl.Convert.0f9)]
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.204 = impl_witness_table (%Core.import_ref.32e), @Int.as.ImplicitAs.impl [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc11_3.1 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.956 = ref_param_pattern [concrete = constants.%self.param_patt.331]
// CHECK:STDOUT: %self.patt: %pattern_type.956 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.319]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32.builtin = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32.builtin = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.1: %Destroy.WithSelf.Op.type.ef016f.1 = fn_decl @Destroy.WithSelf.Op.loc11_3.1 [concrete = constants.%Destroy.WithSelf.Op.403171.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.956 = ref_param_pattern [concrete = constants.%self.param_patt.331]
// CHECK:STDOUT: %self.patt: %pattern_type.956 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.319]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32.builtin = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32.builtin = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc11_3.2 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6b6 = ref_param_pattern [concrete = constants.%self.param_patt.705]
// CHECK:STDOUT: %self.patt: %pattern_type.6b6 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.70d]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.2: %Destroy.WithSelf.Op.type.ef016f.2 = fn_decl @Destroy.WithSelf.Op.loc11_3.2 [concrete = constants.%Destroy.WithSelf.Op.403171.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6b6 = ref_param_pattern [concrete = constants.%self.param_patt.705]
// CHECK:STDOUT: %self.patt: %pattern_type.6b6 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.70d]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0
// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc11_3.3 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.771 = ref_param_pattern [concrete = constants.%self.param_patt.c42]
// CHECK:STDOUT: %self.patt: %pattern_type.771 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.3bc]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type.dc7 = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type.dc7 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.3: %Destroy.WithSelf.Op.type.ef016f.3 = fn_decl @Destroy.WithSelf.Op.loc11_3.3 [concrete = constants.%Destroy.WithSelf.Op.403171.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.771 = ref_param_pattern [concrete = constants.%self.param_patt.c42]
// CHECK:STDOUT: %self.patt: %pattern_type.771 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.3bc]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %array_type.dc7 = ref_param call_param0
// CHECK:STDOUT: %self: ref %array_type.dc7 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic fn @G(%N.loc5_16.2: %i32) {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT:
@@ -261,12 +359,12 @@ fn H() { G(3); }
// CHECK:STDOUT: %pattern_type: type = pattern_type %array_type.loc11_31.2 [template = %pattern_type (constants.%pattern_type.ed3)]
// CHECK:STDOUT: %arr.patt.loc11_17.2: @G.%pattern_type (%pattern_type.ed3) = ref_binding_pattern arr [template = %arr.patt.loc11_17.2 (constants.%arr.patt.515)]
// CHECK:STDOUT: %arr.var_patt.loc11_3.2: @G.%pattern_type (%pattern_type.ed3) = var_pattern %arr.patt.loc11_17.2 [template = %arr.var_patt.loc11_3.2 (constants.%arr.var_patt.821)]
// CHECK:STDOUT: %.loc11_3.5: <instruction> = compound_member_access_action %arr.var, constants.%assoc0.ae8 [template]
// CHECK:STDOUT: %.loc11_3.6: type = type_of_inst %.loc11_3.5 [template = %.loc11_3.6 (constants.%.0be)]
// CHECK:STDOUT: %.loc11_3.7: @G.%.loc11_3.6 (%.0be) = splice_inst %.loc11_3.5 [template = %.loc11_3.7 (constants.%.db2)]
// CHECK:STDOUT: %.loc11_3.5: <instruction> = compound_member_access_action %arr.var, constants.%assoc2 [template]
// CHECK:STDOUT: %.loc11_3.6: type = type_of_inst %.loc11_3.5 [template = %.loc11_3.6 (constants.%.19c)]
// CHECK:STDOUT: %.loc11_3.7: @G.%.loc11_3.6 (%.19c) = splice_inst %.loc11_3.5 [template = %.loc11_3.7 (constants.%.11f)]
// CHECK:STDOUT: %.loc11_3.8: <instruction> = call_action (%.loc11_3.2), true [template]
// CHECK:STDOUT: %.loc11_3.9: type = type_of_inst %.loc11_3.8 [template = %.loc11_3.9 (constants.%.76e)]
// CHECK:STDOUT: %.loc11_3.10: @G.%.loc11_3.9 (%.76e) = splice_inst %.loc11_3.8 [template = %.loc11_3.10 (constants.%.772)]
// CHECK:STDOUT: %.loc11_3.9: type = type_of_inst %.loc11_3.8 [template = %.loc11_3.9 (constants.%.e86)]
// CHECK:STDOUT: %.loc11_3.10: @G.%.loc11_3.9 (%.e86) = splice_inst %.loc11_3.8 [template = %.loc11_3.10 (constants.%.9d4)]
// CHECK:STDOUT:
// CHECK:STDOUT: fn() {
// CHECK:STDOUT: !entry:
@@ -287,23 +385,50 @@ fn H() { G(3); }
// CHECK:STDOUT: %arr.patt.loc11_17.1: @G.%pattern_type (%pattern_type.ed3) = ref_binding_pattern arr [template = %arr.patt.loc11_17.2 (constants.%arr.patt.515)]
// CHECK:STDOUT: %arr.var_patt.loc11_3.1: @G.%pattern_type (%pattern_type.ed3) = var_pattern %arr.patt.loc11_17.1 [template = %arr.var_patt.loc11_3.2 (constants.%arr.var_patt.821)]
// CHECK:STDOUT: }
// CHECK:STDOUT: %.loc11_3.1: type = type_of_inst %.loc11_3.5 [template = %.loc11_3.6 (constants.%.0be)]
// CHECK:STDOUT: %.loc11_3.2: @G.%.loc11_3.6 (%.0be) = splice_inst %.loc11_3.5 [template = %.loc11_3.7 (constants.%.db2)]
// CHECK:STDOUT: %.loc11_3.3: type = type_of_inst %.loc11_3.8 [template = %.loc11_3.9 (constants.%.76e)]
// CHECK:STDOUT: %.loc11_3.4: @G.%.loc11_3.9 (%.76e) = splice_inst %.loc11_3.8 [template = %.loc11_3.10 (constants.%.772)]
// CHECK:STDOUT: %.loc11_3.1: type = type_of_inst %.loc11_3.5 [template = %.loc11_3.6 (constants.%.19c)]
// CHECK:STDOUT: %.loc11_3.2: @G.%.loc11_3.6 (%.19c) = splice_inst %.loc11_3.5 [template = %.loc11_3.7 (constants.%.11f)]
// CHECK:STDOUT: %.loc11_3.3: type = type_of_inst %.loc11_3.8 [template = %.loc11_3.9 (constants.%.e86)]
// CHECK:STDOUT: %.loc11_3.4: @G.%.loc11_3.9 (%.e86) = splice_inst %.loc11_3.8 [template = %.loc11_3.10 (constants.%.9d4)]
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc11_3.1(%self.param: ref %i32.builtin) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc11_3.1(%self.param: ref %i32.builtin) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc11_3.2(%self.param: ref %i32) {
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc11_3.1(%self.param: ref %i32.builtin) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.1(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.1(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc11_3.2(%self.param: ref %i32) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc11_3.3(%self.param: ref %array_type.dc7) {
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc11_3.2(%self.param: ref %i32) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc11_3.2(%self.param: ref %i32) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.2(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.2(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc11_3.3(%self.param: ref %array_type.dc7) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc11_3.3(%self.param: ref %array_type.dc7) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc11_3.3(%self.param: ref %array_type.dc7) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.3(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.3(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -324,7 +449,7 @@ fn H() { G(3); }
// CHECK:STDOUT: %pattern_type => constants.%pattern_type.771
// CHECK:STDOUT: %arr.patt.loc11_17.2 => constants.%arr.patt.316
// CHECK:STDOUT: %arr.var_patt.loc11_3.2 => constants.%arr.var_patt.c63
// CHECK:STDOUT: %.loc11_3.5 => constants.%inst.splice_block.c0d
// CHECK:STDOUT: %.loc11_3.5 => constants.%inst.splice_block.11c
// CHECK:STDOUT: %.loc11_3.6 => <bound method>
// CHECK:STDOUT: %.loc11_3.7 => invalid
// CHECK:STDOUT: %.loc11_3.8 => constants.%inst.call
+8 -3
View File
@@ -173,6 +173,7 @@ var b: B = {.x = ()} as B;
// CHECK:STDOUT: --- adapt_class.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %A: type = class_type @A [concrete]
// CHECK:STDOUT: %A.Make.type: type = fn_type @A.Make [concrete]
// CHECK:STDOUT: %A.Make: %A.Make.type = struct_value () [concrete]
@@ -237,6 +238,7 @@ var b: B = {.x = ()} as B;
// CHECK:STDOUT: --- adapt_i32.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %A: type = class_type @A [concrete]
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
@@ -304,6 +306,7 @@ var b: B = {.x = ()} as B;
// CHECK:STDOUT: --- multi_level_adapt.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete]
// CHECK:STDOUT: %D: type = class_type @D [concrete]
@@ -333,6 +336,7 @@ var b: B = {.x = ()} as B;
// CHECK:STDOUT: --- init_class_value.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %A: type = class_type @A [concrete]
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
@@ -414,12 +418,13 @@ var b: B = {.x = ()} as B;
// CHECK:STDOUT: --- init_tuple_value.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %Noncopyable: type = class_type @Noncopyable [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %A: type = class_type @A [concrete]
// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete]
// CHECK:STDOUT: %tuple.type.c8c: type = tuple_type (%empty_struct_type, type) [concrete]
// CHECK:STDOUT: %tuple: %tuple.type.c8c = tuple_value (%empty_struct, %Noncopyable) [concrete]
// CHECK:STDOUT: %tuple.type.12a: type = tuple_type (%empty_struct_type, type) [concrete]
// CHECK:STDOUT: %tuple: %tuple.type.12a = tuple_value (%empty_struct, %Noncopyable) [concrete]
// CHECK:STDOUT: %tuple.type.c68: type = tuple_type (%empty_struct_type, %Noncopyable) [concrete]
// CHECK:STDOUT: %pattern_type: type = pattern_type %A [concrete]
// CHECK:STDOUT: %a_value.patt: %pattern_type = value_binding_pattern a_value [concrete]
@@ -430,7 +435,7 @@ var b: B = {.x = ()} as B;
// CHECK:STDOUT: %a.ref: %A = name_ref a, %a
// CHECK:STDOUT: %.loc14_35: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct]
// CHECK:STDOUT: %Noncopyable.ref: type = name_ref Noncopyable, file.%Noncopyable.decl [concrete = constants.%Noncopyable]
// CHECK:STDOUT: %.loc14_49.1: %tuple.type.c8c = tuple_literal (%.loc14_35, %Noncopyable.ref) [concrete = constants.%tuple]
// CHECK:STDOUT: %.loc14_49.1: %tuple.type.12a = tuple_literal (%.loc14_35, %Noncopyable.ref) [concrete = constants.%tuple]
// CHECK:STDOUT: %.loc14_49.2: type = converted constants.%empty_struct, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
// CHECK:STDOUT: %.loc14_49.3: type = converted %.loc14_49.1, constants.%tuple.type.c68 [concrete = constants.%tuple.type.c68]
// CHECK:STDOUT: %.loc14_30.1: %tuple.type.c68 = as_compatible %a.ref
+176 -17
View File
@@ -131,6 +131,7 @@ let n: {.x: ()} = {.x = ()} as {.x = ()};
// CHECK:STDOUT: --- simple_as.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
// CHECK:STDOUT: %struct_type.x.y: type = struct_type {.x: %empty_tuple.type, .y: %empty_tuple.type} [concrete]
@@ -173,6 +174,7 @@ let n: {.x: ()} = {.x = ()} as {.x = ()};
// CHECK:STDOUT: --- as_type.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete]
// CHECK:STDOUT: %t.patt: %pattern_type = value_binding_pattern t [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
@@ -204,22 +206,88 @@ let n: {.x: ()} = {.x = ()} as {.x = ()};
// CHECK:STDOUT: --- as_tuple.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %X: type = class_type @X [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %pattern_type.6cb: type = pattern_type %X [concrete]
// CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete]
// CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete]
// CHECK:STDOUT: %tuple: %tuple.type.24b = tuple_value (%X, %X) [concrete]
// CHECK:STDOUT: %tuple.type.693: type = tuple_type (type, type) [concrete]
// CHECK:STDOUT: %tuple: %tuple.type.693 = tuple_value (%X, %X) [concrete]
// CHECK:STDOUT: %tuple.type.359: type = tuple_type (%X, %X) [concrete]
// CHECK:STDOUT: %pattern_type.3ee: type = pattern_type %tuple.type.359 [concrete]
// CHECK:STDOUT: %a.patt: %pattern_type.3ee = value_binding_pattern a [concrete]
// CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete]
// CHECK:STDOUT: %self.param_patt.52f: %pattern_type.a96 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.4b1: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt.52f [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc13_40.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.1: type = fn_type @Destroy.WithSelf.Op.loc13_40.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.1: %Destroy.WithSelf.Op.type.ef016f.1 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.925: %pattern_type.6cb = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.617: %pattern_type.6cb = wrapper_binding_pattern self, %self.param_patt.925 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc13_40.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.2: type = fn_type @Destroy.WithSelf.Op.loc13_40.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.2: %Destroy.WithSelf.Op.type.ef016f.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2: type = fn_type @Destroy.WithSelf.SelfDestruct.loc13_40.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.2: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2 = struct_value () [concrete]
// CHECK:STDOUT: %b.patt: %pattern_type.3ee = ref_binding_pattern b [concrete]
// CHECK:STDOUT: %b.var_patt: %pattern_type.3ee = var_pattern %b.patt [concrete]
// CHECK:STDOUT: %self.param_patt.bc5: %pattern_type.3ee = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.15e: %pattern_type.3ee = wrapper_binding_pattern self, %self.param_patt.bc5 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc20 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.3: type = fn_type @Destroy.WithSelf.Op.loc20 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.3: %Destroy.WithSelf.Op.type.ef016f.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3: type = fn_type @Destroy.WithSelf.SelfDestruct.loc20 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.3: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc13_40.1 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.a96 = ref_param_pattern [concrete = constants.%self.param_patt.52f]
// CHECK:STDOUT: %self.patt: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.4b1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_struct_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_struct_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.1: %Destroy.WithSelf.Op.type.ef016f.1 = fn_decl @Destroy.WithSelf.Op.loc13_40.1 [concrete = constants.%Destroy.WithSelf.Op.403171.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.a96 = ref_param_pattern [concrete = constants.%self.param_patt.52f]
// CHECK:STDOUT: %self.patt: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.4b1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_struct_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_struct_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc13_40.2 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6cb = ref_param_pattern [concrete = constants.%self.param_patt.925]
// CHECK:STDOUT: %self.patt: %pattern_type.6cb = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.617]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %X = ref_param call_param0
// CHECK:STDOUT: %self: ref %X = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.2: %Destroy.WithSelf.Op.type.ef016f.2 = fn_decl @Destroy.WithSelf.Op.loc13_40.2 [concrete = constants.%Destroy.WithSelf.Op.403171.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6cb = ref_param_pattern [concrete = constants.%self.param_patt.925]
// CHECK:STDOUT: %self.patt: %pattern_type.6cb = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.617]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %X = ref_param call_param0
// CHECK:STDOUT: %self: ref %X = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc20 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.3ee = ref_param_pattern [concrete = constants.%self.param_patt.bc5]
// CHECK:STDOUT: %self.patt: %pattern_type.3ee = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.15e]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %tuple.type.359 = ref_param call_param0
// CHECK:STDOUT: %self: ref %tuple.type.359 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.3: %Destroy.WithSelf.Op.type.ef016f.3 = fn_decl @Destroy.WithSelf.Op.loc20 [concrete = constants.%Destroy.WithSelf.Op.403171.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.3ee = ref_param_pattern [concrete = constants.%self.param_patt.bc5]
// CHECK:STDOUT: %self.patt: %pattern_type.3ee = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.15e]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %tuple.type.359 = ref_param call_param0
// CHECK:STDOUT: %self: ref %tuple.type.359 = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Let() {
@@ -233,7 +301,7 @@ let n: {.x: ()} = {.x = ()} as {.x = ()};
// CHECK:STDOUT: %.loc13_41.1: %tuple.type.359 = tuple_literal (%Make.call.loc13_32, %Make.call.loc13_40)
// CHECK:STDOUT: %X.ref.loc13_47: type = name_ref X, file.%X.decl [concrete = constants.%X]
// CHECK:STDOUT: %X.ref.loc13_50: type = name_ref X, file.%X.decl [concrete = constants.%X]
// CHECK:STDOUT: %.loc13_51.1: %tuple.type.24b = tuple_literal (%X.ref.loc13_47, %X.ref.loc13_50) [concrete = constants.%tuple]
// CHECK:STDOUT: %.loc13_51.1: %tuple.type.693 = tuple_literal (%X.ref.loc13_47, %X.ref.loc13_50) [concrete = constants.%tuple]
// CHECK:STDOUT: %.loc13_51.2: type = converted %.loc13_51.1, constants.%tuple.type.359 [concrete = constants.%tuple.type.359]
// CHECK:STDOUT: %.loc13_32.2: ref %X = temporary %.loc13_32.1, %Make.call.loc13_32
// CHECK:STDOUT: %.loc13_32.3: %X = acquire_value %.loc13_32.2
@@ -244,24 +312,42 @@ let n: {.x: ()} = {.x = ()} as {.x = ()};
// CHECK:STDOUT: %.loc13_22.1: type = splice_block %.loc13_22.3 [concrete = constants.%tuple.type.359] {
// CHECK:STDOUT: %X.ref.loc13_18: type = name_ref X, file.%X.decl [concrete = constants.%X]
// CHECK:STDOUT: %X.ref.loc13_21: type = name_ref X, file.%X.decl [concrete = constants.%X]
// CHECK:STDOUT: %.loc13_22.2: %tuple.type.24b = tuple_literal (%X.ref.loc13_18, %X.ref.loc13_21) [concrete = constants.%tuple]
// CHECK:STDOUT: %.loc13_22.2: %tuple.type.693 = tuple_literal (%X.ref.loc13_18, %X.ref.loc13_21) [concrete = constants.%tuple]
// CHECK:STDOUT: %.loc13_22.3: type = converted %.loc13_22.2, constants.%tuple.type.359 [concrete = constants.%tuple.type.359]
// CHECK:STDOUT: }
// CHECK:STDOUT: %a: %tuple.type.359 = wrapper_binding a, %.loc13_41.2
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %a.patt: %pattern_type.3ee = value_binding_pattern a [concrete = constants.%a.patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.bound.loc13_40: <bound method> = bound_method %.loc13_40.2, constants.%Destroy.WithSelf.Op.403171.2
// CHECK:STDOUT: %Destroy.WithSelf.Op.call.loc13_40: init %empty_tuple.type = call %Destroy.WithSelf.Op.bound.loc13_40(%.loc13_40.2)
// CHECK:STDOUT: %Destroy.WithSelf.Op.bound.loc13_32: <bound method> = bound_method %.loc13_32.2, constants.%Destroy.WithSelf.Op.403171.2
// CHECK:STDOUT: %Destroy.WithSelf.Op.call.loc13_32: init %empty_tuple.type = call %Destroy.WithSelf.Op.bound.loc13_32(%.loc13_32.2)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound.loc13_40: <bound method> = bound_method %.loc13_40.2, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.2
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call.loc13_40: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound.loc13_40(%.loc13_40.2)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound.loc13_32: <bound method> = bound_method %.loc13_32.2, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.2
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call.loc13_32: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound.loc13_32(%.loc13_32.2)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc13_40.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc13_40.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc13_40.2(%self.param: ref %X) {
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc13_40.1(%self.param: ref %empty_struct_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.1(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.1(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc13_40.2(%self.param: ref %X) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc13_40.2(%self.param: ref %X) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc13_40.2(%self.param: ref %X) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.2(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.2(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -277,7 +363,7 @@ let n: {.x: ()} = {.x = ()} as {.x = ()};
// CHECK:STDOUT: %.loc20_41.1: %tuple.type.359 = tuple_literal (%Make.call.loc20_32, %Make.call.loc20_40)
// CHECK:STDOUT: %X.ref.loc20_47: type = name_ref X, file.%X.decl [concrete = constants.%X]
// CHECK:STDOUT: %X.ref.loc20_50: type = name_ref X, file.%X.decl [concrete = constants.%X]
// CHECK:STDOUT: %.loc20_51.1: %tuple.type.24b = tuple_literal (%X.ref.loc20_47, %X.ref.loc20_50) [concrete = constants.%tuple]
// CHECK:STDOUT: %.loc20_51.1: %tuple.type.693 = tuple_literal (%X.ref.loc20_47, %X.ref.loc20_50) [concrete = constants.%tuple]
// CHECK:STDOUT: %.loc20_51.2: type = converted %.loc20_51.1, constants.%tuple.type.359 [concrete = constants.%tuple.type.359]
// CHECK:STDOUT: %.loc20_41.2: init %tuple.type.359 to %b.var = tuple_init (%Make.call.loc20_32, %Make.call.loc20_40)
// CHECK:STDOUT: %.loc20_3: init %tuple.type.359 = converted %.loc20_41.1, %.loc20_41.2
@@ -285,7 +371,7 @@ let n: {.x: ()} = {.x = ()} as {.x = ()};
// CHECK:STDOUT: %.loc20_22.1: type = splice_block %.loc20_22.3 [concrete = constants.%tuple.type.359] {
// CHECK:STDOUT: %X.ref.loc20_18: type = name_ref X, file.%X.decl [concrete = constants.%X]
// CHECK:STDOUT: %X.ref.loc20_21: type = name_ref X, file.%X.decl [concrete = constants.%X]
// CHECK:STDOUT: %.loc20_22.2: %tuple.type.24b = tuple_literal (%X.ref.loc20_18, %X.ref.loc20_21) [concrete = constants.%tuple]
// CHECK:STDOUT: %.loc20_22.2: %tuple.type.693 = tuple_literal (%X.ref.loc20_18, %X.ref.loc20_21) [concrete = constants.%tuple]
// CHECK:STDOUT: %.loc20_22.3: type = converted %.loc20_22.2, constants.%tuple.type.359 [concrete = constants.%tuple.type.359]
// CHECK:STDOUT: }
// CHECK:STDOUT: %b: ref %tuple.type.359 = wrapper_binding b, %b.var
@@ -293,19 +379,29 @@ let n: {.x: ()} = {.x = ()} as {.x = ()};
// CHECK:STDOUT: %b.patt: %pattern_type.3ee = ref_binding_pattern b [concrete = constants.%b.patt]
// CHECK:STDOUT: %b.var_patt: %pattern_type.3ee = var_pattern %b.patt [concrete = constants.%b.var_patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.bound: <bound method> = bound_method %b.var, constants.%Destroy.WithSelf.Op.403171.3
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %Destroy.WithSelf.Op.bound(%b.var)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %b.var, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.3
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%b.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc20(%self.param: ref %tuple.type.359) {
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc20(%self.param: ref %tuple.type.359) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc20(%self.param: ref %tuple.type.359) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc20(%self.param: ref %tuple.type.359) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.3(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.3(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- identity.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %X: type = class_type @X [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %pattern_type.6cb: type = pattern_type %X [concrete]
@@ -318,8 +414,52 @@ let n: {.x: ()} = {.x = ()} as {.x = ()};
// CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete]
// CHECK:STDOUT: %x.patt: %pattern_type.6cb = ref_binding_pattern x [concrete]
// CHECK:STDOUT: %x.var_patt: %pattern_type.6cb = var_pattern %x.patt [concrete]
// CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete]
// CHECK:STDOUT: %self.param_patt.52f: %pattern_type.a96 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.4b1: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt.52f [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc24_3.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.1: type = fn_type @Destroy.WithSelf.Op.loc24_3.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.1: %Destroy.WithSelf.Op.type.ef016f.1 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.925: %pattern_type.6cb = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.617: %pattern_type.6cb = wrapper_binding_pattern self, %self.param_patt.925 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc24_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.2: type = fn_type @Destroy.WithSelf.Op.loc24_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.2: %Destroy.WithSelf.Op.type.ef016f.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2: type = fn_type @Destroy.WithSelf.SelfDestruct.loc24_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.2: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc24_3.1 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.a96 = ref_param_pattern [concrete = constants.%self.param_patt.52f]
// CHECK:STDOUT: %self.patt: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.4b1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_struct_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_struct_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.1: %Destroy.WithSelf.Op.type.ef016f.1 = fn_decl @Destroy.WithSelf.Op.loc24_3.1 [concrete = constants.%Destroy.WithSelf.Op.403171.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.a96 = ref_param_pattern [concrete = constants.%self.param_patt.52f]
// CHECK:STDOUT: %self.patt: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.4b1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_struct_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_struct_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc24_3.2 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6cb = ref_param_pattern [concrete = constants.%self.param_patt.925]
// CHECK:STDOUT: %self.patt: %pattern_type.6cb = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.617]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %X = ref_param call_param0
// CHECK:STDOUT: %self: ref %X = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.2: %Destroy.WithSelf.Op.type.ef016f.2 = fn_decl @Destroy.WithSelf.Op.loc24_3.2 [concrete = constants.%Destroy.WithSelf.Op.403171.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6cb = ref_param_pattern [concrete = constants.%self.param_patt.925]
// CHECK:STDOUT: %self.patt: %pattern_type.6cb = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.617]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %X = ref_param call_param0
// CHECK:STDOUT: %self: ref %X = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Value(%n.param: %X) {
@@ -365,21 +505,40 @@ let n: {.x: ()} = {.x = ()} as {.x = ()};
// CHECK:STDOUT: %x.patt: %pattern_type.6cb = ref_binding_pattern x [concrete = constants.%x.patt]
// CHECK:STDOUT: %x.var_patt: %pattern_type.6cb = var_pattern %x.patt [concrete = constants.%x.var_patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.bound: <bound method> = bound_method %x.var, constants.%Destroy.WithSelf.Op.403171.2
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %Destroy.WithSelf.Op.bound(%x.var)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %x.var, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.2
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%x.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc24_3.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc24_3.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc24_3.2(%self.param: ref %X) {
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc24_3.1(%self.param: ref %empty_struct_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.1(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.1(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc24_3.2(%self.param: ref %X) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc24_3.2(%self.param: ref %X) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc24_3.2(%self.param: ref %X) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.2(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.2(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- overloaded.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %X: type = class_type @X [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete]
+164 -7
View File
@@ -95,8 +95,10 @@ fn Use() {
// CHECK:STDOUT: --- add_const.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %X: type = class_type @X [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %pattern_type.6cb: type = pattern_type %X [concrete]
// CHECK:STDOUT: %Init.type: type = fn_type @Init [concrete]
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %Init: %Init.type = struct_value () [concrete]
@@ -112,8 +114,72 @@ fn Use() {
// CHECK:STDOUT: %reference.var: ref %const = var_storage file.%reference.var_patt [concrete]
// CHECK:STDOUT: %addr.daa: %ptr.faf = addr_of %reference.var [concrete]
// CHECK:STDOUT: %b.patt: %pattern_type.7f7 = value_binding_pattern b [concrete]
// CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete]
// CHECK:STDOUT: %self.param_patt.52f: %pattern_type.a96 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.4b1: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt.52f [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc14_3.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.1: type = fn_type @Destroy.WithSelf.Op.loc14_3.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.1: %Destroy.WithSelf.Op.type.ef016f.1 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.925: %pattern_type.6cb = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.617: %pattern_type.6cb = wrapper_binding_pattern self, %self.param_patt.925 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc14_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.2: type = fn_type @Destroy.WithSelf.Op.loc14_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.2: %Destroy.WithSelf.Op.type.ef016f.2 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.b9b: %pattern_type.dfd = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.a99: %pattern_type.dfd = wrapper_binding_pattern self, %self.param_patt.b9b [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc14_3.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.3: type = fn_type @Destroy.WithSelf.Op.loc14_3.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.3: %Destroy.WithSelf.Op.type.ef016f.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3: type = fn_type @Destroy.WithSelf.SelfDestruct.loc14_3.3 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.3: %Destroy.WithSelf.SelfDestruct.type.fbceb5.3 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc14_3.1 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.a96 = ref_param_pattern [concrete = constants.%self.param_patt.52f]
// CHECK:STDOUT: %self.patt: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.4b1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_struct_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_struct_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.1: %Destroy.WithSelf.Op.type.ef016f.1 = fn_decl @Destroy.WithSelf.Op.loc14_3.1 [concrete = constants.%Destroy.WithSelf.Op.403171.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.a96 = ref_param_pattern [concrete = constants.%self.param_patt.52f]
// CHECK:STDOUT: %self.patt: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.4b1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_struct_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_struct_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc14_3.2 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6cb = ref_param_pattern [concrete = constants.%self.param_patt.925]
// CHECK:STDOUT: %self.patt: %pattern_type.6cb = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.617]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %X = ref_param call_param0
// CHECK:STDOUT: %self: ref %X = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.2: %Destroy.WithSelf.Op.type.ef016f.2 = fn_decl @Destroy.WithSelf.Op.loc14_3.2 [concrete = constants.%Destroy.WithSelf.Op.403171.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6cb = ref_param_pattern [concrete = constants.%self.param_patt.925]
// CHECK:STDOUT: %self.patt: %pattern_type.6cb = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.617]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %X = ref_param call_param0
// CHECK:STDOUT: %self: ref %X = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.3: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.3 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc14_3.3 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.dfd = ref_param_pattern [concrete = constants.%self.param_patt.b9b]
// CHECK:STDOUT: %self.patt: %pattern_type.dfd = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.a99]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %const = ref_param call_param0
// CHECK:STDOUT: %self: ref %const = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.3: %Destroy.WithSelf.Op.type.ef016f.3 = fn_decl @Destroy.WithSelf.Op.loc14_3.3 [concrete = constants.%Destroy.WithSelf.Op.403171.3] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.dfd = ref_param_pattern [concrete = constants.%self.param_patt.b9b]
// CHECK:STDOUT: %self.patt: %pattern_type.dfd = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.a99]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %const = ref_param call_param0
// CHECK:STDOUT: %self: ref %const = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Use() {
@@ -179,20 +245,47 @@ fn Use() {
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %b.patt: %pattern_type.7f7 = value_binding_pattern b [concrete = constants.%b.patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.bound: <bound method> = bound_method %i.var, constants.%Destroy.WithSelf.Op.403171.3
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %Destroy.WithSelf.Op.bound(%i.var)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %i.var, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.3
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%i.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc14_3.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc14_3.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc14_3.2(%self.param: ref %X) {
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc14_3.1(%self.param: ref %empty_struct_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.1(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.1(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc14_3.2(%self.param: ref %X) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc14_3.3(%self.param: ref %const) {
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc14_3.2(%self.param: ref %X) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc14_3.2(%self.param: ref %X) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.2(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.2(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc14_3.3(%self.param: ref %const) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc14_3.3(%self.param: ref %const) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc14_3.3(%self.param: ref %const) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.3(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.3(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -204,6 +297,7 @@ fn Use() {
// CHECK:STDOUT: --- remove_const.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %X: type = class_type @X [concrete]
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
// CHECK:STDOUT: %const: type = const_type %X [concrete]
@@ -214,8 +308,52 @@ fn Use() {
// CHECK:STDOUT: %i.patt: %pattern_type.6cb = ref_binding_pattern i [concrete]
// CHECK:STDOUT: %i.var_patt: %pattern_type.6cb = var_pattern %i.patt [concrete]
// CHECK:STDOUT: %v.patt: %pattern_type.6cb = value_binding_pattern v [concrete]
// CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete]
// CHECK:STDOUT: %self.param_patt.52f: %pattern_type.a96 = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.4b1: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt.52f [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc12_3.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.1: type = fn_type @Destroy.WithSelf.Op.loc12_3.1 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.1: %Destroy.WithSelf.Op.type.ef016f.1 = struct_value () [concrete]
// CHECK:STDOUT: %self.param_patt.925: %pattern_type.6cb = ref_param_pattern [concrete]
// CHECK:STDOUT: %self.patt.617: %pattern_type.6cb = wrapper_binding_pattern self, %self.param_patt.925 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc12_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.2: type = fn_type @Destroy.WithSelf.Op.loc12_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.2: %Destroy.WithSelf.Op.type.ef016f.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2: type = fn_type @Destroy.WithSelf.SelfDestruct.loc12_3.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.2: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generated {
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc12_3.1 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.a96 = ref_param_pattern [concrete = constants.%self.param_patt.52f]
// CHECK:STDOUT: %self.patt: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.4b1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_struct_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_struct_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.1: %Destroy.WithSelf.Op.type.ef016f.1 = fn_decl @Destroy.WithSelf.Op.loc12_3.1 [concrete = constants.%Destroy.WithSelf.Op.403171.1] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.a96 = ref_param_pattern [concrete = constants.%self.param_patt.52f]
// CHECK:STDOUT: %self.patt: %pattern_type.a96 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.4b1]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %empty_struct_type = ref_param call_param0
// CHECK:STDOUT: %self: ref %empty_struct_type = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc12_3.2 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6cb = ref_param_pattern [concrete = constants.%self.param_patt.925]
// CHECK:STDOUT: %self.patt: %pattern_type.6cb = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.617]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %X = ref_param call_param0
// CHECK:STDOUT: %self: ref %X = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.2: %Destroy.WithSelf.Op.type.ef016f.2 = fn_decl @Destroy.WithSelf.Op.loc12_3.2 [concrete = constants.%Destroy.WithSelf.Op.403171.2] {
// CHECK:STDOUT: %self.param_patt: %pattern_type.6cb = ref_param_pattern [concrete = constants.%self.param_patt.925]
// CHECK:STDOUT: %self.patt: %pattern_type.6cb = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.617]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param: ref %X = ref_param call_param0
// CHECK:STDOUT: %self: ref %X = wrapper_binding self, %self.param
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Use() {
@@ -243,15 +381,33 @@ fn Use() {
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %v.patt: %pattern_type.6cb = value_binding_pattern v [concrete = constants.%v.patt]
// CHECK:STDOUT: }
// CHECK:STDOUT: %Destroy.WithSelf.Op.bound: <bound method> = bound_method %i.var, constants.%Destroy.WithSelf.Op.403171.2
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %Destroy.WithSelf.Op.bound(%i.var)
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %i.var, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.2
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%i.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc12_3.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc12_3.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc12_3.2(%self.param: ref %X) {
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc12_3.1(%self.param: ref %empty_struct_type) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.1(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.1(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc12_3.2(%self.param: ref %X) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc12_3.2(%self.param: ref %X) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc12_3.2(%self.param: ref %X) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.2(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.2(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -263,6 +419,7 @@ fn Use() {
// CHECK:STDOUT: --- unsafe_remove_const.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
// CHECK:STDOUT: %X: type = class_type @X [concrete]
// CHECK:STDOUT: %const: type = const_type %X [concrete]
// CHECK:STDOUT: %ptr.faf: type = ptr_type %const [concrete]

Some files were not shown because too many files have changed in this diff Show More