Add basic support for lowering templates: we can now lower `SpliceInst`
in the case where the generic and specific are from the same file (and
we don't support importing templates from other files yet in general).
In order for this to work, lowering needs to be able to query the
expression category, and to handle instructions that appear to be
(template) constants in the generic but turn out to be non-constant in
the specific, so support for that is added.
Switch `type_of_inst` from being added as an action inst to being added
as a normal inst, since it's not an action and the old approach led to a
crash in lowering.
Replace `refine_type_action` with `refine_inst_action`, and generate a
`specific_inst` instead of an `as_compatible` to represent the specific
version of an instruction that's used as an input to a template action.
This gives us a place to handle other properties of the instruction that
might vary from generic to specific beyond its type, such as its
constant value and its expression category.
For now, we provide a non-template-dependent constant value to the
`specific_inst` in addition to the non-template-dependent type we have
traditionally provided. This doesn't seem to matter for any current
actions, but sets us up to better handle future actions. The
`specific_inst` representation also allows downstream consumers of the
instruction to track which specific they should be requesting
information from. Providing a correct expression category for
`specific_inst` will be handled in a future PR.
`TemplateInst` wraps another inst. If that inst is symbolic, it is
treated as a template by `OperandDependence`.
Use this to replace `CallCppTemplateAction` with the more general
`CallAction`.
Also add a default for `EqWith.NotEqual`.
Switch advent examples to use these named constraints, and also go
through all the other TODOs in the advent examples and fix the ones that
are trivially fixable now.
`GetCallee` is sometimes called on a spliced instruction, and is
checking its exact inst operand to see if it's a `BoundMethod`. This
fails if the `BoundMethod` is wrapped in another instruction, such as a
splice. Normally our approach for such a situation would be to
constant-evaluate the operand, but that doesn't work here because the
`BoundMethod` will be non-constant if its bound `self` is. So instead we
now step through splice instructions manually when looking for the
`BoundMethod`.
Calls with template callee or args can now be deferred via an
InstAction. This allows code like this to check:
```carbon
import Cpp inline '''
template<typename T>
struct C {};
''';
fn F(generic T: type) {
let unused c: Cpp.C(T) = Cpp.C(T).C();
}
```
This allows generated functions to have different forms for different
parameters, and by-ref or by-value return forms. As a byproduct, this
allows generated functions to supply a return type _inst_ ID when they
have one, which preserves things like location information.
Example:
```carbon
import Cpp library "<vector>";
class C(T: type) {
var v: Cpp.std.vector(T);
}
inline Cpp '''
void F() {
Carbon::C<int> c;
c.v.push_back(123);
std::cout << c.v.back() << std::endl;
}
''';
```
A new `CallCppTemplateAction` is used to delay performing the C++
template call until non-symbolic arguments are known.
Building a `for` loop looks `Core.Iterate` up twice: once for
`NewCursor` to make the cursor, and again for `Next` to advance it. A
range that implements neither failed both lookups and reported both, so
a loop over something that isn't iterable produced two errors saying the
same thing about the same expression.
The second lookup is skipped when the first already failed, which is
what `BuildBinaryOperator`'s `diagnose` parameter is for. The
`ErrorInst` it returns instead does not reach the produced SemIR: the
loop is abandoned on the error either way.
Assisted-by: Claude Code
---------
Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Fix a bunch of cases where we use the same external name to mean
multiple different things in the same test. We've historically gotten
away with this, but under `--share-cpp-ast`, it becomes an error, at
least if the entity is either defined in, or used from, C++ code.
Assisted-by: Gemini via Antigravity (original change) and Claude Code
(suggested edits in review)
---------
Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
It's not enough for field types of Carbon classes to be complete in
SemIR. If the field is exported to Clang, we also need the type to be
complete in Clang's AST, since Clang assumes it has a definition
available for the types of all fields of a complete class.
For certain kinds of error, clang's parser will succeed but produce an
expression marked as "contains error". Clang's constant evaluator
asserts if given one of those, so return early if we encounter one.
This is similar to the previously-added support for accessing generic
carbon classes from C++, but with the specific defined by Carbon, rather
than being derived from template args supplied by clang in
`LoadExternalSpecializations`.
Example:
```carbon
class C(T: type) {
var t: T;
}
alias A = C(i32);
inline Cpp '''
void F() {
Carbon::A a;
a.t = 123;
}
'''
```
The exported class was being inserted with a type inst ID as the key
(and looked up that way elsewhere), but when checking if the generic
class was already exported, the `first_decl_id` was being used. Make it
consistent, and opt for `first_decl_id` everywhere since it provides a
better location for diagnostics.
Implementing interface modifiers causes an infinite loop when generating
fingerprints because the witness value generates a fingerprint that's
dependent on something dependent on the witness value. We've debugged
this to the witness table's `elements_id` field.
This hack is a workaround for creating a new block type whose value is
not codependent with its identity.
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
If we find an overload set containing mulitple methods, discard any
non-const methods and try again. This allows libc++'s `std::vector` to
be iterated with range-based for.
`String.size` is likely to be a signed word-sized integer in the future,
(per a Discord conversation). Changing to `i64` now allows us to iterate
over a string's contents using `IntRange`.
Add `Core.CppCompat.[U]Long64` to represent a 64-bit long that is not
`i64`. Treat it as being "just slightly smaller than" `i64`, like we
treat `Core.CppCompat.LongLong64` as being "just slightly larger than"
`i64`, so that we get implicit conversions `Cpp.long` -> `i64` ->
`Cpp.long_long` on all targets.
This follows the direction of proposal #5448, and seems like the obvious
extension of the `[U]Long32` and `[U]LongLong64` types added in #6275
for targets of this "shape".
Assisted-by: Gemini via Antigravity
Clang treats "file not found" as a fatal error and stops emitting more
diagnostics after reaching it, so these tests don't work in
`--share-cpp-ast` mode if they are all in the same file. So split them
into distinct test files.
Make multiple imports of the same header only parse it once per C++
domain. Reuse of the same header in `--share-cpp-ast` mode now reuses
the representation.
Importing a Carbon file with C++ dependencies now makes those transitive
C++ dependencies in the same C++ domain visible too.
Assisted-by: Gemini via Antigravity
Instead of creating a CodeGenerator per CppDomain, and then crashing in
lowering when we try to consume the same llvm Module multiple times,
create a CodeGenerator for each CppFile within the domain.
For now, we mulitplex all of Clang's ASTConsumer output to all code
generators, which means that any strong external definitions within a
Carbon file (for example, in an inline `Cpp` fragment) will be emitted
to all output files in the same `CppDomain`, resulting in link errors
due to symbol redefinitions. This will be addressed later. But this
should be sufficient for Carbon compilations in which such symbols are
not defined.
We also don't yet attempt to classify which compilations will need C++
code generation, and instead create a clang `CodeGenerator` for every
Carbon file that has C++ imports. For `carbom compile`, only one Carbon
file will need code generation, and yet we still build multiple
`CodeGenerator` objects in general. Fixing this requires more plumbing
from the driver, and this will also be handled in a follow-up.
Assisted-by: Gemini via Antigravity
This isolates the C++ imports in different Carbon files from each other
in `--share-cpp-ast` mode, so that a Carbon file can only see the
portions of the shared Clang `ASTContext` that it actually imported.
Assisted-by: Gemini via Antigravity
Inject the name of a macro rather than its contents when computing its
expansion. If the macro refers to itself, it will not expand within its
own body, rather than expanding once.
Switching from `EnterTokenStream` to `EnterToken` exposed that our Clang
preprocessing environment was a little broken -- we reached the end of
the primary source file and starting tearing stuff down before we
actually finished parsing, which we were mostly getting away with before
but aren't any more. Enabled Clang's incremental processing mode to fix
this. This causes Clang to remain in the main source file when it
reaches EOF instead of popping it. This also causes the diagnostics for
invalid `module;` declarations to change, but in a way that seems not
really any worse than before.
Also slightly changes the diagnostics produced from macro expansion
failures. The new diagnostics are a bit more precise -- they now capture
the outermost level of macro expansion -- but we don't do a good job of
rendering the Clang snippet attached to the "in macro expansion" context
note yet, so the context looks a bit weird: we get two different
snippets attached to the same diagnostic.
In Convert, we require the source value's type to be compete so that we
can look for `base` classes and `adapt` relationships. However these can
only be present in a `ClassType`, so we only need `ClassType`s to be
complete.
Reduce the requirement in Convert to not complete types that are not a
`ClassType`, and which can not contain a `ClassType` as part of their
class.
Ideally we would only _only_ require the `ClassType` itself to be
complete, and only if we're looking for a base or adapt. However lower
depends on us completing all Convert source types that contain a class.
This seems to suggest we're lacking checks for complete types somewhere
else and Convert is making up for it. A TODO has been added. The
`toolchain/driver/testdata/compile/optimize/optimize_debug.carbon` test
is an example that CHECKs due to failing to verify the LLVM module if we
do not compute the complete type of all class-containing types in
Convert.
The critical step this PR is doing is to stop trying to complete a
`FacetType` when converting from a facet. This avoids trying to complete
a named constraint when converting `Self` inside that named constraint.
Doing so causes a cycle when the conversion of `Self` is performed in
eval of an `extend require` decl, since requiring the named constraint
to be complete re-evaluates the `extend require` decl again. A test is
added that crashed in an infinite loop before this change.
It also depends on #7584, which was intended to be an optimization but
is now load bearing. Because converting `Self` leaves an impl lookup
inst behind, and if that inst is re-evaluated inside impl lookup (by
forming a specific of a `require` decl through identify) then we have a
similar cycle.
During impl lookup, for each (generic) impl candidate, we form a
specific for that impl by deducing its generic arguments. Then we
compare the query interface against the impl's specific interface. That
comparison needs the deduced arguments applied to the impl's specific
interface. Previously we were doing this by getting the impl's
constraint facet type with the impl's specific applied (via
`GetConstantValueInSpecific()`) and then identifying that facet type
with the impl's deduced self.
Identify is a fairly expensive operation. It runs subst, trying to
replace `.Self` references. It walks named constraints. It collects
require declarations. We're looking at making it do _more_ in the future
too, including rewrite constraint resolution and collecting rewrite and
same-type constraints. For this reason we have a cache to make it cheap
on the second run, but it's still a very heavyweight operation to
involve in impl lookup, when all we want is to apply the impl's specific
to its target interface.
We almost have all the information we need to avoid the identification
step. We have the impl's specific after deduction. And we have the
SpecificInterface that the impl is targeting in the `Impl` struct. When
we form the specific for the impl itself, we resolve the declaration
block and form new constant values for all instructions in there, but
that does not cover the SpecificInterface that we're storing in the
`Impl` struct. So we add a new instruction to the impl's eval block,
which will be symbolic when the impl is generic and the target interface
depends on a generic parameter. And we store the `InstId` in the `Impl`
struct. This allows us to gets its constant value later with the impl's
specific applied. From that constant value we can then pull out the
SpecificInterface that the impl is targeting.
`abstract fn` was exported to C++ as a plain virtual function rather
than a pure virtual one, so the class wasn't abstract and could be
instantiated from C++.
Abstract functions no longer get a thunk since there is no definition to
call. The tests are prefixed with `fail_` since an abstract class still
errors on `Core.Destroy` regardless.
A method declared with `self` does not modify the object, but it was
exported to C++ as a non-const member function, so calling it on a const
reference would fail.
```carbon
class C {
fn Get(self);
}
inline Cpp '''
void F(const Carbon::C& c) {
c.Get();
}
''';
```
```
error: 'this' argument to member function 'Get' has type 'const Carbon::C', but function is not marked const
```
Import already maps `f() const` to `fn f(self)`, and this PR implements
the same behavior for exporting. No ref-qualifier is added, since that
maps to `ref self`, so that is unchanged.
`GetThisArg()` now builds `this` from the method instead of the parent
record, so that it picks up the method's const-qualifier.
Point symbolic witnesses into `.Self` written inside an impl decl at the
impl that is being declared. This is tricky because the impl does not
yet exist. So we use a new instruction `ImplSelfWitness` which _will_ be
replaced by the `ImplWitness` once it becomes available. The
`ImplSelfWitness` acts like a symbolic witness, except it does not
perform lookup, since we know which impl we will get a witness from.
This prevents us from finding other impls when performing lookups into
`.Self` in an impl decl, which produces incorrect/incoherent results.
Instead of building one Clang `ASTContext` per compilation, the
`--share-cpp-ast` flag causes us to build a single `ASTContext` and
share it across all contexts. One new abstraction is added: `CppDomain`
represents the Carbon-side view of a Clang AST that might be shared
across multiple `SemIR::File`s. This object owns the Clang instance and
the AST.
For now, we have no isolation between the C++ state exposed to different
Carbon compilations, and we have no multiplexing of generated LLVM IR
from C++ into different Carbon compilations, so the mode is not usable
yet. The plan is to keep it behind a flag until it's ready.
Assisted-by: Gemini via Antigravity
Added method_alias.carbon test so that the class export code in
`ExportNameScopeToCpp` is tested. Refactored `ExportClassToCpp` so that
`ExportNameScopeToCpp` can reuse that code.
Moved the `identifier_info` code in `ExportNameScopeToCpp` into the
namespace block, because the name scope's name ID is not valid for
classes.
Added a call to `CompleteType` for classes exported via
`ExportNameScopeToCpp`, otherwise a "queried property of class with no
definition" assert is later reached (when adding methods) in the call
chain `BuildCppToCarbonThunkDecl` -> `DeclContext::addHiddenDecl` ->
`CXXRecordDecl::addedMember` -> `CXXRecordDecl::data`.
The check for a specific in `TryMapClassType` is unnecessary;
immediately after it calls `ExportClassToCpp`, which has the same check.
The latter also has a `context.TODO`, which provides a clearer error.
Also improved the `LocId` in `ExportClassToCpp` to use the location of
the first decl rather than the empty location of the class type. This is
the same fix as
https://github.com/carbon-language/carbon-lang/pull/7533, just applied a
little more broadly. This makes the `context.TODO` above point at the
class rather than the start of the source file.
Add a `return_type_id` field to `FunctionInfo` in
`toolchain/check/cpp/export.cpp`. As with the `explicit_params` field,
`ExportFunctionSpecializationToCpp` updates this to the return type in
the specific.
Refactored `BuildCppFunctionDeclForCarbonFn` into
`BuildCppFunctionDeclForNonGenericCarbonFn` and
`BuildCppFunctionDeclForGenericCarbonFn`, with `BuildCppFunctionDecl`
containing shared code.
The `generic_type_impls_interface.carbon` test is updated to include a
generic return type.
The specific location's not ideal (rather than the open curly, or
semicolon for a declaration - the two locations should be the `class`
and then the class name), but the same as we do for functions for now &
enough to get by.
This specifically also fixes a crash I found due to dtors being
generated without a location (because implicitly created functions would
use the class's location), creating a function call without a debug
location, which fails the LLVM IR verifier.
Split up the CppRange interface into smaller parts, with the intent of
improving the diagnostic quality and making the implementation easier to
understand.
This also makes the implementation details of the CppRange machinery
private, which breaks one of the existing tests; that test is split into
two files, one which tests the low-level machinery works, and another
that tests the resulting prelude behavior.
This change exposes a crash in `where` expression handling, where we
would perform a substitution that creates a new `SpecificConstant` that
refers into a region of a generic that has never been resolved. Fix that
by resolving the definition region of a generic if eval sees a
`SpecificConstant` that refers into it. This is usually not necessary
because something else should have resolved that region first, but that
doesn't happen here.
---------
Co-authored-by: Dana Jansens <danakj@orodu.net>
We can't use a `CallExpr` to call a constructor; use a
`CXXConstructExpr` instead. While this fixes the crash and gets us past
the initial constant evaluation, we still can't map the constant value
back into Carbon, so this doesn't actually make constexpr constructors
work yet. But it does stop Clang from crashing.
Fixes#7498.
Instead of tracking the cleanup scope depth on entry to each scope,
track an "ambient" cleanup scope depth that's *after* the destructors of
local variables in that scope. This gets increased to include the
destructors of local variables when we create a name-binding
declaration. Then, when we reach a point where temporaries should be
destroyed, run cleanups that are after the ambient cleanup scope depth
on the stack. This happens:
* At the `;` of a statement expression.
* At the `)` of an `if` or `while` statement.
* After performing the implied `HasValue()` call in a `for` statement.
Per informal agreement with leads, this means we lifetime-extend all
temporaries created in the initializer of a name-binding declaration to
the full scope of that declaration, but that temporaries created in an
expression statement are destroyed at the `;`.
When a Carbon virtual function overrides a C++ virtual function, we need
to export it with the C++ signature in order for it to work as an
override. Instead of mapping the C++ signature into Carbon and then back
again, use the original C++ signature from the base class as the
signature exported to C++.
Also add documentation explaining how we use thunks in C++ interop,
including in this new virtual function handling logic.
Destroy local variables and temporaries at each `}`, and when branching
with `break` and `continue`. In `for` statements, destroy loop variables
along with anything created within the loop at the end of each loop
iteration, and destroy the cursor and range object when the loop
terminates.
Assisted-by: Gemini via Antigravity
---------
Co-authored-by: Geoff Romer <gromer@google.com>
Instead of manually creating a map from symbolic types to concrete
types, create a Specific and look up parameter types via that Specific.
This allows C++ to call a Carbon function like `fn F[T: type](unused t:
T*) {}`. See generic_pointer.carbon.
Implement the toolchain side of proposal #7254, removing the `:!`
binding
syntax for generic and template parameters in favor of the keywords
`generic`,
`template`, and `runtime` plus contextual defaults for phase.
For valid programs this is semantics-preserving: each binding resolves
to the
same phase, and produces the same SemIR, as it did under `:!`/`:`. The
parser
derives a binding's phase from its syntactic context plus any explicit
phase
keyword; new diagnostics and error recovery for misused keywords are
described
below.
Implementation details for each component:
- Lexer: remove the `:!` (`ColonExclaim`) token, move its virtual
parse-node
budget onto `:`, and add the `generic` and `runtime` keywords.
- Parser: thread a `BindingContext` (`ExplicitParam`, `DeducedParam`, or
`CompileTimeEntityParam`) from declaration introducers down through
parameter
lists to each binding pattern, using a one-token lookahead to
distinguish a
name-qualifier parameter list from a declaration's own final list.
Parameters
of a compile-time entity (`class`, `interface`, `constraint`, `choice`,
`alias`, `export`, `namespace`) and deduced `[]` parameters default to
checked
generic; explicit function parameters and local bindings default to
runtime.
`HandleBindingPattern` resolves the phase from that context plus the
keyword: a
`generic` keyword needs no node of its own (the phase is carried by the
binding's node kind), while a `runtime` keyword is preserved as a
`RuntimeBindingName` node so `check` can name it in a diagnostic. A
phase
keyword that is merely redundant with the contextual default is
diagnosed
here, without invalidating the parse tree.
- Check: a phase keyword that is invalid for its context (for example
`runtime`
on a checked-generic parameter) is diagnosed here, and recovers by
building an
error binding that still introduces the name so that later uses of it do
not
produce cascading errors.
The removed `:!` syntax is now rejected as an ordinary parse error.
The `form`/`:?`/`->?` ("extended types") portion of proposal #7254 is
left for a
separate change.
Assisted-by: Claude Code
`LookupImplWitness` instructions inside the impl declaration can't use
the impl they are apart of. Previously we had an heuristic in eval which
would try to prevent finding the impl for a lookup from inside that
impl. But it breaks when the `.Self` is replaced in a generic impl with
a symbolic, and then that symbolic is replaced in a specific. The
specific's decl block contains that `LookupImplWitness` instruction and
it tries to use the impl it came from. This causes the same specific to
be formed again, but now it exists, so it's used as-is but it has no
decl block yet, and so we crash.
Now we ban an impl while we resolve its specific, both deduction of its
arguments and from any other substitution. The prevents instructions
from inside the impl (which are evaluated when resolving the specific)
from finding their own impl. We do so by adding the ImplId to a stack on
the Context, and then skipping such impls when looking for candidates
during eval.
This fixes a crash, which was demonstrated by the new test being added.
It also makes another todo test pass.
There's a whole lot of other semir churn, which seems to be mostly
reordering of constants. There are some fingerprint changes in
constants, but it appears they are the same canonical instructions, so
they don't represent a behaviour change. For example in
`toolchain/check/testdata/for/actual.carbon` the `%N.patt` constant has
been given its fingerprint suffix now as `%N.patt.aa5`. But they are
both this instruction, so it is just a formatting change:
```
inst6100001A: {kind: SymbolicBindingPattern, arg0: entity_name61000002, type: type(inst61000018)}
- name: `N`
- type: type(inst61000018): <pattern for Core.IntLiteral>; {kind: PatternType, arg0: inst(IntLiteralType), type: type(TypeType)} (concrete)
- value: symbolic_constant61000001
```
In #7436 we stopped substituting `.Self` when collecting witnesses out
of a facet type. While this was correct, it did not capture all the
cases that need to avoid substituting `.Self`. And it poisoned the
`IdentifiedFacetType` cache by not replacing `.Self` but storing the
result in the cache. This led to incoherent behaviour, where the result
of an impl lookup would change depending on which ones had been done
previously.
Now we use a flag to track for each `.Self` if we're currently
type-checking inside the scope where it was introduced in a facet type.
While inside that scope, identify should not replace the `.Self`. Any
use of it should remain as-is since we don't yet know what value will
replace it. We call this state "frozen" since it should not be modified
by identify. This requires a substitution step when we leave the scope
that introduced the `.Self`, to remove the flag. The flag is set in the
`EntityName` of the `SymbolicBinding`, and is part of the canonical
value, since `.Self` can become part of types, which are constants, and
the flag needs to follow it for correct behaviour.
We also have to ensure the flag is the same when doing comparison with
constants from inside a facet type and constants from outside. For
instance in `(Z where .Z1 = ()) where .Z2 = .Z1`, when we arrive at the
second `.Z1` its `.Self` will be frozen, while the `.Z1 = ()` contains a
non-frozen `.Self`. So we add the frozen flag to the first when storing
it in `where_stack` in order to compare the constant values of the two
`.Z1`.
The `WhereExpr` requirement inst kinds now have an `InstConstantKind` of
`AlwaysUnique` instead of `Never`. This allows us to add them to the
usual InstBlocks, and in an `eval fn` body they have a constant value,
so eval does not fail when trying to call that function. We have to be
careful to not consider `AlwaysUnique` as being actually concrete
though, since their constant value erases `.Self`-dependence. This
allows us to stop special casing them when thawing the requirements
block in a `WhereExpr`, and we can just thaw each `InstId` in the block
in a straightforward manner.
We add the new flag to the instruction's fingerprint and name in
formatted semir.
This adds SemIR structs and implements building `observe` lists, as well
as naming, formatting, and importing `observe` declarations.
---------
Co-authored-by: Dana Jansens <danakj@orodu.net>
This allows C++ to call Carbon functions with generic type parameters,
with some conditions. Example:
```carbon
interface I {
fn Doit(self);
}
class A {
impl as I { fn Doit(unused self) {} }
}
class B {
impl as I { fn Doit(unused self) {} }
}
fn F[T:! I](t: T) {
t.Doit();
}
inline Cpp '''
void G() {
Carbon::A a;
Carbon::B b;
Carbon::F(a);
Carbon::F(b);
}
''';
```
The initial support is limited; only explicit parameters are handled
currently.
`CarbonExternalASTSource::GetOrExportFunctionToCpp` now generates a
`clang::FunctionTemplateDecl` for generic Carbon functions. If C++ code
attempts to call that templated function,
`CarbonExternalASTSource::LoadExternalSpecializations` will be called
with the template argument types of that call site. Then we can generate
a specialized thunk for those argument types for C++ to call.