diff --git a/include/swift/AST/AttrKind.h b/include/swift/AST/AttrKind.h index ae2e7ffaeeb4d..64cec824423f0 100644 --- a/include/swift/AST/AttrKind.h +++ b/include/swift/AST/AttrKind.h @@ -99,6 +99,7 @@ enum class ENUM_EXTENSIBILITY_ATTR(closed) EffectsKind : uint8_t { /// This enum represents the possible values of the @_expose attribute. enum class ENUM_EXTENSIBILITY_ATTR(closed) ExposureKind : uint8_t { Cxx SWIFT_NAME("cxx"), + NotCxx SWIFT_NAME("notcxx"), Wasm SWIFT_NAME("wasm"), Last_ExposureKind = Wasm }; diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 0ddafdac9ce32..0a38e83b5a36b 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2174,6 +2174,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)) diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index a07eaf612e628..c41815a2a84c3 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1249,6 +1249,9 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, case ExposureKind::Cxx: Printer << "(Cxx"; break; + case ExposureKind::NotCxx: + Printer << "(!Cxx"; + break; } if (!cast(this)->Name.empty()) Printer << ", \"" << cast(this)->Name << "\""; diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index e45122b199e95..e008914b2ad2c 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3079,13 +3079,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(); diff --git a/lib/PrintAsClang/DeclAndTypePrinter.cpp b/lib/PrintAsClang/DeclAndTypePrinter.cpp index a88f2bd0c7c33..a8ffabd4bbaad 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.cpp +++ b/lib/PrintAsClang/DeclAndTypePrinter.cpp @@ -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" @@ -2976,6 +2977,17 @@ static bool excludeForObjCImplementation(const ValueDecl *VD) { return false; } +bool swift::hasExposeNotCxxAttr(const ValueDecl *VD) { + for (const auto *attr : VD->getAttrs().getAttributes()) + if (attr->getExposureKind() == ExposureKind::NotCxx) + return true; + if (const auto *NMT = dyn_cast(VD->getDeclContext())) + return hasExposeNotCxxAttr(NMT); + if (const auto *ED = dyn_cast(VD->getDeclContext())) + return hasExposeNotCxxAttr(ED->getExtendedNominal()); + return false; +} + static bool isExposedToThisModule(const ModuleDecl &M, const ValueDecl *VD, const llvm::StringSet<> &exposedModules) { if (VD->hasClangNode()) @@ -3027,6 +3039,9 @@ bool DeclAndTypePrinter::shouldInclude(const ValueDecl *VD) { if (requiresExposedAttribute && !hasExposeAttr(VD)) return false; + if (hasExposeNotCxxAttr(VD)) + return false; + if (!isVisible(VD)) return false; diff --git a/lib/PrintAsClang/DeclAndTypePrinter.h b/lib/PrintAsClang/DeclAndTypePrinter.h index 55c7a57344a41..a824e82b6d9db 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.h +++ b/lib/PrintAsClang/DeclAndTypePrinter.h @@ -160,6 +160,8 @@ class DeclAndTypePrinter { bool isStringNestedType(const ValueDecl *VD, StringRef Typename); +bool hasExposeNotCxxAttr(const ValueDecl *VD); + } // end namespace swift #endif diff --git a/lib/PrintAsClang/ModuleContentsWriter.cpp b/lib/PrintAsClang/ModuleContentsWriter.cpp index f9f3c2364a225..55314f84a2f9a 100644 --- a/lib/PrintAsClang/ModuleContentsWriter.cpp +++ b/lib/PrintAsClang/ModuleContentsWriter.cpp @@ -1063,7 +1063,8 @@ class ModuleWriter { // 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, @@ -1073,7 +1074,8 @@ class ModuleWriter { vd->getBaseIdentifier().hasUnderscoredNaming()) || (vd->isStdlibDecl() && isa(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. diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 1fd363ae586ee..af34b013ca4b8 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -24,6 +24,8 @@ #include "TypeChecker.h" #include "swift/AST/ASTPrinter.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" @@ -2462,6 +2464,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()) + 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(D); // Expose cannot be mixed with '@_cdecl' declarations. diff --git a/test/Interop/SwiftToCxx/expose-attr/hidden-decls.swift b/test/Interop/SwiftToCxx/expose-attr/hidden-decls.swift new file mode 100644 index 0000000000000..b043ed5f1ec17 --- /dev/null +++ b/test/Interop/SwiftToCxx/expose-attr/hidden-decls.swift @@ -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 diff --git a/test/attr/attr_expose.swift b/test/attr/attr_expose.swift index 752e28186f8d9..71209b4c548ac 100644 --- a/test/attr/attr_expose.swift +++ b/test/attr/attr_expose.swift @@ -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() { }