Skip to content

Conversation

@bricknerb
Copy link
Contributor

@bricknerb bricknerb commented Oct 27, 2025

This defines Cpp.void as a custom type.
Cpp.void* is mapped to C++ void*.

Not supported yet: Conversions from and to other pointer types.

C++ Interop Demo:

// main.carbon

library "Main";

import Core library "io";

import Cpp inline '''
#include <cstdio>

auto GetPointer() -> void* _Nonnull {
  static int x = 8;
  return &x;
}

auto GetValue(void* _Nonnull ptr) -> int {
  return *static_cast<int*>(ptr);
}
''';

fn Run() -> i32 {
  let ptr: Cpp.void* = Cpp.GetPointer();
  Core.Print(Cpp.GetValue(ptr));
  return 0;
}
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link main.o --output=demo
$ ./demo
8

Part of #6280.

…void*` it to C++ function

This defines `Cpp.void` as a custom type.
`Cpp.void*` is mapped to C++ `void*`.
Not supported yet: Conversions from and to other pointer types.
@bricknerb bricknerb marked this pull request as ready for review October 27, 2025 16:00
@bricknerb bricknerb requested a review from a team as a code owner October 27, 2025 16:00
@bricknerb bricknerb requested review from jonmeow and removed request for a team October 27, 2025 16:00
};

// A type for C++ `void`. Should only be used for pointers (`void*`).
struct CustomCppVoidType {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what's "custom" here -- perhaps remove it?

For contrast, in "custom layout type", I believe "custom" refers to the way layout is determined externally ("customized").

Suggested change
struct CustomCppVoidType {
struct CppVoidType {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 380 to 383
auto StringifyInst(InstId /*inst_id*/, CustomCppVoidType /*inst*/) -> void {
*out_ << "<builtin Cpp.void>";
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functions here look lexically ordered, perhaps move this before CustomLayoutType?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 189 to 190
if (type_id == SemIR::CustomCppVoidType::TypeId && !wrapper_types.empty() &&
context.types().Is<SemIR::PointerType>(wrapper_types.back())) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add tests of things like const (Cpp.void*), (const Cpp.void)*, and Cpp.void**? Particularly the last case looks interesting, would it convert Cpp.void** to void* because wrapper_types.back() is PointerType?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this further, (const Cpp.void)* is probably the awkward case. It's essentially a pointer to void, but the const is in a place this code won't like.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the catch!
Added support for const void* and added tests for void** and const void*.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For (const Cpp.void)*, please note that we don't currently support C++ const pointers well.
For example, see #6284.

Comment on lines 183 to 185
// If `type_id` is `Cpp.void` and the last wrapper type in `wrapper_types` is a
// pointer, pops the pointer wrapper and returns non nullable `void*` QualType.
// Otherwise returns a null QualType.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to note, I would've found this comment more helpful if it'd been a little more high level. Right now it seems to be just documenting the if/else in detail. For a suggestion on a different comment style:

Suggested change
// If `type_id` is `Cpp.void` and the last wrapper type in `wrapper_types` is a
// pointer, pops the pointer wrapper and returns non nullable `void*` QualType.
// Otherwise returns a null QualType.
// Returns `void*` if the type is a wrapped `Cpp.void*`, consuming the
// pointer from `wrapper_types`. Otherwise returns no type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}

auto StringifyInst(InstId /*inst_id*/, CustomCppVoidType /*inst*/) -> void {
*out_ << "<builtin Cpp.void>";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can show up in diagnostics, right? We probably don't want the "<builtin" wrapper as such -- it's a user-visible type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 192 to 207
llvm::SmallVector<SemIR::TypeId>::iterator pointer_iter;
if (!wrapper_types.empty() &&
context.types().Is<SemIR::PointerType>(wrapper_types.back())) {
// `void*`.
pointer_iter = wrapper_types.end() - 1;
} else if (wrapper_types.size() >= 2 &&
context.types().Is<SemIR::ConstType>(wrapper_types.back()) &&
context.types().Is<SemIR::PointerType>(
wrapper_types[wrapper_types.size() - 2])) {
// `const void*`.
pointer_iter = wrapper_types.end() - 2;
} else {
return clang::QualType();
}

wrapper_types.erase(pointer_iter);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative way to write this, which I'm not sure is better:

Suggested change
llvm::SmallVector<SemIR::TypeId>::iterator pointer_iter;
if (!wrapper_types.empty() &&
context.types().Is<SemIR::PointerType>(wrapper_types.back())) {
// `void*`.
pointer_iter = wrapper_types.end() - 1;
} else if (wrapper_types.size() >= 2 &&
context.types().Is<SemIR::ConstType>(wrapper_types.back()) &&
context.types().Is<SemIR::PointerType>(
wrapper_types[wrapper_types.size() - 2])) {
// `const void*`.
pointer_iter = wrapper_types.end() - 2;
} else {
return clang::QualType();
}
wrapper_types.erase(pointer_iter);
llvm::SmallVector<SemIR::TypeId>::iterator wrapper_iter =
wrapper_types.end() - 1;
if (context.types().Is<SemIR::PointerType>(*wrapper_iter)) {
// This is `void*`, wrapper_iter points to the pointer type.
} else if (wrapper_iter != wrapper_types.begin() &&
context.types().Is<SemIR::ConstType>(*wrapper_iter) &&
context.types().Is<SemIR::PointerType>(*(wrapper_iter - 1))) {
// This is `const void*`, move `wrapper_iter` to point to the pointer type.
--wrapper_iter;
} else {
return clang::QualType();
}
wrapper_types.erase(wrapper_iter);

@bricknerb bricknerb requested a review from jonmeow October 28, 2025 13:25
Copy link
Contributor

@jonmeow jonmeow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG, with some TODO suggestions.

requires(InstT::Kind.template IsAnyOf<
SemIR::AssociatedEntityType, SemIR::CppOverloadSetType,
SemIR::CustomCppVoidType, SemIR::FunctionType,
SemIR::CppVoidType, SemIR::FunctionType,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'd missed this before... Can you please break CppVoidType out to its own function, and add a TODO that it should be always-incomplete?

That is, the changes in this file have the consequence of always treating void as a complete type. However, functionally we don't want it to behave that way -- only pointers are valid. So it should probably never be set as complete.

That should make todo_fail_void.carbon fail, for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.
FWIW, I was looking into making it always incomplete but didn't find the right setup for this and thought it shouldn't block this change.
Let me know if you have some pointers on how to do that.

Comment on lines 192 to 206
llvm::SmallVector<SemIR::TypeId>::iterator pointer_iter;
if (context.types().Is<SemIR::PointerType>(wrapper_types.back())) {
// `void*`.
pointer_iter = wrapper_types.end() - 1;
} else if (wrapper_types.size() >= 2 &&
context.types().Is<SemIR::ConstType>(wrapper_types.back()) &&
context.types().Is<SemIR::PointerType>(
wrapper_types[wrapper_types.size() - 2])) {
// `const void*`.
pointer_iter = wrapper_types.end() - 2;
} else {
return clang::QualType();
}

wrapper_types.erase(pointer_iter);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small suggestion to drop pointer_iter (mainly, the type information is complex, but also the direct erase seems helpfully succinct)

Suggested change
llvm::SmallVector<SemIR::TypeId>::iterator pointer_iter;
if (context.types().Is<SemIR::PointerType>(wrapper_types.back())) {
// `void*`.
pointer_iter = wrapper_types.end() - 1;
} else if (wrapper_types.size() >= 2 &&
context.types().Is<SemIR::ConstType>(wrapper_types.back()) &&
context.types().Is<SemIR::PointerType>(
wrapper_types[wrapper_types.size() - 2])) {
// `const void*`.
pointer_iter = wrapper_types.end() - 2;
} else {
return clang::QualType();
}
wrapper_types.erase(pointer_iter);
if (context.types().Is<SemIR::PointerType>(wrapper_types.back())) {
// `void*`.
wrapper_types.pop_back();
} else if (wrapper_types.size() >= 2 &&
context.types().Is<SemIR::ConstType>(wrapper_types.back()) &&
context.types().Is<SemIR::PointerType>(
wrapper_types[wrapper_types.size() - 2])) {
// `const void*`.
wrapper_types.erase(wrapper_types.end() - 2);
} else {
return clang::QualType();
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return clang::QualType();

llvm::SmallVector<SemIR::TypeId>::iterator pointer_iter;
if (context.types().Is<SemIR::PointerType>(wrapper_types.back())) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are probably other instructions that could be here which should be ignored, e.g. MaybeUnformedType. It might be worth thinking about whether a while loop here would be a good fit, but not asking for a change since wrapper_types doesn't support MaybeUnformedType right now either.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this definitely makes sense to do when we can have a test that triggers this.

github-merge-queue bot pushed a commit that referenced this pull request Oct 28, 2025
…l effect. (#6287)

This is to avoid edge cases where there are multiple `ConstType`
instructions, which code may not handle appropriately. I was thinking
about this for #6279
@bricknerb bricknerb enabled auto-merge October 29, 2025 08:27
@bricknerb bricknerb added this pull request to the merge queue Oct 29, 2025
Merged via the queue into carbon-language:trunk with commit 4d4d720 Oct 29, 2025
8 checks passed
@bricknerb bricknerb deleted the void2 branch October 29, 2025 09:45
github-merge-queue bot pushed a commit that referenced this pull request Oct 30, 2025
…6301)

I missed this in #6279, just fixing it. See `IntLiteralType` in
typed_insts.h (or similar) for comparison.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants