Skip to content

[cxx-interop] Mark some zero-sized value types as unavailable #77100

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 1 commit into from
Oct 28, 2024
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 @@ -2086,6 +2086,8 @@ ERROR(unexposed_other_decl_in_cxx,none,
"%kind0 is not yet exposed to C++", (ValueDecl *))
ERROR(unsupported_other_decl_in_cxx,none,
"Swift %kind0 cannot be represented in C++", (ValueDecl *))
ERROR(expose_zero_size_to_cxx,none,
"%0 is a zero sized value type, it cannot be exposed to C++ yet", (ValueDecl *))

ERROR(attr_methods_only,none,
"only methods can be declared %0", (DeclAttribute))
Expand Down
13 changes: 10 additions & 3 deletions include/swift/AST/SwiftNameTranslation.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
#define SWIFT_NAME_TRANSLATION_H

#include "swift/AST/AttrKind.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/Identifier.h"
#include <optional>

namespace swift {

Expand Down Expand Up @@ -84,6 +86,7 @@ enum RepresentationError {
UnrepresentableMoveOnly,
UnrepresentableNested,
UnrepresentableMacro,
UnrepresentableZeroSizedValueType,
};

/// Constructs a diagnostic that describes the given C++ representation error.
Expand All @@ -99,11 +102,15 @@ struct DeclRepresentation {
};

/// Returns the C++ representation info for the given declaration.
DeclRepresentation getDeclRepresentation(const ValueDecl *VD);
DeclRepresentation getDeclRepresentation(
const ValueDecl *VD,
std::optional<std::function<bool(const NominalTypeDecl *)>> isZeroSized);

/// Returns true if the given value decl is exposable to C++.
inline bool isExposableToCxx(const ValueDecl *VD) {
return !getDeclRepresentation(VD).isUnsupported();
inline bool isExposableToCxx(
const ValueDecl *VD,
std::optional<std::function<bool(const NominalTypeDecl *)>> isZeroSized) {
return !getDeclRepresentation(VD, isZeroSized).isUnsupported();
}

/// Returns true if the given value decl D is visible to C++ of its
Expand Down
10 changes: 9 additions & 1 deletion lib/AST/SwiftNameTranslation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "swift/AST/LazyResolver.h"
#include "swift/AST/Module.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/Type.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/StringExtras.h"

Expand Down Expand Up @@ -212,7 +214,9 @@ swift::cxx_translation::getNameForCxx(const ValueDecl *VD,
}

swift::cxx_translation::DeclRepresentation
swift::cxx_translation::getDeclRepresentation(const ValueDecl *VD) {
swift::cxx_translation::getDeclRepresentation(
const ValueDecl *VD,
std::optional<std::function<bool(const NominalTypeDecl *)>> isZeroSized) {
if (getActorIsolation(const_cast<ValueDecl *>(VD)).isActorIsolated())
return {Unsupported, UnrepresentableIsolatedInActor};
if (isa<MacroDecl>(VD))
Expand Down Expand Up @@ -253,6 +257,8 @@ swift::cxx_translation::getDeclRepresentation(const ValueDecl *VD) {
isa_and_nonnull<NominalTypeDecl>(
typeDecl->getDeclContext()->getAsDecl()))
return {Unsupported, UnrepresentableNested};
if (!isa<ClassDecl>(typeDecl) && isZeroSized && (*isZeroSized)(typeDecl))
return {Unsupported, UnrepresentableZeroSizedValueType};
}
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 @@ -393,5 +399,7 @@ swift::cxx_translation::diagnoseRepresenationError(RepresentationError error,
return Diagnostic(diag::expose_nested_type_to_cxx, vd);
case UnrepresentableMacro:
return Diagnostic(diag::expose_macro_to_cxx, vd);
case UnrepresentableZeroSizedValueType:
return Diagnostic(diag::expose_zero_size_to_cxx, vd);
}
}
14 changes: 13 additions & 1 deletion lib/PrintAsClang/DeclAndTypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2958,7 +2958,9 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) {
if (outputLang == OutputLanguageMode::Cxx) {
if (!isExposedToThisModule(M, VD, exposedModules))
return false;
if (!cxx_translation::isExposableToCxx(VD))
if (!cxx_translation::isExposableToCxx(
VD,
[this](const NominalTypeDecl *decl) { return isZeroSized(decl); }))
return false;
if (!isEnumExposableToCxx(VD, *this))
return false;
Expand All @@ -2976,6 +2978,16 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) {
return true;
}

bool DeclAndTypePrinter::isZeroSized(const NominalTypeDecl *decl) {
if (decl->isResilient())
return false;
auto &abiDetails = interopContext.getIrABIDetails();
auto sizeAndAlignment = abiDetails.getTypeSizeAlignment(decl);
if (sizeAndAlignment)
return sizeAndAlignment->size == 0;
return false;
}

bool DeclAndTypePrinter::isVisible(const ValueDecl *vd) const {
return outputLang == OutputLanguageMode::Cxx
? cxx_translation::isVisibleToCxx(vd, minRequiredAccess)
Expand Down
2 changes: 2 additions & 0 deletions lib/PrintAsClang/DeclAndTypePrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ class DeclAndTypePrinter {
/// the options the printer was constructed with.
bool shouldInclude(const ValueDecl *VD);

bool isZeroSized(const NominalTypeDecl *decl);

/// Returns true if \p vd is visible given the current access level and thus
/// can be included in the generated header.
bool isVisible(const ValueDecl *vd) const;
Expand Down
5 changes: 4 additions & 1 deletion lib/PrintAsClang/ModuleContentsWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -952,7 +952,10 @@ class ModuleWriter {

// Emit an unavailable stub for a Swift type.
if (auto *nmtd = dyn_cast<NominalTypeDecl>(vd)) {
auto representation = cxx_translation::getDeclRepresentation(vd);
auto representation = cxx_translation::getDeclRepresentation(
vd, [this](const NominalTypeDecl *decl) {
return printer.isZeroSized(decl);
});
if (nmtd->isGeneric()) {
auto genericSignature =
nmtd->getGenericSignature().getCanonicalSignature();
Expand Down
2 changes: 2 additions & 0 deletions lib/PrintAsClang/PrintClangValueType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ void ClangValueTypePrinter::printValueTypeDecl(
// e.g. it has resilient fields.
if (typeSizeAlign && typeSizeAlign->size == 0) {
// FIXME: How to represent 0 sized structs?
declAndTypePrinter.getCxxDeclEmissionScope()
.additionalUnrepresentableDeclarations.push_back(typeDecl);
return;
}
}
Expand Down
3 changes: 2 additions & 1 deletion lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
#include <optional>

using namespace swift;

Expand Down Expand Up @@ -2321,7 +2322,7 @@ void AttributeChecker::visitExposeAttr(ExposeAttr *attr) {
}

// Verify that the declaration is exposable.
auto repr = cxx_translation::getDeclRepresentation(VD);
auto repr = cxx_translation::getDeclRepresentation(VD, std::nullopt);
if (repr.isUnsupported())
diagnose(attr->getLocation(),
cxx_translation::diagnoseRepresenationError(*repr.error, VD));
Expand Down
41 changes: 41 additions & 0 deletions test/Interop/SwiftToCxx/structs/zero-sized-struct-in-cxx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,45 @@

public struct ZeroSizedStruct {}

public struct ZeroSizedStruct2 {
var property: ZeroSizedStruct
var void: Void
var bar: ()
public init() {
property = .init()
}
}

public enum ZeroSizedEnum {
}

public enum ZeroSizedEnum2 {
case foo
}

public enum ZeroSizedEnum3 {
case foo(ZeroSizedStruct, ZeroSizedEnum, ZeroSizedStruct2)
}

public func f() -> ZeroSizedStruct {
ZeroSizedStruct()
}

public func g(x: ZeroSizedStruct) {
}

// CHECK: class ZeroSizedEnum { } SWIFT_UNAVAILABLE_MSG("'ZeroSizedEnum' is a zero sized value type, it cannot be exposed to C++ yet");

// CHECK: class ZeroSizedEnum2 { } SWIFT_UNAVAILABLE_MSG("'ZeroSizedEnum2' is a zero sized value type, it cannot be exposed to C++ yet");

// CHECK: class ZeroSizedEnum3 { } SWIFT_UNAVAILABLE_MSG("'ZeroSizedEnum3' is a zero sized value type, it cannot be exposed to C++ yet");

// CHECK: class ZeroSizedStruct { } SWIFT_UNAVAILABLE_MSG("'ZeroSizedStruct' is a zero sized value type, it cannot be exposed to C++ yet");

// CHECK: class ZeroSizedStruct2 { } SWIFT_UNAVAILABLE_MSG("'ZeroSizedStruct2' is a zero sized value type, it cannot be exposed to C++ yet");

// CHECK: // Unavailable in C++: Swift global function 'f()'.

// CHECK: // Unavailable in C++: Swift global function 'g(x:)'.

// CHECK: } // namespace Structs