Use of `alignas` incompatible with C++ interop?

I have a C++ class that I was experimenting with in Swift. I discovered that the class is not usable if the alignas specifier is applied to any members:

// Prevents class being imported to Swift
alignas(std::hardware_destructive_interference_size)
std::atomic<size_type> position_{0};

I looked and wasn't able to find anything mentioning that an aligned type would not be available on the C++ Types Supported in Swift section of the features and constraints page. Is this a known limitation?

Does that attribute prevent any std::atomic (the class) from being imported? Or do you mean to say that alignas prevents the importing of declarations attributed with alignas? What version of the Swift compiler are you using?

I find that I cannot import std::atomic<_> but alignas is not the culprit. For reference, swiftc -version:

Apple Swift version 6.2.1 (swiftlang-6.2.1.4.8 clang-1700.4.4.1)
Target: arm64-apple-macosx26.0

That is, I am able to import the following declaration:

alignas(double) int i;

CC: @egor.zhdan

Isn't the problem that c++ templates need to be aliased to be imported into swift(bar some cases made for convenience)?

So you'd need to do:

using AtomicSizeType = std::atomic<size_type>;

For it to be visible in swift.

I don't think that the lack of a typealias is the culprit because if I comment out the alignas things work as expected.

Here is a more complete example:

class Buffer {
public:
	using size_type = std::size_t;
	std::atomic<size_type> position_{0};

	Buffer() = default;
	~Buffer() = default;
	Buffer(const Buffer& other) : position_(other.position_.load()) {}
	Buffer& operator=(const Buffer& other) { position_ = other.position_.load(); return *this; }
};

class Buffer2 {
public:
	using size_type = std::size_t;
	alignas(std::hardware_destructive_interference_size)
	std::atomic<size_type> position_{0};

	Buffer2() = default;
	~Buffer2() = default;
	Buffer2(const Buffer2& other) : position_(other.position_.load()) {}
	Buffer2& operator=(const Buffer2& other) { position_ = other.position_.load(); return *this; }
};

Buffer imports correctly but Buffer2 doesn't.

/***/Tests/CXXBufferTests/CXXBufferTests.swift:8:26: error: type 'CXXBuffer' has no member 'Buffer2'
                let b2 = CXXBuffer.Buffer2();
                         ~~~~~~~~~~~~~ ^~~~~~~
/***/Sources/CXXBuffer/include/CXXBuffer/Buffer.hpp:383:7: note: record 'Buffer2' is over aligned
class Buffer2 {
      ^

I'm experimenting using a C++ SPM project (single .hpp and .cpp) with tests written in Swift with interop enabled, if that makes a difference under the hood.

I'm using

swift-driver version: 1.127.14.1 Apple Swift version 6.2.3 (swiftlang-6.2.3.3.21 clang-1700.6.3.2)
Target: x86_64-apple-macosx26.0

If it would help I can create a GitHub repo showing the issue.

alignas is implemented as a macro over _Alignas in libc++ if I remember correctly. If you replace it with _Alignas, does the import work?

I gave that a try and still had the same issue.

1 Like

I wonder if this has to do with the specific alignment it's given. hardware_constructive_interference_size is allowed to be greater than alignof(max_align_t) as far as I can tell, so it's possible that Swift allows alignas as long as the granted alignment is fundamental.

I did some more experimentation and found the following:

For a non-member/global extern std::size_t i:

:white_check_mark: alignas(double)
:white_check_mark: alignas(32)
:white_check_mark: alignas(64)
:white_check_mark: alignas(std::hardware_destructive_interference_size)

For a class member std::atomic<std::size_t> position_{0}:

:white_check_mark: alignas(double)
:cross_mark: alignas(32)
:cross_mark: alignas(64)
:cross_mark: alignas(std::hardware_destructive_interference_size)

So it seems to be related to the fact that the type is a class member, but why double alignment works while the others don't I'm not sure.

I wonder whether it could be related to copy/assignment?


As an addendum:

For a non-member/global extern std::atomic<std::size_t> j, I get a compiler crash even if no alignment is specified:

1.	Apple Swift version 6.2.3 (swiftlang-6.2.3.3.21 clang-1700.6.3.2)
2.	Compiling with effective version 5.10
3.	While evaluating request ASTLoweringRequest(Lowering AST to SIL for file "/***/Tests/CXXBufferTests/CXXBufferTests.swift")
4.	While silgen emitFunction SIL function "@$s18CXXBufferTestsAAV5fnordyyYaF".
 for 'fnord()' (at /***/CXXBuffer/Tests/CXXBufferTests/CXXBufferTests.swift:6:8)
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  swift-frontend           0x00000001099544b8 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 40
1  swift-frontend           0x0000000109951b69 llvm::sys::RunSignalHandlers() + 233
2  swift-frontend           0x0000000109954a75 SignalHandler(int, __siginfo*, void*) + 261
3  libsystem_platform.dylib 0x00007ff8113ea37d _sigtramp + 29
4  libsystem_platform.dylib 0x00007f9b360cf238 _sigtramp + 18446743674895093464
5  swift-frontend           0x00000001034c4c5c swift::Lowering::SILGenFunction::emitIgnoredExpr(swift::Expr*) + 1196
6  swift-frontend           0x00000001034ef1a8 (anonymous namespace)::RValueEmitter::visitAssignExpr(swift::AssignExpr*, swift::Lowering::SGFContext) + 600
7  swift-frontend           0x00000001034d59ec swift::ASTVisitor<(anonymous namespace)::RValueEmitter, swift::Lowering::RValue, void, void, void, void, void, swift::Lowering::SGFContext>::visit(swift::Expr*, swift::Lowering::SGFContext) + 1180
8  swift-frontend           0x00000001034c4c5c swift::Lowering::SILGenFunction::emitIgnoredExpr(swift::Expr*) + 1196
9  swift-frontend           0x00000001035a50de swift::ASTVisitor<(anonymous namespace)::StmtEmitter, void, void, void, void, void, void>::visit(swift::Stmt*) + 7438
10 swift-frontend           0x0000000103503fb5 swift::Lowering::SILGenFunction::emitFunction(swift::FuncDecl*) + 421
11 swift-frontend           0x000000010341b365 swift::Lowering::SILGenModule::emitFunctionDefinition(swift::SILDeclRef, swift::SILFunction*) + 8869
12 swift-frontend           0x000000010341c2bf swift::Lowering::SILGenModule::emitOrDelayFunction(swift::SILDeclRef) + 223
13 swift-frontend           0x00000001034190b2 swift::Lowering::SILGenModule::emitFunction(swift::FuncDecl*) + 146
14 swift-frontend           0x00000001035c60c8 (anonymous namespace)::SILGenType::visitFuncDecl(swift::FuncDecl*) + 24
15 swift-frontend           0x00000001035c12af (anonymous namespace)::SILGenType::emitType() + 383
16 swift-frontend           0x0000000103418ead swift::ASTVisitor<swift::Lowering::SILGenModule, void, void, void, void, void, void>::visit(swift::Decl*) + 77
17 swift-frontend           0x00000001034208a5 swift::ASTLoweringRequest::evaluate(swift::Evaluator&, swift::ASTLoweringDescriptor) const + 2853
18 swift-frontend           0x00000001035a29a8 swift::SimpleRequest<swift::ASTLoweringRequest, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule>> (swift::ASTLoweringDescriptor), (swift::RequestFlags)17>::evaluateRequest(swift::ASTLoweringRequest const&, swift::Evaluator&) + 200
19 swift-frontend           0x000000010342575c swift::ASTLoweringRequest::OutputType swift::Evaluator::getResultUncached<swift::ASTLoweringRequest, swift::ASTLoweringRequest::OutputType swift::evaluateOrFatal<swift::ASTLoweringRequest>(swift::Evaluator&, swift::ASTLoweringRequest)::'lambda'()>(swift::ASTLoweringRequest const&, swift::ASTLoweringRequest::OutputType swift::evaluateOrFatal<swift::ASTLoweringRequest>(swift::Evaluator&, swift::ASTLoweringRequest)::'lambda'()) + 604
20 swift-frontend           0x00000001027d2707 swift::performCompileStepsPostSema(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 1159
21 swift-frontend           0x00000001027d68ec performCompile(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 3036
22 swift-frontend           0x00000001027d4e46 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 4406
23 swift-frontend           0x000000010273e79c swift::mainEntry(int, char const**) + 6524
24 dyld                     0x00007ff811009781 start + 3457
1 Like