Commit Graph
22 Commits
Author SHA1 Message Date
Geoff RomerandRichard Smith e5b05a1fac ExprCategory for guaranteed-in-place initializing expressions (#6623)
The primary change in this PR is to split the `Initializing` expression
category into separate `ReprInitializing` and `InPlaceInitializing`
categories, depending on whether initialization uses the types
initializing representation, or is guaranteed to be in place. It also
rationalizes and documents the SemIR-level semantics of those categories
(including where #5545's "ephemeral entire reference" category will
fit), and introduces two new inst kinds to close gaps exposed in the
process.

Some additional secondary changes:
- Consistently format the storage arguments of initializers with `to`,
regardless of whether initialization is in-place, and document the `to`
notation.
- Rename some inst kinds and functions, and restructure some of the
code, for clarity and consistency with the new documentation.
- Resolve a TODO to handle more category conversions in
`CategoryConverter`, in order to make it easier to reason about category
conversions.

See #6588 and the review history of this PR for background.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
2026-02-04 02:27:12 +00:00
Geoff Romer 4329a83e4c Form-aware textual format for return parameters and arguments (#6588)
The key changes are:
- Function output parameters are now prefixed with `out`, and more
consistently formatted as named parameters.
- Function and inst output arguments are now written as part of the inst
form, rather than as one of the inst arguments.

As a drive-by fix, this also changes `Temporary::storage_id` from
`DestInstId` to `InstId`, because it doesn't represent an output
parameter of the `Temporary` inst itself.

See the review of
[#6532](https://github.com/carbon-language/carbon-lang/pull/6532) and
[this Discord
discussion](https://discord.com/channels/655572317891461132/999638000126394370/1458268977020141589)
for additional background.
2026-01-14 23:27:21 +00:00
Geoff Romer e1ec8d42d1 Give ReturnExpr a target only when initialization is in-place (#6570)
Also clarify and enforce that `ConversionTarget::init_id` is used only
as storage for in-place initialization, and correspondingly rename it to
`storage_id`.
2026-01-13 01:20:15 +00:00
Geoff Romer 6985ecb1d4 Replace GetCurrentReturnSlot with GetReturnedVarParam (#6571)
Not all functions have a return slot, and once we have composite forms,
functions will be able to have any number of return slots. Obtaining a
unique return slot for a function only makes sense in `returned var`
handling.
2026-01-10 01:56:32 +00:00
Jon Ross-Perkins c5eba90317 Change Destroy to use a CustomWitness instead of a blanket impl (#6512)
Pursuant to recent decisions on #6124, switch `Destroy` to use a
`CustomWitness` for its implementation. Right now this is manufacturing
no-op implementation functions on each lookup, which obviously isn't
ideal but is intended as a first pass. I'm mostly trying to find the
right balance between updating the approach to reflect new decisions,
while still breaking apart work in a way.

The `CoreInterface` logic is intended to build on `CoreIdentifier`
support. We have a number of additional interfaces that require
specialized logic, and that'll extend pretty far with C++ interop, so it
seemed easiest to have a generic function for it. That's what's
replacing the logic inside C++ interop that was doing string comparisons
(which could have already been moved to `CoreIdentifier`, I just missed
it in my first pass).

This adds `CustomWitness` support because the `Destroy` witnesses can be
imported cross-file. `CustomWitness` was previously only used for C++
types, which don't yet support import, which is why that wasn't
previously an issue. The addition of `query_specific_interface_id` is
similarly needed in order to get correct sorting of witness blocks when
imported.

This PR also removes builtin constraint logic (note this is in a
separate commit to help review; it's not a separate PR because it's
difficult to split apart without tests breaking). This had been made
generic with the expectation that destroy, copy, move, and conversions
would all need related support. Under the new decision, we are not going
to do blanket impls and will instead just manufacture a `CustomWitness`
for everything.

A lot of SemIR fingerprints change, but that's probably because the
addition of `Destroy` on core classes is yielding structural changes.
2025-12-19 18:36:06 +00:00
Jon Ross-Perkins 47e551141f Change the package namespace to use the package name (#6495)
Instead of naming the root namespace `package` (because it's accessed by
the `package` keyword), change it to use the current package name. Note,
buried in the checksum changes,
`toolchain/check/testdata/package_expr/fail_not_found.carbon`:

```
-  // CHECK:STDERR: fail_not_found.carbon:[[@LINE+4]]:16: error: member name `x` not found in `package` [MemberNameNotFoundInInstScope]
+  // CHECK:STDERR: fail_not_found.carbon:[[@LINE+4]]:16: error: member name `x` not found in `Main` [MemberNameNotFoundInInstScope]
```

for:

```
  // CHECK:STDERR:   var y: i32 = package.x;
  // CHECK:STDERR:                ^~~~~~~~~
```

I'll leave it to you if you prefer this; the alternative I see is to
just rename `IsCorePackage` to `IsImportedCorePackage`, and/or change it
to a helper that takes a `Context` and does the right thing with
`parse_tree` (which, I need for `Destroy`-related reasons and was my
default approach).
2025-12-16 01:33:53 +00:00
Richard Smith 154e4012c4 Include the parent scope when fingerprinting an entity name. (#6473)
This is a prerequisite for support for interop with C++ template names.
No behavior change here, except that it sadly changes the fingerprinting
for a lot of tests.
2025-12-08 15:31:13 +00:00
Geoff Romer 0811d996e1 Finish renaming BindName and related insts. (#6281)
Resolves the TODO from #6235
2025-10-28 17:17:38 +00:00
Richard Smith 6011040481 Rework handling of C++ references. (#6268)
For now, map C++ reference types to const-qualified Carbon pointer types
rather than picking between a (non-const) pointer or a value type. This
fixes misbehavior in lowering for reference members in classes and
reference return types.

Update the special-case handling for references as function parameters
so that it continues to map const reference parameters to Carbon
pass-by-value, and unify the code paths for `self` parameters and other
parameters, which were mostly doing the same thing but had some subtle
differences.

Add references to the list of types that we can pass to and from C++
directly, without needing an additional layer of thunks.
2025-10-28 00:33:26 +00:00
Richard Smith 304d2056cc Map nullable C++ pointer types to Core.Optional(T*). (#6230) 2025-10-16 20:52:32 +00:00
Boaz Brickner 1ac1d11063 C++ Interop: Support reference types in fields and globals (#6187)
Implemented by generalizing the reference type support for parameters
and return values to other use cases.
The changes to the `method.carbon` test are due to to supporting the
reference types but not supporting the necessary conversions.

C++ Interop Demo:

```c++
// global.h

struct C {
  int member = 0;
  int& member_ref = member;
};

extern C& global;
```

```c++
// global.cpp

#include "global.h"

static C static_c;

C& global= static_c;
```

```carbon
// main.carbon

library "Main";

import Core library "io";

import Cpp library "global.h";

fn Run() -> i32 {
  Core.Print(Cpp.global->member);
  ++(*Cpp.global->member_ref);
  Core.Print(Cpp.global->member);
  ++(*Cpp.global->member_ref);
  Core.Print(Cpp.global->member);
  return 0;
}
```

```shell
$ clang++ -stdlib=libc++ -c global.cpp
$ bazel build toolchain:carbon && bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link global.o main.o --output=demo
$ ./demo
0
1
2
```

**Without this change**:
```shell
main.carbon:10:14: error: semantics TODO: `Unsupported: var type: C &`
  Core.Print(Cpp.global->member);
             ^~~~~~~~~~
main.carbon:10:14: note: in `Cpp` name lookup for `global`
  Core.Print(Cpp.global->member);
             ^~~~~~~~~~
```

Part of #6006 and #6186.
2025-10-10 23:15:31 +00:00
Jon Ross-Perkins 47081be67a Reduce test sensitivity to small import loc changes (#6145)
Locations are similarly fragile, because adding a comment changes them.
This has made me pause when making prelude changes in #6144, so dropping
them for those cases.

Instruction ids aren't actually that interesting outside debugging, and
can be churny when doing other structural changes. I've seen this in
particular when doing singleton changes, which bump every instruction
id.

Note there are still other ways fragility from locations can crop up.
This shouldn't be considered a complete fix, but hopefully a small
improvement.
2025-09-30 17:20:14 +00:00
Dana JansensandRichard Smith 82679e6689 Make BindSymbolicName the canonical form of a FacetValue wrapping the BindSymbolicName (#6107)
If a `BindSymbolicName` is converted to `type` and then to its exact
`FacetType`, we get a `FacetValue` wrapping the `BindSymbolicName` but
providing no different information: it has the same witnesses and
`FacetType` as the original `BindSymbolicName`. Yet it is a different
constant value, creating multiple canonical forms with the same meaning.
Now we make that `FacetValue` with the same `FacetType` as the
`BindSymbolicName` it wraps evaluate back to the `BindSymbolicName`,
making it the unique canonical form.

This makes the "shortcut" in convert for avoiding impl lookup when
converting from `FacetAccessType` to `FacetType` in this exact scenario
work the same as doing the full impl lookup.

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
2025-09-23 22:11:11 +00:00
Jon Ross-PerkinsandDana Jansens 5e3bb523f8 Add builtin functions for destroy, with special requirements in facet types (#6035)
This is in support of a goal of changing the blanket `destroy` impl to
use (roughly):

```
private fn CanAggregateDestroy() -> type = "type.can_aggregate_destroy";

// Handles aggregate type destruction.
impl forall [AggregateDestroyT:! CanAggregateDestroy()] AggregateDestroyT as Destroy {
  fn Op[addr self: Self*]() = "type.aggregate_destroy";
}
```

That isn't done here because there's still other issues that migrating
raises. What this *does* do is add the builtin functions, and in
particular, support to `FacetTypeInfo` to make `CanAggregateDestroy`
work.

The "special requirement" approach in `FacetTypeInfo` allows us to
support restricting a blanket impl under the current approach of impls.
Maybe we'll find a cleaner approach that can work in the future, but
this fits into the current model by propagating similar to other
requirements. I'm using an enum mask because we have a number of similar
things to add (e.g. copy, move) but I'm not sure we need a full vector.

A few alternatives considered were:

- Supporting syntax more like `where .Self impls
TypeCanAggregateDestroy(.Self, SupportedInterface,
UnsupportedInterface)`. I think it'd be a little cleaner, but requires
better compile-time evaluation in order to assess the type of the call.
Right now it's expected to be a `FacetType` too early to make this work,
and I was concerned about pouring too much more time down this route.
- Providing an actual interface, in particular doing name lookup back
into `Core.` for an interface. This would've added name lookup overhead,
and the question of whether an `impl` exists.
- Generating an interface. This avoids the name lookup, but would still
raise the question of whether an `impl` should also be generated. Work
I've previously done generating interfaces for class destruction also
feels complex to both write and understand (an unfortunate issue).
- Still modeling as an `ImplsConstraint`, for example by defining a
special `InterfaceId::CanAggregateDestroy = -2` similar to what we do on
other ids. I was hesitant because of how this expands the number of
modes of `InterfaceId`, and things for consuming code to watch out for,
for what feels like a relatively niche set of use-cases that are only
interface-like.

---------

Co-authored-by: Dana Jansens <danakj@orodu.net>
2025-09-15 17:03:43 +00:00
Richard Smith d60900cbeb Remove special case for returning value expressions by copy (#6052)
When returning a value from a function whose return type has a by-copy
initializing representation, perform initialization like we do when the
return type has an in-place initializing representation. This makes our
SemIR representation more uniform, as the return expression will now
always be an initializing expression rather than a value expression, but
more importantly it means that attempts to return a non-copyable type by
value now fail, even if the type has a by-copy initializing
representation.

This catches a bunch of places where we were returning a value of an
unconstrained template parameter `T:! type`, which we were incorrectly
allowing because we didn't notice it was not copyable. Unfortunately
this then requires quite a few test updates.

Like #6034, this exposes a lowering issue where lowering crashes when
attempting to lower a specific copy operation for certain types; a
couple more tests are temporarily disabled here. An upcoming PR
dependent on this one will fix the issue and re-enable those tests.
2025-09-12 00:13:33 +00:00
Richard SmithandGeoff Romer 1ec8ac7ef9 Add Copy interface and use it for making copies. (#6034)
Instead of hardcoding which types are copyable, add a `Core.Copy`
interface to perform copying. Move almost all the current copy support
to that interface. Some remaining pieces are still using builtin logic
after this PR:

* For tuples and structs, builtin logic is used to perform elementwise
copies. This also supports copying *adapters of* tuples and structs,
which seems like it may not be desirable, especially for non-extending
adapters. A `Copy` impl is provided for tuples of at most 2 elements, so
that `Core.Copy` constraints are satisfied, but we can't implement this
generally until we have variadics support, and don't yet have a
mechanism to generalize this to structs.
* For `enum` types imported from C++, builtin logic is used to perform a
copy. This is temporary until we have a mechanism to identify these
types from an impl in the prelude.

One lowering test in `toolchain/lower/testdata/class/generic.carbon` is
disabled for now, as it causes a crash in the lowering code due to an
ABI mismatch between the call signature in the lowered declaration of a
specific function and the call that is generated in the specific callee.
Fixing this is a little involved, and will be done in a separate PR.

---------

Co-authored-by: Geoff Romer <gromer@google.com>
2025-09-10 23:55:55 +00:00
Boaz Brickner 8adb3570ac C++ Interop: Add support for char (#5988)
Added tests for different character types.
`char` is currently not in primitives prelude, so had to use full
prelude.

C++ Interop Demo:

```carbon
// main.carbon

library "Main";

import Cpp inline '''
auto output_char(char c) -> void {
  printf("%c", c);
}
''';

fn Run() -> i32 {
  let msg: array(Core.Char, 13) =
      ('H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n');
  for (c: Core.Char in msg) {
    Cpp.output_char(c);
  }
  return 0;
}
```

```shell
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link main.o --output=demo
$ ./demo
Hello world!
```

Part of https://github.com/carbon-language/carbon-lang/issues/5263.
2025-08-29 01:11:24 +00:00
Richard SmithandJon Ross-Perkins b2b0b4a73f Improve recovery from bad type imports. (#5953)
The main change here is that a bad type appearing somewhere within a
field or base class of a class shouldn't cause an import of that class
to fail. Instead, only that field or base class becomes inaccessible
from Carbon.

Also improve the way that type importing errors are diagnosed. While we
lose the precision of a diagnostic saying why a type is not supported,
we gain a useful source location for where the type was mentioned in C++
code.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
2025-08-13 22:07:54 +00:00
Richard Smith ae16014df8 Don't import a C++ class definition until the class is required to be complete. (#5865)
When importing a class definition, ask Clang to complete it first. This
causes class template specializations to get instantiated as needed when
the type-checking of Carbon requires a C++ class to be complete. It also
allows Clang to implement things like modules-aware definition
visibility checking.

Don't reject importing a class with a virtual base if it's never
required to be complete. Instead, defer diagnosing until the definition
is required.

This also removes the recursion from `MapType`, as mapping a class type
no longer maps its definition.

In order to get diagnostics from instantiation failures, fix a bug that
caused any Clang diagnostics produced after the initial building of the
`ASTUnit` to get discarded. This exposed some duplicate diagnostic
issues in `ImportNameFromCpp` which are fixed here too.
2025-07-30 23:34:18 +00:00
Boaz Brickner 6a3e222fb7 Don't ignore SemIR ranges in C++ interop tests (#5877)
The non failing tests already define ranges.
2025-07-30 17:17:37 +00:00
Richard Smith 0d74162e2a Support C++ import for anonymous struct and union members. (#5855) 2025-07-28 20:26:24 +00:00
Richard Smith 36f0a73092 Initial support for interop with class/struct/union fields. (#5849)
Add a new type, `custom_layout_type`, representing a struct type whose
size, alignment, and field offsets can be manually controlled. Use this
as the object representation type for imported C++ class types (which
also includes struct and union types), allowing us to model C++ class
type layouts. In passing, also add support for incomplete C++ class
types, mapping them into incomplete Carbon class types.

Map C++ fields into Carbon field declarations, allowing direct access to
C++ fields from Carbon. So far, no support is added for base classes nor
anonymous struct or union declarations; those will be added in
subsequent PRs. Also, we don't map C++ access control into Carbon yet,
so all C++ fields are accessible regardless of their access control.

For now we still use a `struct_type` as the object representation for
empty C++ classes, in order to continue to support our existing tests
that convert `{}` to empty C++ class types. This is temporary and should
be removed once we support interop with C++ class initialization.
2025-07-25 21:09:24 +00:00