Commit Graph
19 Commits
Author SHA1 Message Date
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
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 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
Jon Ross-Perkins a85d292f8d Change from ToImplicit to AsDesugared (#5591)
This changes `ToImplicit` to `AsDesugared`, and adds a
`GetLocIdForDesugaring` to `InstStore`.

In particular, I'm motivated by the latter, to make it clearer what the
intended call convention is.
2025-06-03 17:55:16 +00:00
Dana JansensandJon Ross-Perkins 315e206ff1 Construct LocId from InstId directly (explicitly) instead of doing lookups when possible (#5355)
Remove calls to `InstStore::GetLocId()` to build a LocId from an InstId
now that they can be constructed directly from the InstId. Most uses of
LocId are just plumbing, so this does not affect them. However places
that want to look inside the LocId do not want to work with the InstId
form. In these places, introduce `InstStore::GetResolvedLocId()` which
converts a LocId (or an InstId as an optimization) into a LocId which is
not backed by an InstId. These locations can be printed (they have a
line and column when they are a NodeId), they can have flags added to
them (`ToImplicit`, `ToTokenOnly`), they can be converted to an
underlying ImportIRInstId, or they may be `None`.

`Dump()` is made to print a resolved location instead of printing the
InstId in the location, since (at least in my experience) the resolved
location is what is interesting in debugging, and this saves manual
`MakeInstId` steps in the debugger every time a location is of interest.

The LocId constructor from InstId is made `explicit` to add clarity to
function calls passing an `inst_id` now directly instead of calling
`context.insts().GetLocId(inst_id)`. To avoid needing to construct
`SemIR::LocId(...)` explicitly in all cases though, the diagnostics code
in Check uses `DiagnosticLocId` as its template parameter which accepts
InstId as well and does the construction of LocId from it.

Because LocId now requires an explicit construction from InstId, any
callers to `AddInst()` functions will have to explicitly convert to
LocId if they had an InstId, but not if they pass a NodeId. To make this
difference clear to callers, we `requires` that the input type can be
converted to LocId. This ensures that passing an InstId results in an
error at the callsite where the InstId is passed, instead of generating
a compiler error when trying to construct `LocIdAndInst` inside
`AddInst()`, which is less clear about what went wrong and doesn't seem
entirely intentional.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
2025-04-28 19:06:24 +00:00
Jon Ross-Perkins 4923445e3a Drop Singleton from ErrorInst::SingletonInstId and similar (#5304)
We frequently want to operate on singletons. Per discussion, drop
`Singleton` to make the code shorter.

This started off as wanting to write `inst_id.is_error()`, but the
dependency relationship between ids.h and singleton_insts.h would
require some kind of delayed evaluation to allow the implementation to
remain in headers (which I suspect is helpful to have for inlining). I
could have added something like `IsErrorInst`, forward declared in ids.h
and defined in singleton_insts.h (which would always be included by
typed_insts.h), but the template approach felt like a decent balance
between (a) removing the boilerplate `::SingletonInstId`, (b)
understandability, (c) still visually mirroring if we immediately return
a singleton, and (d) flexibility for more than just `ErrorInst`. But TBH
I'd probably still have written `is_error()` if it didn't require
addressing the cross-header cycle.

Then I tried `SemIR::InstId::Is<SemIR::ErrorInst>`, which generally
worked with types but generated the complaint that it didn't shorten
*all* singleton uses. So pulling back on `::Is`, and instead just
dropping `Singleton`.
2025-04-15 22:40:29 +00:00
Jon Ross-Perkins 422cc3d48a Move diagnostic usings off Context (#5007)
There aren't remaining uses on `Context` other than `DiagnosticEmitter`
itself. I'm adding `SemIRLoc` because I feel odd about having both
`Carbon::Check::DiagnosticBuilder` and
`Carbon::DiagnosticEmitter<T>::DiagnosticBuilder`, but it seems
relatively little additional typing outside the handful of
`DiagnosticEmitter` uses on `Context` itself:

```
Context::DiagnosticEmitter
DiagnosticEmitter<SemIRLoc>

Context::DiagnosticBuilder
SemIRLocDiagnosticBuilder

Context::BuildDiagnosticFn
BuildSemIRLocDiagnosticFn
```

Also clean up #include's while I'm finishing here.
2025-02-26 18:44:36 +00:00
Jon Ross-Perkins afef6cd940 Refactor name lookup logic out of Context (#4930)
This is a pretty straight move of name lookup functionality to
name_lookup.*
2025-02-12 22:03:08 +00:00
Jon Ross-Perkins efab39cbd9 Remove InstId::Builtin members (#4632)
- `InstId::Builtin<Inst>` -> `<Inst>::SingletonInstId`
- `InstId::PackageNamespace` -> `Namespace::PackageInstId`
2024-12-05 18:13:46 +00:00
Jon Ross-Perkins 4a80d6758d Rename the builtin FloatType to LegacyFloatType, Error to ErrorInst (#4555)
This is for more clearly distinct names, and to make it a clearer
transition from `BuiltinInst` for name conflicts. `FloatType` is also an
instruction, and we have `Carbon::Error` (common/error.h). This avoids
affecting tests, although the name is embedded in the builtin test.

In `LegacyFloatType`, `Legacy` because I was having trouble coming up
with a more appropriate name. I'm not clear this is a `FloatLiteralType`
at present, it needs some work to mirror `IntLiteralType`.

In `ErrorInst`, the suffix `Inst` was discussed as good and similar to
`BuiltinInst` (although I'm trying to get rid of that).
2024-11-19 20:37:39 +00:00
Richard Smith fcabeb6725 Don't create instructions for implicit constants. (#4497)
When an instruction is created as part of an implicit call to an
interface member, we generated a bunch of constants for naming the
interface, finding the corresponding specific, accessing its member
function, and so on. This led to significant bloat in SemIR.

Instead, we now track whether an instruction is created implicitly in
its location, and where relevant, we use the constant value of the
instruction directly instead of storing a new `Inst`.

This doesn't reduce the amount of work we need to do, but does make the
representation in SemIR smaller and more readable.
2024-11-06 15:44:56 +00:00
David Blaikie 0b5d1101f9 Remove redundant optional wrapping llvm::function_ref (#4367)
llvm::function_ref (like std::unique_ptr, for instance) already has a
null/empty state, so use that to avoid confusion/duplication of empty
states between optional and the nested function_refs.
2024-10-07 17:25:35 +00:00
Richard SmithandJon Ross-Perkins 187a3608df Use As and ImplicitAs interfaces for conversions. (#4209)
Add these interfaces to the core library. For now, they're two separate
interfaces because we don't yet support one interface extending another.

This collapses a lot of the layering in check: for example, the call
building logic depends on implicit conversions, conversions now depend
on the overloaded operator machinery, and that machinery depends on
building calls.

In passing, improve the diagnostics for failing to find a name required
from the prelude. Also convert all the transitively-called code from
`NodeId` to `LocId` given the latter is what the conversion machinery
has available.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
2024-09-05 23:39:58 +00:00
Richard Smith 3cb769a053 Rename "generic instance" to "specific" throughout the toolchain. (#4165)
As discussed in toolchain meeting, we want to avoid overloading the
meaning of "instance", and "specific" was the best name we found. It's a
little unorthodox and inventive, but hopefully over time will become as
unsurprising as the term "generic" is.
2024-07-25 16:42:01 +00:00
Richard SmithandJon Ross-Perkins 50d56aa7c9 Add an instruction to represent a use of a dependent value from a generic instance. (#4122)
We can't use the instruction from the generic directly, because it
doesn't have the right constant value. Instead add an instruction that
models the transition from the constant value in the generic to the
constant value in the generic instance.

Also start associating the self generic instance with unqualified
lookups that find results in an enclosing generic, so that we track the
information necessary to create the new instruction.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
2024-07-12 14:59:01 +00:00
Richard Smith 6d3c915bbf When performing name lookup, determine the generic instance within which the lookup result was found. (#4118)
Require types into which qualified lookup is performed to be completely
defined. Eventually this will trigger substitution into the definition
for generic types.
2024-07-10 18:35:55 +00:00
Richard Smith 9029cac727 Remove inst_id from the public interface of ConstantId. (#4053)
Require mapping from a `ConstantId` to an `InstId` to go through the
`ConstantValueStore`.

This is a preparatory step for an upcoming generics change where
symbolic `ConstantId`s are no longer just a thin wrapper around an
`InstId` but instead are indexes into a table with additional
information about the symbolic constant beyond its `InstId`.
2024-06-14 00:40:53 +00:00
Jon Ross-Perkins 895e90e791 Start including the prelude for testing. (#3861)
- Adds an empty prelude.carbon file
- Imports that file in any non-Core package file
  - Adds --disable-prelude-import to avoid that
- Adds --exclude-dump-file-prefix to be able to hide files from dumping
- Used to hide core files (we can't do this by package name due to lex
dumps, for example)
- Restructures some tests to not rely on `i32`, particularly `alias`
tests (which rely on a name ref) and tests with no prelude.

I'm adding the framework for switching i32 to calling Int32 in the
prelude, but I'm running into a separate error actually switching over.
So that *mostly* works, but isn't quite ready for prime time. However,
maybe the current state of this PR is still useful to review since it
does a lot of the infrastructure work and adds the %Core everywhere?
2024-04-07 17:11:42 +00:00
cf361a83f3 Overloaded operator support. (#3796)
Support is added for all overloaded operator interfaces in the current
design apart from `Assign`, which is going to require some more work to
properly handle, given that primitive assignment currently has a special
implementation for quite a few builtin types.

As we don't have support for generics yet -- in particular, generic
interfaces -- there is no support for `*With` interfaces, but homogenous
interfaces such as `Add` are supported instead.

Factor out building of call expressions so that overloaded operators can
generate calls.

Switch a few places from using specific kinds of NodeId to a general
NodeId. Because overloaded operators and other things like implicit
conversions can result in member access and function calls, those
operations can't require a specific kind of NodeId.

Add import support for associated entities, and fix import support for
interfaces and symbolic bindings. We now import interfaces in two steps,
first importing a forward declaration then a definition, just like we do
for classes. For symbolic bindings, we ensure that each BindSymbolicName
is imported only once, because its ID is used as its symbolic identity.
This is necessary because we (only) support operator interfaces that are
defined in an imported Carbon package for now.

The entire contents of `check/operator.cpp` should probably be
rethought. In particular, doing a lot of name lookups on each operator
is likely to be bad for performance. But this gets us to the point where
overloaded operators are basically working, which seems like a good
place to iterate from.

For now, the tests that the individual operators map to the right
interfaces are mostly generated by a script, but that's just because I'm
expecting a fair bit of churn in how we define the prelude and the
`impl`s -- in particular, when we add support for `AddWith`, we'll need
to update all the tests. The plan is to remove the script once things
settle down.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
2024-03-19 19:47:29 +00:00