Skip to content

[interop][SwiftToCxx] simd and nested type fixes #67668

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,8 @@ ERROR(expose_protocol_to_cxx_unsupported,none,
"protocol %0 can not yet be represented in C++", (ValueDecl *))
ERROR(expose_move_only_to_cxx,none,
"noncopyable %kind0 can not yet be represented in C++", (ValueDecl *))
ERROR(expose_nested_type_to_cxx,none,
"nested %kind0 can not yet be represented in C++", (ValueDecl *))
ERROR(unexposed_other_decl_in_cxx,none,
"%kind0 is not yet exposed to C++", (ValueDecl *))
ERROR(unsupported_other_decl_in_cxx,none,
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/SwiftNameTranslation.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ enum RepresentationError {
UnrepresentableEnumCaseTuple,
UnrepresentableProtocol,
UnrepresentableMoveOnly,
UnrepresentableNested,
};

/// Constructs a diagnostic that describes the given C++ representation error.
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/SwiftNameTranslation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,11 @@ swift::cxx_translation::getDeclRepresentation(const ValueDecl *VD) {
genericSignature =
typeDecl->getGenericSignature().getCanonicalSignature();
}
// Nested types are not yet supported.
if (!typeDecl->hasClangNode() &&
isa_and_nonnull<NominalTypeDecl>(
typeDecl->getDeclContext()->getAsDecl()))
return {Unsupported, UnrepresentableNested};
}
if (const auto *varDecl = dyn_cast<VarDecl>(VD)) {
// Check if any property accessor throws, do not expose it in that case.
Expand Down Expand Up @@ -321,5 +326,7 @@ swift::cxx_translation::diagnoseRepresenationError(RepresentationError error,
return Diagnostic(diag::expose_protocol_to_cxx_unsupported, vd);
case UnrepresentableMoveOnly:
return Diagnostic(diag::expose_move_only_to_cxx, vd);
case UnrepresentableNested:
return Diagnostic(diag::expose_nested_type_to_cxx, vd);
}
}
8 changes: 8 additions & 0 deletions lib/PrintAsClang/ClangSyntaxPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ bool ClangSyntaxPrinter::printNominalTypeOutsideMemberDeclInnerStaticAssert(
}

void ClangSyntaxPrinter::printClangTypeReference(const clang::Decl *typeDecl) {
if (cast<clang::NamedDecl>(typeDecl)->getDeclName().isEmpty() &&
isa<clang::TagDecl>(typeDecl)) {
if (auto *tnd =
cast<clang::TagDecl>(typeDecl)->getTypedefNameForAnonDecl()) {
printClangTypeReference(tnd);
return;
}
}
auto &clangCtx = typeDecl->getASTContext();
clang::PrintingPolicy pp(clangCtx.getLangOpts());
const auto *NS = clang::NestedNameSpecifier::getRequiredQualification(
Expand Down
40 changes: 23 additions & 17 deletions lib/PrintAsClang/PrintClangFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,8 +556,10 @@ static std::string encodeTypeInfo(const T &abiTypeInfo,
return std::move(typeEncodingOS.str());
}

// Returns false if the given direct type is not yet supported because
// of its ABI.
template <class T>
static void printDirectReturnOrParamCType(
static bool printDirectReturnOrParamCType(
const T &abiTypeInfo, Type valueType, const ModuleDecl *emittedModule,
raw_ostream &os, raw_ostream &cPrologueOS,
PrimitiveTypeMapping &typeMapping,
Expand All @@ -575,20 +577,21 @@ static void printDirectReturnOrParamCType(

unsigned Count = 0;
clang::CharUnits lastOffset;
abiTypeInfo.enumerateRecordMembers(
[&](clang::CharUnits offset, clang::CharUnits end, Type t) {
if (abiTypeInfo.enumerateRecordMembers([&](clang::CharUnits offset,
clang::CharUnits end, Type t) {
lastOffset = offset;
++Count;
addABIRecordToTypeEncoding(typeEncodingOS, offset, end, t, typeMapping);
});
}))
return false;
assert(Count > 0 && "missing return values");

// FIXME: is this "prettyfying" logic sound for multiple return values?
if (isKnownCType(valueType, typeMapping) ||
(Count == 1 && lastOffset.isZero() && !valueType->hasTypeParameter() &&
valueType->isAnyClassReferenceType())) {
prettifiedValuePrinter();
return;
return true;
}

os << "struct " << typeEncodingOS.str();
Expand Down Expand Up @@ -642,6 +645,7 @@ static void printDirectReturnOrParamCType(
interopContext.runIfStubForDeclNotEmitted(typeEncodingOS.str(), [&]() {
printStub(cPrologueOS, typeEncodingOS.str());
});
return true;
}

/// Make adjustments to the Swift parameter name in generated C++, to
Expand Down Expand Up @@ -750,18 +754,20 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature(
if (!directResultType) {
os << "void";
} else {
printDirectReturnOrParamCType(
*directResultType, resultTy, emittedModule, os, cPrologueOS,
typeMapping, interopContext, [&]() {
OptionalTypeKind retKind;
Type objTy;
std::tie(objTy, retKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(FD, resultTy);

auto s = printClangFunctionReturnType(objTy, retKind, emittedModule,
outputLang);
assert(!s.isUnsupported());
});
if (!printDirectReturnOrParamCType(
*directResultType, resultTy, emittedModule, os, cPrologueOS,
typeMapping, interopContext, [&]() {
OptionalTypeKind retKind;
Type objTy;
std::tie(objTy, retKind) =
DeclAndTypePrinter::getObjectTypeAndOptionality(FD,
resultTy);

auto s = printClangFunctionReturnType(
objTy, retKind, emittedModule, outputLang);
assert(!s.isUnsupported());
}))
return ClangRepresentation::unsupported;
}
} else {
OptionalTypeKind retKind;
Expand Down
4 changes: 4 additions & 0 deletions test/Inputs/clang-importer-sdk/usr/include/simd.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ typedef double __attribute__((ext_vector_type(3))) double3;
typedef double __attribute__((ext_vector_type(4))) double4;
typedef double __attribute__((ext_vector_type(8))) double8;

typedef struct { float4 columns[4]; } float4x4;

// Types that we should not be able to import.
typedef char __attribute__((ext_vector_type(17))) char17;
typedef unsigned char __attribute__((ext_vector_type(21))) uchar21;
Expand Down Expand Up @@ -89,6 +91,7 @@ ushort6 makes_ushort6();
int128 makes_int128();
uint20 makes_uint20();

#ifndef SIMD_NO_CODE
takes_char2(char2);
takes_char64(char64);
takes_uchar3(uchar3);
Expand Down Expand Up @@ -130,3 +133,4 @@ takes_short5(short5);
takes_ushort6(ushort6);
takes_int128(int128);
takes_uint20(uint20);
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ namespace ns {

using SimpleTypedef = int;

typedef struct { float column; } anonymousStruct;

namespace ns {

using anonStructInNS = struct { float row; };

}

//--- module.modulemap
module CxxTest {
header "header.h"
Expand Down Expand Up @@ -149,6 +157,12 @@ public func takeTrivial(_ x: Trivial) {
public func takeTrivialInout(_ x: inout Trivial) {
}

@_expose(Cxx)
public struct Strct {
public let transform: anonymousStruct
public let transform2: ns.anonStructInNS
}

// CHECK: #if __has_feature(objc_modules)
// CHECK: #if __has_feature(objc_modules)
// CHECK-NEXT: #if __has_warning("-Watimport-in-framework-header")
Expand Down Expand Up @@ -284,3 +298,9 @@ public func takeTrivialInout(_ x: inout Trivial) {
// CHECK: SWIFT_INLINE_THUNK void takeTrivialInout(Trivial& x) noexcept SWIFT_SYMBOL({{.*}}) {
// CHECK-NEXT: return _impl::$s8UseCxxTy16takeTrivialInoutyySo0E0VzF(swift::_impl::getOpaquePointer(x));
// CHECK-NEXT: }

// CHECK: SWIFT_INLINE_THUNK anonymousStruct Strct::getTransform() const {
// CHECK-NEXT: alignas(alignof(anonymousStruct)) char storage[sizeof(anonymousStruct)];

// CHECK: SWIFT_INLINE_THUNK ns::anonStructInNS Strct::getTransform2() const {
// CHECK-NEXT: alignas(alignof(ns::anonStructInNS)) char storage[sizeof(ns::anonStructInNS)];
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// RUN: %empty-directory(%t)
// RUN: split-file %s %t

// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck %t/use-cxx-types.swift -typecheck -module-name UseCxxTy -emit-clang-header-path %t/UseCxxTy.h -I %t -enable-experimental-cxx-interop -disable-availability-checking -Xcc -DSIMD_NO_CODE

// RUN: %FileCheck %s < %t/UseCxxTy.h

// FIXME: remove once https://github.com/apple/swift/pull/60971 lands.
// RUN: echo "#include \"header.h\"" > %t/full-cxx-swift-cxx-bridging.h
// RUN: cat %t/UseCxxTy.h >> %t/full-cxx-swift-cxx-bridging.h

// RUN: %check-interop-cxx-header-in-clang(%t/full-cxx-swift-cxx-bridging.h -Wno-reserved-identifier -DSIMD_NO_CODE)

// This is required to verify that `Struct` is returned and passed directly.
// REQUIRES: OS=macosx
// REQUIRES: PTRSIZE=64

//--- header.h

#include <simd.h>

using simd_float4x4 = float4x4;

//--- module.modulemap
module CxxTest {
header "header.h"
requires cplusplus
}

//--- use-cxx-types.swift
import CxxTest

public struct Struct {
private let transform: simd_float4x4

public init() {
transform = simd_float4x4()
}
}

public func passStruct(_ x : Struct) {

}

// CHECK: class SWIFT_SYMBOL("s:8UseCxxTy6StructV") Struct final {
// CHECK-NOT: init(
// CHECK: // Unavailable in C++: Swift global function 'passStruct(_:)'
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,7 @@ public distributed actor DistributedActorClass {

@_expose(Cxx) // ok
nonisolated public var prop2: Int { 42 }

@_expose(Cxx) // expected-error {{nested struct 'NestedStruct' can not yet be represented in C++}}
public struct NestedStruct {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,11 @@ public protocol TestProtocol {}
public typealias unsupportedTypeAlias = () -> (Float, Float)

// CHECK: // Unavailable in C++: Swift type alias 'unsupportedTypeAlias'

public struct Container {
public struct NestedStruct {}
}

public typealias unsupportedTypeAliasNested = Container.NestedStruct

// CHECK: // Unavailable in C++: Swift type alias 'unsupportedTypeAliasNested'