mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[cxx-interop] Add attribute to hide Swift declarations from interop
Explanation: We generate declarations in the C++ interop header with "unavailable" annotations when we cannot export something to C++. These declarations can collide with existing names. Previously, there were no ways to resolve these name collisions. This PR introduces a new attribute to hide declarations from the interop header. Issues: rdar://158843666 Original PRs: #82616 Risk: Low, this adds a new, straightforward code path. Testing: Added a compiler test. Reviewers: @egorzhdan
This commit is contained in:
committed by
Gabor Horvath
parent
f15d600460
commit
fa65cc9b2b
@@ -105,6 +105,7 @@ enum : unsigned { NumEffectsKindBits =
|
||||
/// This enum represents the possible values of the @_expose attribute.
|
||||
enum class ExposureKind: uint8_t {
|
||||
Cxx,
|
||||
NotCxx,
|
||||
Wasm,
|
||||
Last_ExposureKind = Wasm
|
||||
};
|
||||
|
||||
@@ -2132,6 +2132,8 @@ ERROR(expose_only_non_other_attr,none,
|
||||
ERROR(expose_inside_unexposed_decl,none,
|
||||
"'@_expose' cannot be applied inside of unexposed declaration %0",
|
||||
(const ValueDecl *))
|
||||
ERROR(expose_redundant_name_provided, none,
|
||||
"'@_expose(!Cxx)' does not accept a name argument", ())
|
||||
ERROR(expose_invalid_name_pattern_init,none,
|
||||
"invalid declaration name '%0' specified in '@_expose'; "
|
||||
"exposed initializer name must start with 'init'", (StringRef))
|
||||
|
||||
@@ -1253,6 +1253,9 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
|
||||
case ExposureKind::Cxx:
|
||||
Printer << "(Cxx";
|
||||
break;
|
||||
case ExposureKind::NotCxx:
|
||||
Printer << "(!Cxx";
|
||||
break;
|
||||
}
|
||||
if (!cast<ExposeAttr>(this)->Name.empty())
|
||||
Printer << ", \"" << cast<ExposeAttr>(this)->Name << "\"";
|
||||
|
||||
@@ -3006,13 +3006,22 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
|
||||
"Cxx");
|
||||
ParseSymbolName = false;
|
||||
};
|
||||
bool isNegated = false;
|
||||
if (Tok.is(tok::oper_prefix) && Tok.getText() == "!") {
|
||||
isNegated = true;
|
||||
consumeToken(tok::oper_prefix);
|
||||
}
|
||||
if (Tok.isNot(tok::identifier)) {
|
||||
diagnoseExpectOption();
|
||||
return makeParserSuccess();
|
||||
}
|
||||
if (Tok.getText() == "Cxx") {
|
||||
ExpKind = ExposureKind::Cxx;
|
||||
ExpKind = isNegated ? ExposureKind::NotCxx : ExposureKind::Cxx;
|
||||
} else if (Tok.getText() == "wasm") {
|
||||
if (isNegated) {
|
||||
diagnoseExpectOption();
|
||||
return makeParserSuccess();
|
||||
}
|
||||
ExpKind = ExposureKind::Wasm;
|
||||
} else {
|
||||
diagnoseExpectOption();
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "swift/AST/ASTMangler.h"
|
||||
#include "swift/AST/ASTVisitor.h"
|
||||
#include "swift/AST/Attr.h"
|
||||
#include "swift/AST/ClangSwiftTypeCorrespondence.h"
|
||||
#include "swift/AST/Comment.h"
|
||||
#include "swift/AST/ConformanceLookup.h"
|
||||
@@ -2935,6 +2936,17 @@ static bool excludeForObjCImplementation(const ValueDecl *VD) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool swift::hasExposeNotCxxAttr(const ValueDecl *VD) {
|
||||
for (const auto *attr : VD->getAttrs().getAttributes<ExposeAttr>())
|
||||
if (attr->getExposureKind() == ExposureKind::NotCxx)
|
||||
return true;
|
||||
if (const auto *NMT = dyn_cast<NominalTypeDecl>(VD->getDeclContext()))
|
||||
return hasExposeNotCxxAttr(NMT);
|
||||
if (const auto *ED = dyn_cast<ExtensionDecl>(VD->getDeclContext()))
|
||||
return hasExposeNotCxxAttr(ED->getExtendedNominal());
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isExposedToThisModule(const ModuleDecl &M, const ValueDecl *VD,
|
||||
const llvm::StringSet<> &exposedModules) {
|
||||
if (VD->hasClangNode())
|
||||
@@ -2986,6 +2998,9 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) {
|
||||
if (requiresExposedAttribute && !hasExposeAttr(VD))
|
||||
return false;
|
||||
|
||||
if (hasExposeNotCxxAttr(VD))
|
||||
return false;
|
||||
|
||||
if (!isVisible(VD))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -160,6 +160,8 @@ public:
|
||||
|
||||
bool isStringNestedType(const ValueDecl *VD, StringRef Typename);
|
||||
|
||||
bool hasExposeNotCxxAttr(const ValueDecl *VD);
|
||||
|
||||
} // end namespace swift
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1018,7 +1018,8 @@ public:
|
||||
// library. Also skip structs from the standard library, they can cause
|
||||
// ambiguities because of the arithmetic types that conflict with types we
|
||||
// already have in `swift::` namespace. Also skip `Error` protocol from
|
||||
// stdlib, we have experimental support for it.
|
||||
// stdlib, we have experimental support for it. Also skip explicitly exluded
|
||||
// declarations.
|
||||
removedVDList.erase(
|
||||
llvm::remove_if(
|
||||
removedVDList,
|
||||
@@ -1028,7 +1029,8 @@ public:
|
||||
vd->getBaseIdentifier().hasUnderscoredNaming()) ||
|
||||
(vd->isStdlibDecl() && isa<StructDecl>(vd)) ||
|
||||
(vd->isStdlibDecl() &&
|
||||
vd->getASTContext().getErrorDecl() == vd);
|
||||
vd->getASTContext().getErrorDecl() == vd) ||
|
||||
swift::hasExposeNotCxxAttr(vd);
|
||||
}),
|
||||
removedVDList.end());
|
||||
// Sort the unavaiable decls by their name and kind.
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "TypeCheckType.h"
|
||||
#include "TypeChecker.h"
|
||||
#include "swift/AST/ASTVisitor.h"
|
||||
#include "swift/AST/Attr.h"
|
||||
#include "swift/AST/AttrKind.h"
|
||||
#include "swift/AST/AvailabilityInference.h"
|
||||
#include "swift/AST/ClangModuleLoader.h"
|
||||
#include "swift/AST/ConformanceLookup.h"
|
||||
@@ -2404,6 +2406,14 @@ void AttributeChecker::visitExposeAttr(ExposeAttr *attr) {
|
||||
diagnose(attr->getLocation(), diag::expose_wasm_not_at_top_level_func);
|
||||
break;
|
||||
}
|
||||
case ExposureKind::NotCxx:
|
||||
for (const auto *attr : D->getAttrs().getAttributes<ExposeAttr>())
|
||||
if (attr->getExposureKind() == ExposureKind::Cxx)
|
||||
diagnose(attr->getLocation(), diag::expose_only_non_other_attr,
|
||||
"@_expose(Cxx)");
|
||||
if (!attr->Name.empty())
|
||||
diagnose(attr->getLocation(), diag::expose_redundant_name_provided);
|
||||
break;
|
||||
case ExposureKind::Cxx: {
|
||||
auto *VD = cast<ValueDecl>(D);
|
||||
// Expose cannot be mixed with '@_cdecl' declarations.
|
||||
|
||||
33
test/Interop/SwiftToCxx/expose-attr/hidden-decls.swift
Normal file
33
test/Interop/SwiftToCxx/expose-attr/hidden-decls.swift
Normal file
@@ -0,0 +1,33 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: %target-swift-frontend %s -module-name SwiftPrivate -clang-header-expose-decls=all-public -typecheck -verify -emit-clang-header-path %t/swiftprivate.h
|
||||
// RUN: %FileCheck %s < %t/swiftprivate.h
|
||||
|
||||
// RUN: %check-interop-cxx-header-in-clang(%t/swiftprivate.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY)
|
||||
|
||||
public struct Exposed {
|
||||
public var x: Int
|
||||
@_expose(!Cxx)
|
||||
public var notExposedField: Int
|
||||
}
|
||||
|
||||
@_expose(!Cxx)
|
||||
public struct NotExposed {
|
||||
public var x: Int
|
||||
}
|
||||
|
||||
extension NotExposed {
|
||||
func notExposed() {}
|
||||
}
|
||||
|
||||
@_expose(!Cxx)
|
||||
public func NotExposedfunction() {}
|
||||
|
||||
@MainActor
|
||||
@_expose(!Cxx)
|
||||
public class UnavailableClass {
|
||||
}
|
||||
|
||||
// CHECK-NOT: NotExposed
|
||||
// CHECK-NOT: notExposed
|
||||
// CHECK: Exposed
|
||||
// CHECK-NOT: UnavailableClass
|
||||
@@ -15,6 +15,15 @@ func incorrectLangSpecifier() {}
|
||||
@_expose(Cxx) @_cdecl("test") // expected-error {{'@_expose' cannot be applied to an '@_cdecl' declaration}}
|
||||
func cdeclAndExpose() {}
|
||||
|
||||
@_expose(Cxx) @_expose(!Cxx) // expected-error {{'@_expose' cannot be applied to an '@_expose(Cxx)' declaration}}
|
||||
func contradictingExpose() {}
|
||||
|
||||
@_expose(!Cxx) @_expose(Cxx) // expected-error {{'@_expose' cannot be applied to an '@_expose(Cxx)' declaration}}
|
||||
func contradictingExpose2() {}
|
||||
|
||||
@_expose(!Cxx, "name") // expected-error {{'@_expose(!Cxx)' does not accept a name argument}}
|
||||
func notExposeWithName() {}
|
||||
|
||||
func hasNested() {
|
||||
@_expose(Cxx) // expected-error{{can only be used in a non-local scope}}
|
||||
func nested() { }
|
||||
|
||||
Reference in New Issue
Block a user