Commit Graph
74 Commits
Author SHA1 Message Date
Jon Ross-Perkins 9704dc670e Change the Destroy blanket impls to be more specific (#6098)
The main direction of this change is the edits to `destroy.carbon`
(matching in both prelude and min_prelude).

Previously there was a no-op blanket impl for `Destroy`, which hid all
missing implementations of `Destroy`. This does a few things:

- Sets up builtin aggregate destruction for struct and tuple types as
before, but also adds C++ class types and array types to the same
handling. (all as a TODO for actual implementation)
- Also maybe-unformed destruction, for now at least. (there's a chance I
may try a different approach on this, but the impl lookup wasn't working
as I'd hope in order to write it in code)
- Adds handlers for simple things that are easy to do in code: `type`,
`bool`, pointers. (because these are no-op destruction)
- Redirect `const T` destruction to `T` destruction.

This leaves as future issues:

- `partial T` destruction. (this can't be done similar to `const`
because it only works for non-`final` class types; I think `class`
definitions should just generate what's needed)
- Destruction of other prelude-provided types. (will probably come up as
we implement class destruction, that the adapted builtin type doesn't
implement `Destroy` -- but may end up special-casing that in a way that
moots it)

This moves the `&` operator from `facet_types.carbon` to
`convert.carbon` because more things need to handle type and now that
we're getting separate copy and destroy interfaces. It should be
low-cost (an interface and builtin) so hopefully this is the right
balance for complexity and re-use.

A few tests are also edited in order to focus them more on what they
intend to test, and avoid a `Destroy` dependency.
2025-09-18 22:10:50 +00:00
Boaz Brickner 868c4b768c C++ interop: Don't crash when looking up names inside an incomplete C++ class/struct/union (#6096)
Only do the lookup when the class is complete.

Before this change we crash in `Sema::LookupQualifiedName()` on
`Declaration context must already be complete!`.
2025-09-18 22:03:42 +00:00
Richard Smith 1e47f29963 Add reference support to C++ interop. (#6082)
For now this works as follows:

* `T&&` is mapped to a by-value `param: T` parameter.
* `T&` is mapped to an `addr param: T*` parameter.

In either case, we will generate a thunk, which will internally pass the
parameter as a pointer.
2025-09-17 02:03:22 +00:00
Richard Smith 65a7e50037 Instantiate C++ templates at end of file. (#6084)
Mark C++ functions as used when overload resolution selects them, and
trigger Clang's end-of-TU processing at the end of the Carbon
compilation to perform instantiation and other pending cleanup steps.
2025-09-17 01:06:47 +00:00
Richard Smith 9d84391f11 Support for passing pointers to function templates. (#6080)
When a function template takes a parameter of deduced type, and we
deduce that type to a pointer type because we passed a Carbon pointer as
the argument, don't complain that the deduced type is not nullable. We
still know that it can't be null, because we deduced it from a
non-nullable type.
2025-09-16 23:39:07 +00:00
Richard Smith b054e3d2b0 Overload resolution support for more kinds of candidate. (#6071)
* Add support for template candidates by calling the suitable
`AddCandidate` function for them.
* Add support for overloading on `*this` qualifiers by calling
`AddMethodCandidate` when appropriate.
* Make mapping from Carbon arguments to Clang arguments a little more
faithful by mapping the Carbon expression category into the Clang value
kind.
2025-09-16 21:57:32 +00:00
Richard Smith bac828d244 Add support for char keyword per #5903. (#6078)
Make inst namer and stringify print `Core.Char` and `Core.String` as
`char` and `str` respectively. Plus a few cleanups.
2025-09-16 20:45:01 +00:00
Richard Smith b44ba47cf3 Don't treat dependent types as having a copy value representation. (#6055)
Add `Dependent` value and initializing representations for types whose
representations are unknown because they are dependent. When generating
SemIR in such cases, use a worst-case initializing representation that
both provides a destination address and also propagates a potential
result value.

Use this to fix incorrect lowering and lowering crashes for specific
functions involving generic types that don't use a copy value
representation.

In lowering, be careful to distinguish between whether the initializing
representation for the generic return type uses a return slot (which
affects whether the SemIR declaration and call have one) and whether the
initializing representation for the specific return type uses a return
slot (which affects whether the LLVM IR declaration and call have one).
2025-09-15 23:59:00 +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
Ivana Ivanovska 12ddfb9c7c [Carbon/C++ interop] Add support for C++ overloaded functions (#5891)
As proposed in [Carbon: C++ interop for overloaded functions and
function
templates](https://docs.google.com/document/d/1KUxumZtNe3mY3TsjW2s_ZADOlAaFlrtsLKHVILtqIaM/edit?tab=t.0),
Clang is used to perform the overload resolution using C++ rules, when
an overloaded C++ set is called from Carbon. Once a function is
selected, it's converted into a Carbon function and called using the
Carbon rules including argument conversions.

A single non-templated function is treated the same way as an overload
set and the same rules apply for its call.
Template functions are not supported yet.

Demo:

a) Non-templated function calls:

```c++
// --- overloads.h

auto foo(int a, short b) -> void;
auto foo(double a) -> void;
auto foo(int a) -> void;
```

```c++
// overloads.cpp

#include "overloads.h"
#include <cstdio>

auto foo(int a, short b) -> void {
  printf("hello from foo_int_short(%d, %d) \n", a, b);
}
auto foo(double a) -> void { printf("hello from foo_double(%f) \n", a); }
auto foo(int a) -> void { printf("hello from foo_int(%d) \n", a); }
```
```c++
library "Main";

import Cpp library "overloads.h";

fn Run() -> i32 {
  Cpp.foo(1.1 as f64);
  return 0;
}
```
```
$ clang -c overloads.cpp 
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link overloads.o main.o --output=demo
$ ./demo
hello from foo_double(1.100000) 
```

b) Constructors:
```c++
// --- constructor_overloads.h
class C {
 public:
  C();
  C(int a, int b);
};
```

```c++
// constructor_overloads.cpp
#include "constructor_overloads.h"
#include <cstdio>

C::C() { printf("hello from C() \n"); }
C::C(int a, int b) { printf("hello from C(%d, %d) \n", a, b); }
```
```c++
library "Main";

import Cpp library "constructor_overloads.h";

fn Run() -> i32 {
  let c1: Cpp.C = Cpp.C.C();
  let c2: Cpp.C = Cpp.C.C(1, 2);
  return 0;
}
```

```
$ clang -c constructor_overloads.cpp 
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link constructor_overloads.o main.o \--output=demo
$ ./demo
hello from C() 
hello from C(1, 2) 
```


Follow-ups:

- `Cpp.foo({})` - proper handling of struct literals as call args.
- Fix access for overloaded sets.
- Fix tests:
- Method calls: `error: missing object argument in method call
[MissingObjectInMethodCall]` in tests.
    - Fix `toolchain/check/testdata/interop/cpp/import.carbon` test.
    - Fix `enums` support.
    - Fix `str` -> `std::string_view` mapping.


Part of #5915
2025-09-15 12:29:49 +00:00
Boaz Brickner 508a88e2a9 C++ inteop: Set type source info for a generated C++ thunk function (#6049)
This prevents a null pointer access crash when generating a thunk with
an automatically deduced trivial return type.
In this case, Clang calls `Sema::DeduceFunctionTypeFromReturnExpr()`
which calls `Sema::getReturnTypeLoc()`, which requires this information.

Part of #5514.
2025-09-12 07:11:36 +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
Richard Smith e8cd229e74 When performing an impl lookup, only import impls for related interfaces. (#6040)
This avoids impl lookups involving, say, `Core.Int` pulling in all ~65
impls in "prelude/types/int", which resulted in a lot of unnecessary
importing work, followed by a lot of unnecessary inst namer and inst
formatter work.

Before:
```
Ran 1335 tests in 6186 ms wall time, 146818 ms across threads
  Slowest tests:
  - toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon: 5611 ms, 5532 ms in Run
  - toolchain/check/testdata/interop/cpp/function/operators.carbon: 2034 ms, 1981 ms in Run
  - toolchain/check/testdata/primitives/import_symbolic.carbon: 1796 ms, 1786 ms in Run
  - toolchain/lower/testdata/operators/arithmetic.carbon: 1729 ms, 1728 ms in Run
  - toolchain/lower/testdata/function/generic/call_recursive_sccs_deep.carbon: 1700 ms, 1697 ms in Run
[==========] 1335 tests from 1 test suite ran. (682 ms total)
```

After:
```
Ran 1335 tests in 2419 ms wall time, 109587 ms across threads
  Slowest tests:
  - toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon: 1748 ms, 1665 ms in Run
  - toolchain/check/testdata/interop/cpp/function/operators.carbon: 1106 ms, 1057 ms in Run
  - toolchain/lower/testdata/function/generic/call_recursive_diamond.carbon: 1044 ms, 1041 ms in Run
  - toolchain/lower/testdata/function/generic/call_recursive_sccs_deep.carbon: 1015 ms, 1012 ms in Run
  - toolchain/lower/testdata/operators/arithmetic.carbon: 998 ms, 997 ms in Run
[==========] 1335 tests from 1 test suite ran. (652 ms total)
```

That's still slower than it should be, but a large improvement
nonetheless.

Fixes #6029
2025-09-10 21:40:27 +00:00
Boaz Brickner d6fbe3c663 C++ interop: Support importing operators defined in namespaces (#6024)
C++ Interop Demo:

```c++
// my_number.h

namespace MyNamespace {

class MyNumber {
 public:
  explicit MyNumber(int value) : value_(value) {}
  auto value() const -> int { return value_; }

 private:
  int value_;
};

auto operator+(MyNumber lhs, MyNumber rhs) -> MyNumber;

}  // namespace MyNamespace
```

```c++
// my_number.cpp

#include "my_number.h"

namespace MyNamespace {

auto operator+(MyNumber lhs, MyNumber rhs) -> MyNumber {
  return MyNumber(lhs.value() + rhs.value());
}

}  // namespace MyNamespace
```

```carbon
// main.carbon

library "Main";

import Core library "io";
import Cpp library "my_number.h";

fn Run() -> i32 {
  let n1: Cpp.MyNamespace.MyNumber = Cpp.MyNamespace.MyNumber.MyNumber(5);
  Core.Print(n1.value());
  let n2: Cpp.MyNamespace.MyNumber = Cpp.MyNamespace.MyNumber.MyNumber(7);
  Core.Print(n2.value());
  let n3: Cpp.MyNamespace.MyNumber = n1 + n2;
  Core.Print(n3.value());
  return 0;
}
```

Before this change:
```
$ bazel-bin/toolchain/carbon compile main.carbon
main.carbon:13:38: error: cannot access member of interface `Core.AddWith(Cpp.MyNamespace.MyNumber)` in type `Cpp.MyNamespace.MyNumber` that does not implement that interface
  let n3: Cpp.MyNamespace.MyNumber = n1 + n2;
                                     ^~~~~~~
```

With this change:

```shell
$ clang -c my_number.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link my_number.o main.o --output=demo
$ ./demo
5
7
12
```

Part of https://github.com/carbon-language/carbon-lang/issues/5995.
2025-09-10 14:03:09 +00:00
Jon Ross-Perkins 0518fdebbc Fix potential fingerprint conflict in constraints (#6033)
This uses each vector's size as a barrier between lists, to eliminate
the possibility of incidental collisions between entries of different
lists. This is the same as is done inside `AddBlock`.
2025-09-09 21:44:03 +00:00
Boaz Brickner 56adfa20ce C++ interop: Add a test for calling an operator on an inner class (#6030)
This currently works and I'd like to keep it that way when adding
operators in namespace support.

Part of #5995.
2025-09-09 13:06:55 +00:00
Boaz Brickner 471b394c6d C++ interop: Support unary operators (#6020)
Newly supported: `-`.
Partially supported due to lack of reference support: `++` (prefix),
`--` (prefix).
Not supported due to lack of Carbon support to call them correctly: `+`,
`++` (postfix), `--` (postfix), `~`, `!`, `&`, `*`, `->`.

Also (for consistency):
* Add the operator declarations to unsupported binary operators tests.
* Logical operators and the unary `operator&` (address of) are expected
to be called by explicitly calling `operatorX`.

C++ Interop Demo:

```c++
// my_number.h

class MyNumber {
 public:
  explicit MyNumber(int value) : value_(value) {}
  auto value() const -> int { return value_; }

 private:
  int value_;
};

auto operator++(MyNumber operand) -> MyNumber;
auto operator--(MyNumber operand) -> MyNumber;
auto operator-(MyNumber operand) -> MyNumber;
```

```c++
// my_number.cpp

#include "my_number.h"

auto operator++(MyNumber operand) -> MyNumber {
  return MyNumber(operand.value() + 1);
}

auto operator--(MyNumber operand) -> MyNumber {
  return MyNumber(operand.value() - 1);
}

auto operator-(MyNumber operand) -> MyNumber {
  return MyNumber(-operand.value());
}
```

```carbon
// main.carbon

library "Main";

import Core library "io";
import Cpp library "my_number.h";

fn Run() -> i32 {
  var num: Cpp.MyNumber = Cpp.MyNumber.MyNumber(14);
  Core.Print(num.value());
  ++num;
  Core.Print(num.value());
  --num;
  Core.Print(num.value());
  num = -num;
  Core.Print(num.value());
  return 0;
}
```

```shell
$ clang -c my_number.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link my_number.o main.o --output=demo
$ ./demo
14
14
14
-14
```

Part of https://github.com/carbon-language/carbon-lang/issues/5995.
2025-09-08 15:39:08 +00:00
Boaz Brickner ee42b2db93 C++ interop: Support more binary operators (#6017)
Already supported: `+`.
Newly supported: `-`, `*`, `/`, `%`, `&`, `|`, `^`, `<<`, `>>`, `==`,
`!=`, `<`, `>`, `<=`, `>=`.
Partially supported due to lack of reference support: `+=`, `-=`, `*=`,
`/=`, `%=`, `&=`, `|=`, `^=`.
Not supported due to lack of reference support: `<<=`, `>>=`.
Not supported (I think Carbon doesn't want overloading these): `&&`,
`||`.

C++ Interop Demo:

```c++
// my_number.h

class MyNumber {
 public:
  explicit MyNumber(int value) : value_(value) {}
  auto value() const -> int { return value_; }
  void set_value(int value) { value_ = value; }

 private:
  int value_;
};

// Arithmetic
auto operator+(MyNumber lhs, MyNumber rhs) -> MyNumber;
auto operator-(MyNumber lhs, MyNumber rhs) -> MyNumber;
auto operator*(MyNumber lhs, MyNumber rhs) -> MyNumber;
auto operator/(MyNumber lhs, MyNumber rhs) -> MyNumber;
auto operator%(MyNumber lhs, MyNumber rhs) -> MyNumber;

// Bitwise
auto operator&(MyNumber lhs, MyNumber rhs) -> MyNumber;
auto operator|(MyNumber lhs, MyNumber rhs) -> MyNumber;
auto operator^(MyNumber lhs, MyNumber rhs) -> MyNumber;
auto operator<<(MyNumber lhs, int shift) -> MyNumber;
auto operator>>(MyNumber lhs, int shift) -> MyNumber;

// Compound Arithmetic
auto operator+=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull;
auto operator-=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull;
auto operator*=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull;
auto operator/=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull;
auto operator%=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull;

// Compound Bitwise
auto operator&=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull;
auto operator|=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull;
auto operator^=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull;

// Relational
auto operator==(MyNumber lhs, MyNumber rhs) -> bool;
auto operator!=(MyNumber lhs, MyNumber rhs) -> bool;
auto operator<(MyNumber lhs, MyNumber rhs) -> bool;
auto operator>(MyNumber lhs, MyNumber rhs) -> bool;
auto operator<=(MyNumber lhs, MyNumber rhs) -> bool;
auto operator>=(MyNumber lhs, MyNumber rhs) -> bool;
```

```c++
// my_number.cpp

#include "my_number.h"

// Arithmetic
auto operator+(MyNumber lhs, MyNumber rhs) -> MyNumber {
  return MyNumber(lhs.value() + rhs.value());
}
auto operator-(MyNumber lhs, MyNumber rhs) -> MyNumber {
  return MyNumber(lhs.value() - rhs.value());
}
auto operator*(MyNumber lhs, MyNumber rhs) -> MyNumber {
  return MyNumber(lhs.value() * rhs.value());
}
auto operator/(MyNumber lhs, MyNumber rhs) -> MyNumber {
  return MyNumber(lhs.value() / rhs.value());
}
auto operator%(MyNumber lhs, MyNumber rhs) -> MyNumber {
  return MyNumber(lhs.value() % rhs.value());
}

// Bitwise
auto operator&(MyNumber lhs, MyNumber rhs) -> MyNumber {
  return MyNumber(lhs.value() & rhs.value());
}
auto operator|(MyNumber lhs, MyNumber rhs) -> MyNumber {
  return MyNumber(lhs.value() | rhs.value());
}
auto operator^(MyNumber lhs, MyNumber rhs) -> MyNumber {
  return MyNumber(lhs.value() ^ rhs.value());
}
auto operator<<(MyNumber lhs, int shift) -> MyNumber {
  return MyNumber(lhs.value() << shift);
}
auto operator>>(MyNumber lhs, int shift) -> MyNumber {
  return MyNumber(lhs.value() >> shift);
}

// Compound Arithmetic
auto operator+=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull {
  return &(*lhs = *lhs + rhs);
}
auto operator-=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull {
  return &(*lhs = *lhs - rhs);
}
auto operator*=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull {
  return &(*lhs = *lhs * rhs);
}
auto operator/=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull {
  return &(*lhs = *lhs / rhs);
}
auto operator%=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull {
  return &(*lhs = *lhs % rhs);
}

// Compound Bitwise
auto operator&=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull {
  return &(*lhs = *lhs & rhs);
}
auto operator|=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull {
  return &(*lhs = *lhs | rhs);
}
auto operator^=(MyNumber* _Nonnull lhs, MyNumber rhs) -> MyNumber* _Nonnull {
  return &(*lhs = *lhs ^ rhs);
}

// Relational
auto operator==(MyNumber lhs, MyNumber rhs) -> bool {
  return lhs.value() == rhs.value();
}
auto operator!=(MyNumber lhs, MyNumber rhs) -> bool {
  return lhs.value() != rhs.value();
}
auto operator<(MyNumber lhs, MyNumber rhs) -> bool {
  return lhs.value() < rhs.value();
}
auto operator>(MyNumber lhs, MyNumber rhs) -> bool {
  return lhs.value() > rhs.value();
}
auto operator<=(MyNumber lhs, MyNumber rhs) -> bool {
  return lhs.value() <= rhs.value();
}
auto operator>=(MyNumber lhs, MyNumber rhs) -> bool {
  return lhs.value() >= rhs.value();
}
```

```carbon
// main.carbon

library "Main";

import Core library "io";
import Cpp library "my_number.h";

fn PrintBool(b: bool) {
  if (b) {
    Core.Print(1);
  } else {
    Core.Print(0);
  }
}

fn Run() -> i32 {
  // Arithmetic
  var num1: Cpp.MyNumber = Cpp.MyNumber.MyNumber(14);
  var num2: Cpp.MyNumber = Cpp.MyNumber.MyNumber(5);
  Core.Print(num1.value());
  Core.Print(num2.value());
  Core.Print((num1 + num2).value());
  Core.Print((num1 - num2).value());
  Core.Print((num1 * num2).value());
  Core.Print((num1 / num2).value());
  Core.Print((num1 % num2).value());

  // Bitwise
  var bits1: Cpp.MyNumber = Cpp.MyNumber.MyNumber(12);
  var bits2: Cpp.MyNumber = Cpp.MyNumber.MyNumber(10);
  Core.Print(bits1.value());
  Core.Print(bits2.value());
  Core.Print((bits1 & bits2).value());
  Core.Print((bits1 | bits2).value());
  Core.Print((bits1 ^ bits2).value());
  Core.Print((bits1 << 2).value());
  Core.Print((bits1 >> 1).value());

  // Compound Arithmetic
  var c: Cpp.MyNumber = Cpp.MyNumber.MyNumber(100);
  Core.Print(c.value());
  &c += Cpp.MyNumber.MyNumber(10);
  Core.Print(c.value());
  &c -= Cpp.MyNumber.MyNumber(20);
  Core.Print(c.value());
  &c *= Cpp.MyNumber.MyNumber(2);
  Core.Print(c.value());
  &c /= Cpp.MyNumber.MyNumber(6);
  Core.Print(c.value());
  &c %= Cpp.MyNumber.MyNumber(9);
  Core.Print(c.value());

  // Compound Bitwise
  &c |= Cpp.MyNumber.MyNumber(12);
  Core.Print(c.value());
  &c &= Cpp.MyNumber.MyNumber(7);
  Core.Print(c.value());
  &c ^= Cpp.MyNumber.MyNumber(10);
  Core.Print(c.value());

  // Relational
  var rel1: Cpp.MyNumber = Cpp.MyNumber.MyNumber(20);
  var rel2: Cpp.MyNumber = Cpp.MyNumber.MyNumber(30);
  var rel3: Cpp.MyNumber = Cpp.MyNumber.MyNumber(20);
  Core.Print(rel1.value());
  Core.Print(rel2.value());
  Core.Print(rel3.value());
  PrintBool(rel1 == rel3);
  PrintBool(rel1 != rel2);
  PrintBool(rel1 < rel2);
  PrintBool(rel2 > rel1);
  PrintBool(rel1 <= rel3);
  PrintBool(rel1 >= rel2);

  return 0;
}
```

```shell
$ clang -c my_number.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link my_number.o main.o --output=demo
$ ./demo
14
5
19
9
70
2
4
12
10
8
14
6
48
6
100
110
90
180
30
3
15
7
13
20
30
20
1
1
1
1
1
0
```

Part of https://github.com/carbon-language/carbon-lang/issues/5995.
2025-09-05 13:03:10 +00:00
Richard Smith db0a00d713 Fix double-destruction of temporaries. (#6010)
Attach the cleanup to the `Temporary` instruction instead of to the
`TemporaryStorage` instruction. We create `TemporaryStorage`
instructions speculatively when creating an initializing expression, and
may overwrite those instructions with other instructions if it turns out
that a temporary is not required. Instead, wait until we finalize the
temporary and create a `Temporary` instruction to register the cleanup.
2025-09-04 19:19:59 +00:00
Boaz Brickner 870c5380a0 C++ interop: Support importing binary operator+ (#5996)
Triggered by calling a binary operator with LHS being an imported C++
class type.

Not supported (yet):
* Multiple overloads.
* Other operators.

C++ Interop Demo:

```c++
// hello_world.h

class C {
 public:
  C(int x) : x_(x) {}
  auto x() const -> int { return x_; }

 private:
  int x_ = 0; 
};

auto operator+ (C c1, C c2) -> C;
```

```c++
// hello_world.cpp

#include "hello_world.h"

#include <cstdio>

auto operator+ (C c1, C c2) -> C {
  printf("Adding %d with %d\n", c1.x(), c2.x());
  return C(c1.x() + c2.x());
}
```

```carbon
// main.carbon

library "Main";

import Cpp library "hello_world.h";

fn Run() -> i32 {
  let c1 : Cpp.C = Cpp.C.C(7);
  let c2 : Cpp.C = Cpp.C.C(8);
  let c3 : Cpp.C = c1 + c2;
  let c4 : Cpp.C = c3 + c2;
  return 0;
}

```

```shell
$ clang -c hello_world.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link hello_world.o main.o --output=demo
$ ./demo
Adding 7 with 8
Adding 15 with 8
```

Part of #5995.
2025-09-03 12:01:20 +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 Smith d37f1ae6b5 Add return value support to C++ thunks. (#5976)
Based on #5948. A couple of tricky parts:

* When generating the C++ side of the thunk, we are given a pointer to
the location to emplace the return value. The only mechanism C++
provides to perform this emplacement is using placement `operator new`,
which requires a library function in the `<new>` header. We handle this
by declaring that library function ourselves, and rely on Clang not
actually needing a definition for it (which the standard library owns).

* On the Carbon side of the thunk, we want to form an initializing
expression as the result of the call. We don't have a way of expressing
in SemIR that an initializing expression performs its initialization by
storing through a pointer, so this PR adds a new initializing
instruction, `InPlaceInit`, to model an initialization that's performed
opaquely in-place.
2025-08-26 02:23:38 +00:00
Richard Smith 8c9080801c Support for building thunks for C++ methods. (#5972)
Also tweak how we import C++ methods to properly handle C++23's explicit
object parameters.
2025-08-25 22:33:51 +00:00
Richard Smith ddafbc9331 Use direct passing for 32- and 64-bit unsigned integers. (#5980)
Previously we only avoided creating a thunk for signed integers. But the
same logic also applies to the unsigned 32-bit and 64-bit types.
2025-08-25 21:49:03 +00:00
Richard Smithandjosh11b 30b8a93fde Support conversion from T* to const T*. (#5971)
Also support conversion from Derived* to const Base*.

---------

Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com>
2025-08-20 23:27:03 +00:00
Richard Smith b851e8c423 Add support for f16, f64, f128. (#5952)
Generalize the f64 support to support other sizes. Also provide interop
support for `float`, `_Float16`, and `__float128`.

Also lay some groundwork for non-standard floating-point types, though
we don't have any syntax to name them yet.
2025-08-14 01:14:40 +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 629f77eb61 Switch to representing FloatLiteralType as a RealId. (#5944)
Don't convert to f64 until we know that's the type that we actually
want. Also reimplement the conversion from RealId to FloatId to perform
an exact conversion with a real check for overflow, rather than
performing an approximate conversion via the host `double` type.

Unfortunately, LLVM doesn't expose its integer mantissa and exponent to
APFloat conversion, so we convert the RealId back to a string for now.

The LLVM conversion also detects overflow only if the literal would
round to having an out-of-range exponent, not if the literal is outside
the range of values of the type as the Carbon design expects. It's not
clear to me which rule we actually want here, so for simplicitly I'm
using the LLVM rule for now.

In preparation for adding other floating-point types beyond f64.
2025-08-12 22:08:07 +00:00
Richard Smith 28103b8f2e Convert LegacyFloatType into FloatLiteralType. (#5939)
* Rename the type.
* Change lowering to lower FloatLiteralType values as the placeholder
  `{}` value we use for literals instead of as an LLVM f64.
* Change eval to convert the type as part of a floating point
  conversion, so that lowering can lower converted constants properly.

For now we still represent a value of FloatLiteralType as a
double-precision APFloat. (That will need to change so that we can
losslessly convert literals to f80 / f128 values, and so that we can
convert literals to f32 values without double-rounding.)
2025-08-12 18:55:38 +00:00
Jon Ross-Perkins b410ebd088 Fix destruction of generic types (#5943)
The self access is important; for the test `generic_class.carbon` being
added to `toolchain/check/testdata/class/destroy_calls.carbon`, it was
using `%T.as.Destroy` instead of `%D.as.Destroy`, indicating the default
blank impl was being used instead of the type-specific version. That
test is trying to focus on the issue, but the delta is visible in a
couple other files in this PR, for example
`toolchain/check/testdata/class/generic/init.carbon`.

I'm separately working on getting rid of the default impl, which is how
I noticed this.
2025-08-12 00:10:43 +00:00
694c00c7eb Make Core.Float a class. Add missing builtins for float support. (#5932)
Add missing builtins for float compound assignment, for building a
FloatType, and for converting a float literal to FloatType. Switch
`Core.Float` to being a class and add impls for the various
floating-point operators.

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
2025-08-11 21:01:34 +00:00
Richard Smith e0de9ddf05 Implement initialization for C++ thunk parameters. (#5938)
When initializing a C++ thunk parameter:

* If we have an initializing expression, materialize a temporary and
pass its address.
* If we have a reference expression, pass its address directly.
* If we have a value expression with a pointer value representation,
pass the pointer.
* Otherwise, create a new temporary and initialize it with a copy of the
argument, and pass its address.
2025-08-08 23:38:47 +00:00
Boaz Brickner 52ed26235d Add a flag to dump the C++ AST (#5918)
Use it to dump the AST that includes a generated C++ thunk.
Based on #5917.

Also added printing of the full actual text when check fails to make
debugging easier.
Changed line replacement to allow removing complete lines.

Part of #5514.
2025-08-07 17:09:30 +00:00
Boaz Brickner 3c9d267388 Generate and use a C++ thunk to call non simple ABI C++ functions (#5850)
When the C++ function has a parameter that is not a pointer and not a
signed integer of 32 or 64 bits, generate a thunk.

Terminology:
* Callee function: The C++ function we actually want to call.
* Thunk function: The C++ function we generated that calls the callee
function.
* A simple ABI type, for now, is one of:
  * A pointer
  * signed integer with 32 bits
  * signed integer with 64 bits

The thunk function is marked `always_inline` and uses the `asm`
attribute to set its mangled to the callee function mangled name
suffixed with `".carbon_thunk"`.

When importing a C++ function, we decide whether calling it requires a
thunk and if so we generate it and import it as well, which is currently
a recursive call.

When calling the thunk function, we initialize a temporary storage for
each non simple ABI parameter type and take its address. This can be
optimized when the variable is already in storage.

Not supported yet:
* Functions with non void return values.
* Member methods.

Moved unsigned int param test from `arithmetic_types_direct.carbon` to
`arithmetic_types_bridged.carbon`, since only signed integers aren't
bridged using a thunk.

C++ Interop Demo:

```c++
// hello_world.h

struct S {
  S() {}
  S(const S&) { x = 1; }
  int x;
};

void hello_world(S s);
```

```c++
// hello_world.cpp

#include "hello_world.h"

#include <cstdio>

void hello_world2(S s) { printf("hello_world2: %d\n", s.x); }

void hello_world(S s) {
  printf("hello_world: %d\n", s.x);
  hello_world2(s);
}
```

```carbon
// main.carbon

library "Main";

import Cpp library "hello_world.h";

fn Run() -> i32 {
  var s : Cpp.S;
  Cpp.hello_world(s);
  return 0;
}
```

```shell
$ clang -c hello_world.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link hello_world.o main.o --output=demo
$ ./demo
hello_world: 1
hello_world2: 1
```

Before this change (no thunk - copy constructor not called when calling
`hello_world()`):
```shell
$ ./demo
hello_world: -1219172304
hello_world2: 1
```
2025-08-06 10:27:33 +00:00
Richard Smithandgoogle-labs-jules[bot] 4685890d63 Rename FloatLiteral to FloatValue. (#5911)
In preparation for `FloatValue` being used more generally, and not only
for literals.

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2025-08-05 22:34:34 +00:00
Boaz Brickner 720c77f6e7 Don't use struct literals in tests (#5906)
The first version of C++ overload resolution would not support struct
literals, so we prepare the tests for that.
2025-08-05 18:57:03 +00:00
Richard Smith 25681901bd Improve mapping of Clang diagnostics into Carbon diagnostics (#5894)
Instead of taking the complete text of the Clang diagnostic and using it
as the message portion of a Carbon diagnostic, generate the individual
pieces separately and pass them into the Carbon diagnostic
infrastructure.

* Clang's context lines are generated by running a custom "diagnostic
renderer" and tracking which lines it wants to print as context for a
given source location. When mapping from a C++ source location back to a
Carbon location, the Carbon `Loc` structure is now fully populated,
including filling in the context line and the column number.
* Clang's snippet is generated by running a custom diagnostic renderer
that is a cut-down version of the full text diagnostic renderer that
only prints a snippet. This is then attached to the Carbon diagnostic
manually as an override for the snippet we'd usually create.

We no longer repeat the file location twice on each diagnostic, and no
longer produce a bogus "in import" line for all locations coming from
clang that point arbitrarily to the first C++ import in the Carbon file.
The `[diagnostic kind]` marker is now displayed at the end of the
diagnostic message, not on a line of its own after the snippet.
2025-08-01 01:24:31 +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 a905f15bf9 Fix the tests for forward declared union pointer as return type by making the return type pointer _Nonnull (#5878)
Followup of #5773.
Part of #5772.
2025-07-30 20:20:30 +00:00
Boaz Brickner f0cff612eb Add support for using C++ double type in imported function declarations (#5868)
Carbon only supports f64, so only double can be mapped.

https://github.com/carbon-language/carbon-lang/blob/30f0ddab71bda71f8789080962b1fe8a5938e327/toolchain/check/type.cpp#L54

C++ Interop Demo:

```c++
// hello_world.h

auto hello_world(double x) -> void;
```

```c++
// hello_world.cpp

#include "hello_world.h"

#include <cstdio>

auto hello_world(double x) -> void {
  printf("double: %f\n", x);
}
```

```carbon
// main.carbon

library "Main";

import Cpp library "hello_world.h";

fn Run() -> i32 {
  Cpp.hello_world(0.25);
  return 0;
}
```

```shell
$ clang -c hello_world.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link hello_world.o main.o --output=demo
$ $ ./demo
double: 0.250000
```

Before this change:

```shell
$ bazel-bin/toolchain/carbon compile main.carbon
main.carbon:8:3: error: semantics TODO: `Unsupported: parameter type: double`
  Cpp.hello_world(0.25);
  ^~~~~~~~~~~~~~~
main.carbon:8:3: note: in `Cpp` name lookup for `hello_world`
  Cpp.hello_world(0.25);
  ^~~~~~~~~~~~~~~
```

Part of #5263.
2025-07-30 07:13:59 +00:00
Boaz Brickner 30f0ddab71 Add support for importing access from C++ to Carbon (#5858)
Better access control with inheritance should come with better
inheritance support (actually importing inheritance).

C++ Interop Demo:

```c++
// hello_world.h

class HelloWorld {
 public:
  static auto Pub() -> void;

 protected:
  static auto Pro() -> void;

 private:
  static auto Pri() -> void;
};
```

```c++
// hello_world.cpp

#include "hello_world.h"

#include <cstdio>

auto HelloWorld::Pub() -> void { printf("Public!\n"); }
auto HelloWorld::Pro() -> void { printf("Protected!\n"); }
auto HelloWorld::Pri() -> void { printf("Private!\n"); }
```

```carbon
// main.carbon

library "Main";

import Cpp library "hello_world.h";

fn Run() -> i32 {
  Cpp.HelloWorld.Pub();
  Cpp.HelloWorld.Pro();
  Cpp.HelloWorld.Pri();
  return 0;
}
```

```shell
$ clang -c hello_world.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
main.carbon:9:3: error: cannot access protected member `Pro` of type `Cpp.HelloWorld`
  Cpp.HelloWorld.Pro();
  ^~~~~~~~~~~~~~~~~~
main.carbon: note: declared here

main.carbon:10:3: error: cannot access private member `Pri` of type `Cpp.HelloWorld`
  Cpp.HelloWorld.Pri();
  ^~~~~~~~~~~~~~~~~~
main.carbon: note: declared here
```

Before this change (no access checks):
```shell
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link hello_world.o main.o --output=demo
$ ./demo
Public!
Protected!
Private!
```

Part of #5859.
2025-07-29 08:25:15 +00:00
Boaz Brickner 6d6e0d0418 Add support for using C++ bool type in imported function declarations. (#5860)
C++ in

C++ Interop Demo:

```c++
// hello_world.h

auto hello_world(bool x) -> bool;
```

```c++
// hello_world.cpp

#include "hello_world.h"

#include <cstdio>

auto hello_world(bool x) -> bool {
  printf("bool: %d\n", x);
  return !x;
}
```

```carbon
// main.carbon

library "Main";

import Cpp library "hello_world.h";

fn Run() -> i32 {
  let x: bool = Cpp.hello_world(false);
  if (x) {
    return 0;
  } else {
    return 1;
  }
}
```

```shell
$ clang -c hello_world.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link hello_world.o main.o --output=demo
$ ./demo
bool: 0
```

Before this change (bool is interpreted as a 1 bit integer):
```shell
$ bazel-bin/toolchain/carbon compile main.carbon
... CRASH! ...
clang/include/clang/AST/Type.h:952: const ExtQualsTypeCommonBase *clang::QualType::getCommonPtr() const: Assertion `!isNull() && "Cannot retrieve a NULL type pointer"' failed.
```

Part of #5263.
2025-07-29 06:56:45 +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
Boaz Brickner a269c72e48 Fix variadic arguments test to use the format that is not deprecated in C++26 and fix the call site to be valid (#5842)
See https://en.cppreference.com/w/cpp/language/variadic_arguments.html.

Part of #5436.
2025-07-24 19:40:19 +00:00
Richard Smith c90c6728fd Interop: support all C++ integer types that map to intN_t or uintN_t. (#5836)
Expand support for `int` and `short` to cover all the other `intN_t` and
`uintN_t` types too. We achieve this by asking Clang what the `intN_t` /
`uintN_t` type that it would use for the given bitwidth is, and checking
if that's the type we're trying to map.
2025-07-22 21:29:09 +00:00
Jon Ross-Perkins 7ccc1e0144 Expand naming for impls and functions (#5808)
Change impls from `<interface>.impl` to `<self>.as.<interface>.impl`,
and *member* functions to `<parent scope>.<fn>` (non-member functions
exclude their parent scope). Stop special-casing builtin functions,
given the new naming scheme.

The purpose of this is to make it clearer when a member function is
being accessed and, if so, which member function. In particular, we
often access interface `Op` functions. The builtin function
special-casing was intended to help with that, but we still have lots of
`Op` functions. This particular approach should make the interactions
clearer.

This changes up queueing of block IDs a little because, in particular,
we need to process bodies of entities only after constants finish
processing. But, it should also result in less memory usage during
processing because it means we have less on the insts stack at any given
time, since we track a block rather than all instructions contained by
the block.
2025-07-21 18:45:07 +00:00
Jon Ross-PerkinsandGeoff Romer eae3491129 Switch inst namer to queue entities when reached (#5806)
This switches from the `CollectNamesInBlock` approach for entities, to
instead traversing entities as they're encountered. For example, when
traversing constants, when a type is found, the entity will have its
block queued for processing.

This leads to a change in the traversal order, which affects
disambiguation done by numeric sequencing (since that's just showing the
traversal order).

This will allow for simpler "name based on name" logic. This is
something I plan to use for:

- impls: `<type>.as.<interface>.impl`
- functions: `<entity>.<member function>`
  - Note an impl may be used as the entity for a bound function.

By naming the entities as they're encountered, I'll be able to rely on
the generated names rather than recalculating them.

To assist this, I'm also differentiating between the ambiguous and
disambiguated name. Otherwise, we could end up with things like
`<function>.<disambiguator>.<call>.<other disambiguator>`, where the
repeated disambiguator may not be necessary in order to get full
disambiguation. It's also a smaller delta from the current output.

Note, changing `Name` to a class felt appropriate given its shape. I was
also noticing that parts of its API were unused, and the class helps
detect unused private members.

---------

Co-authored-by: Geoff Romer <gromer@google.com>
2025-07-21 17:58:55 +00:00
Boaz Brickner 52976c55fb When importing a declaration, first collect all dependent unimported declarations and import them first (#5821)
This fixes some tests since we now handle record name scopes correctly.

Part of #5533.
2025-07-18 18:01:27 +00:00
Boaz Brickner f4f521de7f Explicitly mark as unsupported instead of crashing mapping a parameter record type defined not in a namespace (#5777)
Before this change we crash in this case when trying to use the outer
type.
After this change we diagnose a TODO.
Will add the missing support for this in a separate PR.

Part of #5533.
2025-07-18 02:42:45 +00:00