`ErrorRecoveryBuffer::Apply` flagged each inserted token via the
pre-insertion index of the token it was inserted before. Once any
earlier insertion had been applied, that index no longer matches the
token's position in the merged list: with two closers inserted at the
same point (`{((}`), the second recovery token was left unflagged, and
with insertions at two separate points (`{(} {(}`), a real token was
flagged in the second one's place.
`IsRecoveryToken` now reports exactly the inserted tokens. This will
matter for `carbon format`: a recovery token's text exists in no source
byte range, so a minimal-edit model must know not to anchor edits on it.
Assisted-by: Claude Code
Carbon currently requires a comment to be the only non-whitespace on its
line. A `//` comment that follows other content on a line, called a
_trailing comment_, is a lexer error. This proposal removes that
restriction, allowing a comment to follow other content on a line.
Everything else about comments is unchanged: a comment still begins with
`//`, still requires whitespace after the `//`, and still runs to the
end of the line. Carbon continues to provide only line comments; no
block or intra-line comments are added.
Three observations motivate the change. First, trailing comments are
well suited to short _annotations_ attached to a specific entity or
value on a line. Second, the lexer design now makes it trivial to lex
trailing comments, and in fact requires extra logic and potentially cost
to reject them. Third, C++ code routinely uses trailing comments, so
allowing them lets Carbon carry the layout of migrated code over
directly, rather than reworking each comment to read well in a different
structure.
Implementation notes (beyond the proposal's design):
Keeping trailing comments cheap to lex required a few supporting
changes, all of which keep the cost off the lexer's hot path:
- The lexer already dispatches `//` to comment lexing wherever it
appears, so classifying a comment as trailing is a single O(1) check of
whether the `//` is the line's first non-whitespace (`start + indent`).
The hot comment path is otherwise unchanged.
- That check relies on each line's recorded indentation being its real
leading whitespace. Multi-line string literals previously recorded the
column where the literal opened for the lines they span; they now record
the true (closing-delimiter) indentation instead.
- Parser error recovery (`SkipPastLikelyEnd`) had relied on that
opening-column indentation to keep tokens following a multi-line string
literal attached to the same construct. It now reconstructs that
relationship directly by consulting the line on which the literal
opened, including when other tokens follow the closing delimiter (such
as `''' + "more"`). This is on the cold recovery path.
- `CommentData` records the trailing bit in the high bit of its length
field, keeping it at 8 bytes.
Assisted-by: Claude Code
Instead of maintaining a stack of pending subpatterns which might or
might not contain expressions, we mark non-nesting regions during
pattern handling that might contain an expression. The implementation
remains largely the same; the difference is that callers are expected to
end a pending expression region as soon as possible, rather than wait
for the end of the subpattern. This makes it possible to emit
non-pattern insts during pattern handling, without the risk that they
will get caught in a pending expression region further up the stack.
When we find a named constraint during identity, we recurse into it. The
specific args of the named constraint may contain references to `.Self`
which can then make `.Self` appear inside the named constraint, which
was making us replace `.Self` at multiple levels and incorrectly. The
first specific argument replaces `Self` in the named constraint, and we
pass in the self-type of the identify operation. This may contain
`.Self` and we should _not_ be replacing the `.Self` references with the
self-type that they are contained within. This led to infinite cycles.
In the meantime, we have made the toolchain reject any ambiguous `.Self`
from being constructed. So we know there is only one value of `.Self`
around in a facet type.
So now we replace `.Self` only in the top level facet type during
identity. That means replacing `.Self` in the specifics of the named
constraints that we recurse into. But we do _not_ replace `.Self`
anymore inside those named constraints. This resolves the infinite loop.
At the same time, when we are identifying an `impls` constraint from
earlier in the same facet type, like `C impls Z(.Self)` we are
identifying with a self-type of `C`. We want the output to use `C` as
the self-type since we should get back an identified facet type that
says `C impls Z(.Self)`. But we do _not_ want to replace the `.Self`
there since we're inside a facet type and the `.Self` does not refer to
`C`. So we parameterize `TryToIdentifyFacetType` to not replace `.Self`
when identifying an `impls` constraint from the `where_stack()`. This
resolves a large number of `fail_todo_` tests.
Impl lookup identifies the types of facets in the query, replacing
`.Self` in each type with the facet. This allows references using the
facet from outside the facet type to match similar structures inside the
facet type.
However replacing `.Self` with the facet can re-evaluate symbolic impl
lookups inside the facet type. These can perform deduction in generic
`final impl`s, which can attempt to convert the facet. Convert does an
impl lookup with that facet in the query, which causes us to form an
infinite recursion cycle.
Add tests that caused such a cycle, to demonstrate we no longer crash.
Some of these tests fail impl lookups using concrete values in the query
that match values from a `final impl`, with TODOs to address them.
This proposal removes the `:!` syntax for generics and templates in
favor of keywords (`generic`, `template`, `runtime`) and contextual
defaults for phase. It also replaces `:?` with `fwd` and introduces
`exttype` for extended types.
Assisted-by: Antigravity with Gemini, and Claude
---------
Co-authored-by: Geoff Romer <gromer@google.com>
A lambda introducer (`fn` in expression context) with no parameters and
no body -- for example `(fn)`, where `)` immediately follows `fn` --
left `HandleLambdaAfterParams` calling `ReturnErrorOnState()` without
ever emitting a `Lambda` node. The orphaned `LambdaIntroducer` leaf was
then left where an expression was required, so `Parse` produced a tree
that failed its own verification and aborted via `CARBON_FATAL`.
Recover the way `HandleLambdaBody` already does for a missing body after
a return type: emit a placeholder `InvalidParse` body and finish a
complete `Lambda` node, so the lambda stays a valid expression and the
surrounding construct (here a `ParenExpr`) extracts cleanly.
Found by fuzzing.
Assisted-by: Claude Code
One API fix: BumpPtrAllocator no longer tracks the amount of memory it's
handed out separately from the amount of memory it has allocated from
the system.
Assisted-by: Gemini via Antigravity
`IsRawIdentifier` checked `token_text.starts_with("r#")` and then read
`token_text[2]`, but `starts_with` only guarantees a length of two. An
`r` identifier immediately followed by `#` at the end of the source --
so the token text is exactly `r#` -- made the `token_text[2]` read run
off the end. Guard on the length first.
Found by fuzzing. The read is reached only via `GetTokenText`, so the
parser fuzzer, which does not request token text, never hit it.
Assisted-by: Claude Code
Our development instructions now recommend installing a number of
binaries through `cargo`. Updating these binaries has to be done by
hand. So we can provide a script that users can use to update them
easily and regularly.
Adds `toolchain/benchmarking/prelude_benchmark.cpp`, which measures the
time to compile the Core prelude and reports the most interesting SemIR
memory statistics as benchmark counters.
The prelude is compiled by checking a file that imports it: the implicit
prelude import causes the check phase to lex, parse, and check the full
set of prelude files, so this is a direct measure of prelude compilation
cost. Four input variations exercise increasing amounts of the prelude:
an empty file, a minimal single-type use, an operator-heavy file that
hits many impls, and a compact file that pulls in a wide swath of the
prelude.
Memory usage is queried directly: `Driver::set_mem_usage` takes a
`MemUsage` that a compile merges each file's usage into; the benchmark
passes one, compiles, and sums the entries by label. A compilation unit
collects into its own `MemUsage` whenever usage is dumped or a sink is
provided (decided in `SetMultiUnitCache`); after a file is done it dumps
that `MemUsage` per-file as before and, if a sink was provided, merges
into it via a new `MemUsage::Add(const MemUsage&)` overload. `MemUsage`
also exposes its entries via a public `Entry` type and an `entries()`
accessor.
Also extends `scripts/bench_runner.py` to (1) treat Mem-prefixed
counters as cost metrics (smaller is better) and (2) tolerate metrics
that aren't reported by every benchmark in a binary.
Assisted-by: Claude Code
---------
Co-authored-by: Christopher Di Bella <cjdb.ns@gmail.com>
Instead of traversing the entire pattern block looking for
`VarPattern`s, we keep track of them on creation, and then build
`VarStorage`s directly from that list.
This also gets rid of the global `var_storage_map`, and instead keep
that information narrowly scoped to each full-pattern, and consume it in
a single linear traversal instead of with random-access lookups. To
enable that, this fixes a parse bug where nested `var` patterns were
getting diagnosed but not marked as errors.
Rumdl already appears to have _significantly_ fewer bugs than prettier,
and a solid LSP for editor integration.
The tool is: https://github.com/rvben/rumdl/
I've separated out the change across three commits for easier review.
The configuration tries to match the existing formatting, the changes to
the all the files are to correct issues found by the new tool.
Assisted-by: Antigravity with Gemini
---------
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Support multi-file compilation, and in particular imports of files from
the prelude, in `carbon language_server`.
In order to properly interface with `CompileDriver`, also switch over to
building a proper VFS from the documents we're given.
Assisted-by: Gemini via Antigravity
In #7378 we forbid writing `where` on the RHS of another `where`
expression. However it's still possible to inject a `where` expression
through eval, using an `alias` or an `eval fn`. We address this by
looking in the constant value of constraints of a `WhereExpr` (which
sees the outcome of eval) and searching for a nested `where` there. We
can determine a facet type was written with a nested `where` by seeing
that it has some non-extend constraint.
---------
Co-authored-by: David Blaikie <dblaikie@gmail.com>
If the deduction fails while forming the parameter type, ensure that we
print an actual diagnostic saying what went wrong.
And always ensure that an invalid array bounds error points at a
location. The `inst_id` given to `EvalConstantInst` always has a
location, but the `bounds_id` instruction inside it may be canonical
when it's coming from inside a larger type. So when it is, fall back to
using the location of the whole array inst.
These files aren't exactly written in Carbon, but rather in some
meta-language with file splits and semantically meaningful comments, and
in any case getting red squiggles for expected errors is distracting and
largely unhelpful.
(We *could* teach the LSP to run the test and produce errors if the test
doesn't match its expectations, but it's not clear that that would be
helpful in practice either.)
This disallows building a facet type that contains another facet type
with non-extend constraints in it. Which in turn prevents the
possibility of introducing a different `.Self` into a facet type.
Eval can still insert a facet type with non-extend constraints, as we
only prevent it for `where` being written into the facet type. There is
a TODO in handle_where.cpp for this and some tests in
toolchain/check/testdata/facet/nested_facet_types_from_eval.carbon
In `PerformActionHelper`, use the unqualified type for lookup.
In `PerformInstanceBinding`, propagate qualifiers to the unbound element
type's class type when doing the `ConvertToValueOrRefOfType` conversion,
and to the element type when forming the `ClassElementAccess` instr
(except for `partial`, which is only used if the member being accessed
is `base`).
In handle_operator.cpp, prevent assignment to a reference to a const
type.
Move the existing derived->base conversion earlier in
`PerformBuiltinConversion`, into the block that handles qualifier
conversions. This allows, for example, converting from `partial Derived`
to `partial Base` -- see the tests in
`toolchain/check/testdata/class/inheritance/derived_to_base.carbon`.
The function to set the visible declarations with a given name
overwrites any existing declarations imported from an AST file, so we
need to avoid calling that for declaration contexts whose names are
managed by Clang to avoid clobbering names imported from modules.
Assisted-by: Gemini via Antigravity
Depending on the order in which type completion identifies things in the
facet type, it may try to replace `.Self` with a facet that fails to
convert to the type of `.Self` due to a monomorphization error. We
should fail gracefully, not crash.
The text SVG would sometimes be processed by vscode before it finished
initializing its font engine and then get aggressively cached, resulting
in the logo sometimes missing one of the characters.
The new SVG logo is a bit more dense than the old text one was, so shows
up as a C++ "banner" a bit better, and should be displayed more
consistently.
Assisted-by: Gemini via Antigravity
Our mirror repositories contain vim scripts and textmate grammars,
neither of which have any need for the LLVM exception, so we can use the
base Apache 2.0 license there with no loss of relevant permissions. This
is a compatible license, so providing these repositories under these
license terms is valid.
Other than removing the LLVM exception, the new license file has one
other change compared to LICENSE: the amount of whitespace on some lines
is reduced from four spaces to three. This makes the new license
*exactly* match the Apache 2.0 license, byte-for-byte.
This is important because the license checking done by GitHub's linguist
project doesn't recognize Apache-2.0-with-LLVM-exception as an
acceptable license, but does allow plain Apache-2.0.
Copy MultiplexExternalSemaSource.h and MultiplexExternalSemaSource.cpp
from https://github.com/llvm/llvm-project/pull/204458 into
third_party/llvm, and apply a few minor changes to allow them to compile
and pass precommit checks.
This allows
`0011-Add-empty-constructor-and-GetSources-method-to-Multi.patch` to be
removed, which brings Carbon closer to being able to compile on an
unmodified LLVM toolchain.
Add support to the dump command to `dump context import_ir_inst123`
Support for dumping the ids was previously added, but missed support for
parsing and creating a C++ id from its printed id.
Formatting an instruction with an initializing expr category will call
`FindStorageArgForInitializer()` to get the target id.
`GetExprCategory()` supports imported instructions by walking to the
imported IR, in order to get the expr category. We do the same in
`FindStorageArgForInitializer()`, instead of hitting a CARBON_CHECK.
The new test crashes without the changes here.
Also some Bundle API tweaks:
- Remove support for non-canonical bundle IDs. Bundles don't have a
unique identity, so non-canonical bundle IDs would bloat the SemIR for
no benefit.
- Adjust the conversions between raw and typed bundle IDs to not be
templated. This makes the conversions easier to access in a debugger.
Notably this allows accessing fields in an abstract base class via a
derived class without going through `base`. E.g.
`my_obj.field_in_base_class` rather than
`my_obj.base.field_in_base_class`.
Incorporates changes from these proposals:
- #2022
- #2875
- #3262
- #3763
- #3848
- #5434
A small amount of updating was done to lambdas.md and variadics.md to
harmonize with these changes.
Assisted-by: Gemini via Antigravity
---------
Co-authored-by: Josh L <josh11b@users.noreply.github.com>
Co-authored-by: Geoff Romer <gromer@google.com>
TextMate doesn't support JSON grammars, so convert our JSON grammar to a
plist automatically as a pre-commit check. Fix malformed info.plist
file. Add missing uuid to grammar file.
Assisted-by: Gemini via Antigravity