Skip to content
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
7 changes: 7 additions & 0 deletions include/swift/AST/ASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "swift/AST/AttrKind.h"
#include "swift/AST/DiagnosticKind.h"
#include "swift/AST/DiagnosticList.h"
#include "swift/AST/ExportKind.h"
#include "swift/AST/GenericTypeParamKind.h"
#include "swift/AST/Identifier.h"
#include "swift/AST/LayoutConstraintKind.h"
Expand Down Expand Up @@ -956,6 +957,12 @@ BridgedImplementsAttr BridgedImplementsAttr_createParsed(
swift::SourceRange range, BridgedTypeRepr cProtocolType,
BridgedDeclNameRef cMemberName, BridgedDeclNameLoc cMemberNameLoc);

SWIFT_NAME("BridgedExportAttr.createParsed(_:atLoc:range:kind:)")
BridgedExportAttr BridgedExportAttr_createParsed(BridgedASTContext cContext,
swift::SourceLoc atLoc,
swift::SourceRange range,
swift::ExportKind kind);

SWIFT_NAME("BridgedInlineAttr.createParsed(_:atLoc:range:kind:)")
BridgedInlineAttr BridgedInlineAttr_createParsed(BridgedASTContext cContext,
swift::SourceLoc atLoc,
Expand Down
30 changes: 30 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "swift/AST/AvailabilityRange.h"
#include "swift/AST/ConcreteDeclRef.h"
#include "swift/AST/DeclNameLoc.h"
#include "swift/AST/ExportKind.h"
#include "swift/AST/Identifier.h"
#include "swift/AST/KnownProtocols.h"
#include "swift/AST/LifetimeDependence.h"
Expand Down Expand Up @@ -2955,6 +2956,35 @@ enum class NonSendableKind : uint8_t {
Assumed
};

/// Specify whether the declaration should be exported as an interface or
/// an implementation.
class ExportAttr : public DeclAttribute {
public:
/// How this declaration is exported.
const ExportKind exportKind;

ExportAttr(SourceLoc atLoc, SourceRange range, ExportKind exportKind,
bool implicit = false)
: DeclAttribute(DeclAttrKind::Export, atLoc, range, implicit),
exportKind(exportKind) {}

ExportAttr(ExportKind exportKind, bool implicit = false)
: ExportAttr(SourceLoc(), SourceRange(), exportKind, implicit) { }

static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DeclAttrKind::Export;
}

/// Create a copy of this attribute.
ExportAttr *clone(ASTContext &ctx) const {
return new (ctx) ExportAttr(AtLoc, Range, exportKind, isImplicit());
}

bool isEquivalent(const ExportAttr *other, Decl *attachedTo) const {
return exportKind == other->exportKind;
}
};

/// Marks a declaration as explicitly non-Sendable.
class NonSendableAttr : public DeclAttribute {
public:
Expand Down
10 changes: 10 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1085,12 +1085,22 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
/// behaviors for it and, if it's an extension, its members.
bool isObjCImplementation() const;

/// True if this declaration should always have its implementation made
/// available to the client, and not have an ABI symbol.
///
/// This can be spelled with @export(implementation) or the historical
/// @_alwaysEmitIntoClient.
bool isAlwaysEmittedIntoClient() const;

/// True if this declaration should never have its implementation made
/// available to any client. This overrides cross-module optimization and
/// optimizations that might use the implementation, such that the only
/// implementation of this function is the one compiled into its owning
/// module. Practically speaking, this prohibits serialization of the SIL
/// for this definition.
///
/// This can be spelled with @export(interface) or the historical
/// @_neverEmitIntoClient.
bool isNeverEmittedIntoClient() const;

using AuxiliaryDeclCallback = llvm::function_ref<void(Decl *)>;
Expand Down
5 changes: 4 additions & 1 deletion include/swift/AST/DeclAttr.def
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,10 @@ SIMPLE_DECL_ATTR(noDerivative, NoDerivative,
ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | ForbiddenInABIAttr,
100)

// Unused '101'
DECL_ATTR(export, Export,
OnVar | OnSubscript | OnAbstractFunction,
ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr,
101)

CONTEXTUAL_SIMPLE_DECL_ATTR(actor, Actor,
OnClass,
Expand Down
6 changes: 5 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4170,6 +4170,10 @@ ERROR(final_not_allowed_here,none,
"'final' may only be applied to classes, properties, methods, and "
"subscripts", ())

ERROR(attr_incompatible_with_attr,none,
"'%0' cannot be used with '%1'",
(DeclAttribute, DeclAttribute))

ERROR(attr_incompatible_with_non_final,none,
"'%0' cannot be applied to a non-final %kindonly1",
(DeclAttribute, const ValueDecl *))
Expand Down Expand Up @@ -5369,7 +5373,7 @@ GROUPED_ERROR(opaque_type_unsupported_availability,OpaqueTypeInference,none,
"a default argument value|" \
"a property initializer in a '@frozen' type|" \
"a '@backDeployed' function|" \
"an embedded function not marked '@_neverEmitIntoClient'}"
"an embedded function not marked '@export(interface)'}"

ERROR(discard_wrong_context_decl,none,
"'discard' statement cannot appear in %kindonly0",
Expand Down
37 changes: 37 additions & 0 deletions include/swift/AST/ExportKind.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//===-- AST/ExportKind.h ----------------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_AST_EXPORT_KIND_H
#define SWIFT_AST_EXPORT_KIND_H

/// `ExportKind.h` is imported into Swift. Be *very* careful with what you
/// include here and keep these includes minimal!
///
/// See include guidelines and caveats in `BasicBridging.h`.
#include "swift/Basic/SwiftBridging.h"

namespace swift {

/// How a particular declaration is exported per the @export attribute.
enum class ENUM_EXTENSIBILITY_ATTR(closed) ExportKind {
/// Export only the interface to this declaration (e.g., as a callable symbol)
/// and never it's definition.
Interface SWIFT_NAME("interface"),

/// Export only the implementation of this declaration and do not produce a
/// symbol.
Implementation SWIFT_NAME("implementation"),
};

} // namespace swift

#endif // SWIFT_AST_EXPORT_KIND_H
2 changes: 2 additions & 0 deletions include/swift/AST/KnownIdentifiers.def
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ IDENTIFIER(always)
IDENTIFIER_(_always)
IDENTIFIER_(assumed)
IDENTIFIER(checked)
IDENTIFIER(interface)
IDENTIFIER(implementation)
IDENTIFIER(never)
IDENTIFIER(none)
IDENTIFIER(safe)
Expand Down
2 changes: 1 addition & 1 deletion include/swift/SIL/SILFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1421,7 +1421,7 @@ class SILFunction
return false;

auto *V = getLocation().getAsASTNode<ValueDecl>();
return V && V->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>();
return V && V->isAlwaysEmittedIntoClient();
}

/// Return whether this function has attribute @used on it
Expand Down
2 changes: 1 addition & 1 deletion lib/APIDigester/ModuleAnalyzerNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1764,7 +1764,7 @@ SDKContext::shouldIgnore(Decl *D, const Decl* Parent) const {
// Exclude decls with @_alwaysEmitIntoClient if we are checking ABI.
// These decls are considered effectively public because they are usable
// from inline, so we have to manually exclude them here.
if (D->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
if (D->isAlwaysEmittedIntoClient())
return true;
} else {
if (D->isPrivateSystemDecl(false))
Expand Down
14 changes: 14 additions & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,15 @@ static StringRef getDumpString(InlineKind kind) {
}
llvm_unreachable("unhandled InlineKind");
}
static StringRef getDumpString(ExportKind kind) {
switch (kind) {
case ExportKind::Interface:
return "interface";
case ExportKind::Implementation:
return "implementation";
}
llvm_unreachable("unhandled ExportKind");
}
static StringRef getDumpString(MacroRole role) {
return getMacroRoleString(role);
}
Expand Down Expand Up @@ -5306,6 +5315,11 @@ class PrintAttribute : public AttributeVisitor<PrintAttribute, void, Label>,
printField(Attr->getKind(), Label::always("kind"));
printFoot();
}
void visitExportAttr(ExportAttr *Attr, Label label) {
printCommon(Attr, "export_attr", label);
printField(Attr->exportKind, Label::always("kind"));
printFoot();
}
void visitLifetimeAttr(LifetimeAttr *Attr, Label label) {
printCommon(Attr, "lifetime_attr", label);
// FIXME: Improve, more detailed info.
Expand Down
10 changes: 10 additions & 0 deletions lib/AST/Attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
case DeclAttrKind::AccessControl:
case DeclAttrKind::ReferenceOwnership:
case DeclAttrKind::Effects:
case DeclAttrKind::Export:
case DeclAttrKind::Optimize:
case DeclAttrKind::Exclusivity:
case DeclAttrKind::NonSendable:
Expand Down Expand Up @@ -1932,6 +1933,15 @@ StringRef DeclAttribute::getAttrName() const {
case EffectsKind::Custom:
return "_effects";
}
case DeclAttrKind::Export: {
switch (cast<ExportAttr>(this)->exportKind) {
case ExportKind::Interface:
return "export(interface)";
case ExportKind::Implementation:
return "export(implementation)";
}
llvm_unreachable("Invalid export kind");
}
case DeclAttrKind::AccessControl:
case DeclAttrKind::SetterAccess: {
AccessLevel access = cast<AbstractAccessControlAttr>(this)->getAccess();
Expand Down
8 changes: 8 additions & 0 deletions lib/AST/Bridging/DeclAttributeBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,14 @@ BridgedImplementsAttr BridgedImplementsAttr_createParsed(
cMemberName.unbridged().getFullName(), cMemberNameLoc.unbridged());
}

BridgedExportAttr BridgedExportAttr_createParsed(BridgedASTContext cContext,
SourceLoc atLoc,
SourceRange range,
swift::ExportKind kind) {
return new (cContext.unbridged()) ExportAttr(atLoc, range, kind);
}


BridgedInlineAttr BridgedInlineAttr_createParsed(BridgedASTContext cContext,
SourceLoc atLoc,
SourceRange range,
Expand Down
32 changes: 28 additions & 4 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2276,8 +2276,32 @@ bool Decl::isObjCImplementation() const {
return getAttrs().hasAttribute<ObjCImplementationAttr>(/*AllowInvalid=*/true);
}

bool Decl::isAlwaysEmittedIntoClient() const {
// @export(implementation)
if (auto exportAttr = getAttrs().getAttribute<ExportAttr>()) {
if (exportAttr->exportKind == ExportKind::Implementation)
return true;
}

// @_alwaysEmitIntoClient
if (getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
return true;

return false;
}

bool Decl::isNeverEmittedIntoClient() const {
return getAttrs().hasAttribute<NeverEmitIntoClientAttr>();
// @export(interface)
if (auto exportAttr = getAttrs().getAttribute<ExportAttr>()) {
if (exportAttr->exportKind == ExportKind::Interface)
return true;
}

// @_neverEmitIntoClient
if (getAttrs().hasAttribute<NeverEmitIntoClientAttr>())
return true;

return false;
}

PatternBindingDecl::PatternBindingDecl(SourceLoc StaticLoc,
Expand Down Expand Up @@ -4962,22 +4986,22 @@ bool ValueDecl::isUsableFromInline() const {
return abiRole.getCounterpart()->isUsableFromInline();

if (getAttrs().hasAttribute<UsableFromInlineAttr>() ||
getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>() ||
isAlwaysEmittedIntoClient() ||
hasAttributeWithInlinableSemantics())
return true;

if (auto *accessor = dyn_cast<AccessorDecl>(this)) {
auto *storage = accessor->getStorage();
if (storage->getAttrs().hasAttribute<UsableFromInlineAttr>() ||
storage->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>() ||
storage->isAlwaysEmittedIntoClient() ||
storage->hasAttributeWithInlinableSemantics())
return true;
}

if (auto *opaqueType = dyn_cast<OpaqueTypeDecl>(this)) {
if (auto *namingDecl = opaqueType->getNamingDecl()) {
if (namingDecl->getAttrs().hasAttribute<UsableFromInlineAttr>() ||
namingDecl->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>() ||
namingDecl->isAlwaysEmittedIntoClient() ||
namingDecl->hasAttributeWithInlinableSemantics())
return true;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,
return {FragileFunctionKind::Inlinable};
}

if (AFD->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>()) {
if (AFD->isAlwaysEmittedIntoClient()) {
return {FragileFunctionKind::AlwaysEmitIntoClient};
}

Expand All @@ -608,7 +608,7 @@ swift::FragileFunctionKindRequest::evaluate(Evaluator &evaluator,
if (storage->hasAttributeWithInlinableSemantics()) {
return {FragileFunctionKind::Inlinable};
}
if (storage->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>()) {
if (storage->isAlwaysEmittedIntoClient()) {
return {FragileFunctionKind::AlwaysEmitIntoClient};
}
if (storage->isBackDeployed()) {
Expand Down
5 changes: 2 additions & 3 deletions lib/AST/SwiftNameTranslation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,8 @@ swift::cxx_translation::getDeclRepresentation(
if (isa<MacroDecl>(VD))
return {Unsupported, UnrepresentableMacro};
GenericSignature genericSignature;
// Don't expose @_alwaysEmitIntoClient decls as they require their
// bodies to be emitted into client.
if (VD->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
// Don't expose decls with definitions that are emitted into the client.
if (VD->isAlwaysEmittedIntoClient())
return {Unsupported, UnrepresentableRequiresClientEmission};
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(VD)) {
if (AFD->hasAsync())
Expand Down
29 changes: 29 additions & 0 deletions lib/ASTGen/Sources/ASTGen/DeclAttrs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ extension ASTGenVisitor {
return handle(self.generateEffectsAttr(attribute: node)?.asDeclAttribute)
case .Exclusivity:
return handle(self.generateExclusivityAttr(attribute: node)?.asDeclAttribute)
case .Export:
return handle(self.generateExportAttr(attribute: node)?.asDeclAttribute)
case .Expose:
return handle(self.generateExposeAttr(attribute: node)?.asDeclAttribute)
case .Extern:
Expand Down Expand Up @@ -1071,6 +1073,33 @@ extension ASTGenVisitor {
)
}

/// E.g.:
/// ```
/// @export(interface)
/// @export(implementation)
/// ```
func generateExportAttr(attribute node: AttributeSyntax) -> BridgedExportAttr? {
let kind: swift.ExportKind? = self.generateSingleAttrOption(
attribute: node,
{
switch $0.rawText {
case "interface": return .interface
case "implementation": return .implementation
default: return nil
}
}
)
guard let kind else {
return nil
}
return .createParsed(
self.ctx,
atLoc: self.generateSourceLoc(node.atSign),
range: self.generateAttrSourceRange(node),
kind: kind
)
}

/// E.g.:
/// ```
/// @inline(never)
Expand Down
Loading