Fixes mangling collisions when two thunks with the same name (eg, `Op`)
are created in the same context, which in turn would lead to LLVM
verifier failures and miscompiles.
To support this, add a new value store to track a little more
information about thunks beyond what's in the `Function`.
This lets us stop eliding it in textual semir tests with dump ranges.
Previously it would always get elided, even though it was part of the
range being dumped, and was referred to by other instructions in the
dump range.
Since each `.Self` is unique (can change its type if not its value) in a
facet type, having each one distinct by location also aids
understanding.
When any field of a Carbon class is access from C++ for the first time,
all fields are exported as `clang::FieldDecl`s (this is necessary
because clang fields have an internal index that is initialized on first
use).
`ClangDeclStore` now provides bidirectional mapping. This allows looking
up a `ClangDeclId` by `InstId`, so when Carbon class fields are exported
they can be looked up that way.
See
[here](https://docs.google.com/document/d/1rWcueFwIfZox6GKVGxiUG4cBzjrZ6djXiIDGyJDtrE4/edit?tab=t.0)
for the design doc.
This also removes the default value of the `result_type_inst_id`
parameter of `HandleAction`, moves it before the action in the parameter
list, and documents it. This solves two problems:
- The default made it easy to forget, leading to unnecessary
`TypeOfInst` instructions.
- When it was present, putting it after the fairly "bulky" action
argument tended to make the callsite harder to read.
When importing a C++ function with an rvalue reference parameter, we
previously produced a Carbon value parameter. This would lead to the
toolchain believing it could pass the address of a non-expiring object
to the function, which would lead to a use-after-move.
Instead, we now map non-const rvalue reference parameters to Carbon
`var` parameters. This forces the object passed into C++ to be unique
and owned by the call. While that's not an exact match for C++ rvalue
reference parameters, given that it provides "always move" not
"conditionally move", it's the closest match we have at the moment.
When a namespace that was imported from C++ is indirectly imported, find
the corresponding namespace in the current C++ AST and return that
instead. This namespace may have completely different contents than the
one we found before; that's fine. The current file's view of a namespace
depends on what it imported.
Assisted-by: Gemini via Antigravity
When a class template specialization is indirectly imported, map the
template arguments into the importing File and find the corresponding
local class template specialization. This is a short-term fix:
eventually we should import the C++ AST from the imported file into the
C++ AST for the current file, but we're not ready to do that yet.
So far we only support very simple template arguments: just classes and
builtin types. Unfortunately we can't just map the C++ template
arguments to Carbon types, then import the Carbon types, then map them
back, because mapping from C++ template arguments to Carbon types would
require a `Check::Context` for the imported code, which we don't have.
As this is only a temporary workaround, directly mapping from one C++
AST to another will do for now.
Assisted-by: Gemini via Antigravity
Fixes a crash that would occur due to `scope_id` of the class being
unset.
Relands #7106 that was reverted by #7103 due to a github infrastructure
bug.
Co-authored-by: David Blaikie <dblaikie@gmail.com>
When importing Carbon code that refers to a C++ class, look for a
corresponding C++ class in the current context and import that instead.
This is a workaround for not having proper cross-file C++ import
support. For now, we only support non-templated namespace-scope class
types.
Assisted-by: Gemini via Antigravity
When creating the C++ thunk, make the parameters references if the
corresponding callee parameters are `ref`s.
When creating the Carbon thunk, tag the call arguments as `ref` if the
corresponding callee parameters are `ref`s.
Instead of treating all C++ code as coming from a single synthetic
`CheckIRId`, track the `SemIR::File` associated with each C++ location.
This is necessary since each `SemIR::File` has a distinct `CppFile` and
therefore distinct `SourceLocation`s and `ClangSourceLocId`s.
Assisted-by: Gemini via Antigravity
The FunctionDecl created for calling the Carbon thunk now takes a `self`
parameter for non-static methods, and the C++ thunk now passes an extra
argument for that `self` parameter when needed.
The CXXMethodDecl thunk created for calling methods now sets the storage
class appropriate depending on whether the method is static or not.
To reduce the number of parameters being passed around to thunk-building
functions, added a `FunctionInfo` struct and pass that around instead.
Add import/ and export/ under function/. Move most top-level tests to a
new basics/ with subdirectories for `import` directives and `inline
Cpp`. Add subdirectory for primitive type handling. Move all `reverse/`
tests to somewhere else, typically under an `export/` directory.
I split two test files up: constexpr.carbon got split into var/ and
function/ pieces, and reverse/simple.carbon was inlined into
namespace/export.carbon. The rest are just simple renames.
Instead of allowing lower to pick whatever type layout it desires,
compute the layouts of types as part of completing the type, and make
lower build types that match that representation.
For now we assume that all pointers are 64-bit, since we don't have
access to target information. We allow tail padding reuse for structs
and tuple types (and by extension, for classes, since they use structs
as their object representation), but not for arrays.
In order to build matching LLVM types, we create LLVM packed structs
where necessary, and we insert inter-field padding on the end of the
previous field so that GEP indexes still always match Carbon's
ElementIndexes.
We don't yet use the computed alignment much in LLVM IR generation -- in
particular, `alloca`s, `load`s, and `store`s should probably use the
computed type alignment, but don't.
Assisted-by: Gemini via Antigravity
These turn up frequently in real-world code, for example when converting
a mutable global `Cpp.std.string_view` to a `Cpp.std.string`. Only
reject a non-constant call if the callee is `consteval`, not if it's
`constexpr`.
For calling non-`()` functions, the Carbon->Carbon thunk now takes an
extra reference parameter and writes the target function's return value
out to that parameter. (At the SemIR level this is how returns already
work, but adding this extra reference parameter is needed so that the
function is lowered correctly.) The C++ thunk now creates a local
variable to be initialized by the Carbon thunk, and then returns that
value to the original C++ caller.
Allow any type that has a mapping from Carbon to C++ to be exposed to
C++ via name lookup. This also exposes the logic to export Carbon
classes to C++ to apply during type mapping, which gives very slight
support for passing Carbon types to C++ functions from Carbon, but not
really enough to sensibly test yet.
Depends on #7042.
Instead of exporting a class or namespace each time a new C++ name
lookup discovers it, track that we have exported the entity on its name
scope, and if a new name lookup finds the same entity, produce the same
clang declaration.
Also declare them `inline` since we're putting the `always_inline`
attribute on them. Use the `internal_linkage` attribute rather than
`SC_Static` since it's a more precise mechanism and matches what we do
for static member functions in reverse interop (where `SC_Static` means
something else and would not give the function internal linkage).
We don't yet populate the bases or fields, so the class types show up as
empty classes in C++ for now. But we do allow calls to static member
functions.
This works by generating two thunks, one in C++ and one in Carbon. For
example, given this input:
```c++
// Carbon:
fn Callme(f: f32) {}
// C++:
void F() {
// This will call `Callme__cpp_thunk`
Carbon::Callme(1.0);
}
```
These functions are generated:
```c++
// Carbon:
fn Callme__carbon_thunk(ref f: f32) {
// Call the target function.
Callme(f);
}
// C++:
// C++ declaration for the Carbon thunk.
void Callme__carbon_thunk(float& f);
void Callme__cpp_thunk(float f) {
// Call the Carbon thunk with args passed by reference.
Callme__carbon_thunk(f);
}
```
For now, all arguments are passed by reference, even if they are simple
types like pointers or i32.
Functions with non-void return types are not supported yet.
Use the same C++ -> Carbon map for both interop directions, and when
importing an entity from Carbon -> C++, check whether it was originally
a C++ entity and if so return the original.
Assisted-by: Gemini via Google Antigravity
This is already allowed as a builtin conversion, but the impl allows the
generics system to know about it, so that conversions like
`Optional(T*)` to `Optional(const T*)` are allowed. This in turn allows
a C++ `T*` to be implicitly converted to a C++ `const T*` in Carbon
code.
Instead of recursing back into Convert, make CppThunkRef conversion just
add an extra step to category conversion, performing a copy conversion
followed by an ephemeral reference binding conversion.
This is only fixing the decision about *whether* to produce a witness.
Implementation of the witness is still a TODO, though where a body is
generated, it should also precisely reflect where one _needs_ to be
generated.
Note the tests:
- toolchain/lower/testdata/function/generic/import_core_witness.carbon
- toolchain/lower/testdata/function/generic/import_unused_def.carbon
These tests can probably be produced _without_ Core.Destroy, but I found
the essence of them while trying to build //examples with Core.Destroy
and a simpler minimization wasn't striking me.
Assisted-by: Google Antigravity with Gemini
---------
Co-authored-by: jonmeow <jperkins@google.com>
Remove special-case handling in conversion logic for C++ enum types,
synthesize a custom witness of `Core.Copy` using the `primitive_copy`
builtin function.
This makes it possible to do const eval when calling a constexpr C++
function with params and return types other than 32/64-bit integers.
Most of the new logic is in `MaybeModifyCppThunkCallForConstEval`, which
is called by `MakeConstantForCall`. This checks if the callee is a C++
thunk (using a new `SpecialFunctionKind::CppThunk` variant), and if so
it:
* Changes the callee from the C++ thunk to the thunk's callee
* Remaps parameters that are passed by pointer to the thunk to the
underlying value
* Drops the return value parameter, if present
For #6830, add support for inline C++ fragments as a declaration rather
than as a packaging directive. For now, this uses `inline Cpp
<string-literal>;` as syntax. The prior `import Cpp inline
<string-literal>;` is left alone for the time being. We can decide
separately whether to remove that.
`inline Cpp` requires that there was at least one `import Cpp`. It's not
clear to me if that's the right design long-term, but it seems
reasonable for now.
Assisted-by: Gemini via Google Antigravity
This will be used for const-evaling functions. Splitting into a separate
commit since it touches a lot of test files, and a couple fail_todo
tests are no longer failing.
Start recording the clang::DeclContext* -> InstId mapping for use in
later operations.
The test update includes removing the initial fail_* test because I
hadn't thought about the use of namespace aliases as a way to test for
the presence of a namespace without the failure caused by not finding
the thing inside the namespace.
---------
Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Initialize the `Diag` field `EvalResult` to get notes from clang when
`EvaluateAsConstantExpr` fails, then emit them using clang's diagnostic
infrastructure.
Also set valid source locations in a couple places, otherwise clang's
diagnostics code crashes.
This commit creates an instance for any associated types in an interface
with a custom witness table. This unlocks interfaces designed for C++
interop that rely on arbitrary return types. For example,
`CppUnsafeDeref` becomes usable as of this commit.
This commit may have also implemented support for non-type associated
constants, but since we're lacking a practical test case, they're still
marked as TODO for the time being.
---------
Co-authored-by: Dana Jansens <danakj@orodu.net>
Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Instead of injecting code to declare an `operator new`, generate AST for
it directly. In order to use this, directly generate a `CXXNewExpr`
rather than asking Clang to build one.
This is less of a hack, and doesn't visibly leak an `operator new`
declaration that inline C++ code or template instantiations might see.
It also avoids generating a warning in C++26 and later that the
`constexpr` declaration of `operator new` is used but not defined.
Assisted-by: Gemini 3.1 Pro via Antigravity