Skip to content

Commit fa65cc9

Browse files
Xazax-hunGabor Horvath
authored andcommitted
[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: swiftlang#82616 Risk: Low, this adds a new, straightforward code path. Testing: Added a compiler test. Reviewers: @egorzhdan
1 parent f15d600 commit fa65cc9

File tree

10 files changed

+89
-3
lines changed

10 files changed

+89
-3
lines changed

include/swift/AST/AttrKind.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ enum : unsigned { NumEffectsKindBits =
105105
/// This enum represents the possible values of the @_expose attribute.
106106
enum class ExposureKind: uint8_t {
107107
Cxx,
108+
NotCxx,
108109
Wasm,
109110
Last_ExposureKind = Wasm
110111
};

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,6 +2132,8 @@ ERROR(expose_only_non_other_attr,none,
21322132
ERROR(expose_inside_unexposed_decl,none,
21332133
"'@_expose' cannot be applied inside of unexposed declaration %0",
21342134
(const ValueDecl *))
2135+
ERROR(expose_redundant_name_provided, none,
2136+
"'@_expose(!Cxx)' does not accept a name argument", ())
21352137
ERROR(expose_invalid_name_pattern_init,none,
21362138
"invalid declaration name '%0' specified in '@_expose'; "
21372139
"exposed initializer name must start with 'init'", (StringRef))

lib/AST/Attr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,9 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
12531253
case ExposureKind::Cxx:
12541254
Printer << "(Cxx";
12551255
break;
1256+
case ExposureKind::NotCxx:
1257+
Printer << "(!Cxx";
1258+
break;
12561259
}
12571260
if (!cast<ExposeAttr>(this)->Name.empty())
12581261
Printer << ", \"" << cast<ExposeAttr>(this)->Name << "\"";

lib/Parse/ParseDecl.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3006,13 +3006,22 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
30063006
"Cxx");
30073007
ParseSymbolName = false;
30083008
};
3009+
bool isNegated = false;
3010+
if (Tok.is(tok::oper_prefix) && Tok.getText() == "!") {
3011+
isNegated = true;
3012+
consumeToken(tok::oper_prefix);
3013+
}
30093014
if (Tok.isNot(tok::identifier)) {
30103015
diagnoseExpectOption();
30113016
return makeParserSuccess();
30123017
}
30133018
if (Tok.getText() == "Cxx") {
3014-
ExpKind = ExposureKind::Cxx;
3019+
ExpKind = isNegated ? ExposureKind::NotCxx : ExposureKind::Cxx;
30153020
} else if (Tok.getText() == "wasm") {
3021+
if (isNegated) {
3022+
diagnoseExpectOption();
3023+
return makeParserSuccess();
3024+
}
30163025
ExpKind = ExposureKind::Wasm;
30173026
} else {
30183027
diagnoseExpectOption();

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/AST/ASTContext.h"
2323
#include "swift/AST/ASTMangler.h"
2424
#include "swift/AST/ASTVisitor.h"
25+
#include "swift/AST/Attr.h"
2526
#include "swift/AST/ClangSwiftTypeCorrespondence.h"
2627
#include "swift/AST/Comment.h"
2728
#include "swift/AST/ConformanceLookup.h"
@@ -2935,6 +2936,17 @@ static bool excludeForObjCImplementation(const ValueDecl *VD) {
29352936
return false;
29362937
}
29372938

2939+
bool swift::hasExposeNotCxxAttr(const ValueDecl *VD) {
2940+
for (const auto *attr : VD->getAttrs().getAttributes<ExposeAttr>())
2941+
if (attr->getExposureKind() == ExposureKind::NotCxx)
2942+
return true;
2943+
if (const auto *NMT = dyn_cast<NominalTypeDecl>(VD->getDeclContext()))
2944+
return hasExposeNotCxxAttr(NMT);
2945+
if (const auto *ED = dyn_cast<ExtensionDecl>(VD->getDeclContext()))
2946+
return hasExposeNotCxxAttr(ED->getExtendedNominal());
2947+
return false;
2948+
}
2949+
29382950
static bool isExposedToThisModule(const ModuleDecl &M, const ValueDecl *VD,
29392951
const llvm::StringSet<> &exposedModules) {
29402952
if (VD->hasClangNode())
@@ -2986,6 +2998,9 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) {
29862998
if (requiresExposedAttribute && !hasExposeAttr(VD))
29872999
return false;
29883000

3001+
if (hasExposeNotCxxAttr(VD))
3002+
return false;
3003+
29893004
if (!isVisible(VD))
29903005
return false;
29913006

lib/PrintAsClang/DeclAndTypePrinter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ class DeclAndTypePrinter {
160160

161161
bool isStringNestedType(const ValueDecl *VD, StringRef Typename);
162162

163+
bool hasExposeNotCxxAttr(const ValueDecl *VD);
164+
163165
} // end namespace swift
164166

165167
#endif

lib/PrintAsClang/ModuleContentsWriter.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,8 @@ class ModuleWriter {
10181018
// library. Also skip structs from the standard library, they can cause
10191019
// ambiguities because of the arithmetic types that conflict with types we
10201020
// already have in `swift::` namespace. Also skip `Error` protocol from
1021-
// stdlib, we have experimental support for it.
1021+
// stdlib, we have experimental support for it. Also skip explicitly exluded
1022+
// declarations.
10221023
removedVDList.erase(
10231024
llvm::remove_if(
10241025
removedVDList,
@@ -1028,7 +1029,8 @@ class ModuleWriter {
10281029
vd->getBaseIdentifier().hasUnderscoredNaming()) ||
10291030
(vd->isStdlibDecl() && isa<StructDecl>(vd)) ||
10301031
(vd->isStdlibDecl() &&
1031-
vd->getASTContext().getErrorDecl() == vd);
1032+
vd->getASTContext().getErrorDecl() == vd) ||
1033+
swift::hasExposeNotCxxAttr(vd);
10321034
}),
10331035
removedVDList.end());
10341036
// Sort the unavaiable decls by their name and kind.

lib/Sema/TypeCheckAttr.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include "TypeCheckType.h"
2424
#include "TypeChecker.h"
2525
#include "swift/AST/ASTVisitor.h"
26+
#include "swift/AST/Attr.h"
27+
#include "swift/AST/AttrKind.h"
2628
#include "swift/AST/AvailabilityInference.h"
2729
#include "swift/AST/ClangModuleLoader.h"
2830
#include "swift/AST/ConformanceLookup.h"
@@ -2404,6 +2406,14 @@ void AttributeChecker::visitExposeAttr(ExposeAttr *attr) {
24042406
diagnose(attr->getLocation(), diag::expose_wasm_not_at_top_level_func);
24052407
break;
24062408
}
2409+
case ExposureKind::NotCxx:
2410+
for (const auto *attr : D->getAttrs().getAttributes<ExposeAttr>())
2411+
if (attr->getExposureKind() == ExposureKind::Cxx)
2412+
diagnose(attr->getLocation(), diag::expose_only_non_other_attr,
2413+
"@_expose(Cxx)");
2414+
if (!attr->Name.empty())
2415+
diagnose(attr->getLocation(), diag::expose_redundant_name_provided);
2416+
break;
24072417
case ExposureKind::Cxx: {
24082418
auto *VD = cast<ValueDecl>(D);
24092419
// Expose cannot be mixed with '@_cdecl' declarations.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %s -module-name SwiftPrivate -clang-header-expose-decls=all-public -typecheck -verify -emit-clang-header-path %t/swiftprivate.h
3+
// RUN: %FileCheck %s < %t/swiftprivate.h
4+
5+
// RUN: %check-interop-cxx-header-in-clang(%t/swiftprivate.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY)
6+
7+
public struct Exposed {
8+
public var x: Int
9+
@_expose(!Cxx)
10+
public var notExposedField: Int
11+
}
12+
13+
@_expose(!Cxx)
14+
public struct NotExposed {
15+
public var x: Int
16+
}
17+
18+
extension NotExposed {
19+
func notExposed() {}
20+
}
21+
22+
@_expose(!Cxx)
23+
public func NotExposedfunction() {}
24+
25+
@MainActor
26+
@_expose(!Cxx)
27+
public class UnavailableClass {
28+
}
29+
30+
// CHECK-NOT: NotExposed
31+
// CHECK-NOT: notExposed
32+
// CHECK: Exposed
33+
// CHECK-NOT: UnavailableClass

test/attr/attr_expose.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ func incorrectLangSpecifier() {}
1515
@_expose(Cxx) @_cdecl("test") // expected-error {{'@_expose' cannot be applied to an '@_cdecl' declaration}}
1616
func cdeclAndExpose() {}
1717

18+
@_expose(Cxx) @_expose(!Cxx) // expected-error {{'@_expose' cannot be applied to an '@_expose(Cxx)' declaration}}
19+
func contradictingExpose() {}
20+
21+
@_expose(!Cxx) @_expose(Cxx) // expected-error {{'@_expose' cannot be applied to an '@_expose(Cxx)' declaration}}
22+
func contradictingExpose2() {}
23+
24+
@_expose(!Cxx, "name") // expected-error {{'@_expose(!Cxx)' does not accept a name argument}}
25+
func notExposeWithName() {}
26+
1827
func hasNested() {
1928
@_expose(Cxx) // expected-error{{can only be used in a non-local scope}}
2029
func nested() { }

0 commit comments

Comments
 (0)