Skip to content

SE-0460: Introduce @specialized attribute #81714

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
May 28, 2025
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
9 changes: 9 additions & 0 deletions include/swift/AST/ASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,15 @@ BridgedSpecializeAttr BridgedSpecializeAttr_createParsed(
BridgedDeclNameRef cTargetFunction, BridgedArrayRef cSPIGroups,
BridgedArrayRef cAvailableAttrs);

SWIFT_NAME("BridgedSpecializedAttr.createParsed(_:atLoc:range:whereClause:"
"exported:kind:taretFunction:spiGroups:availableAttrs:)")
BridgedSpecializedAttr BridgedSpecializedAttr_createParsed(
BridgedASTContext cContext, BridgedSourceLoc cAtLoc,
BridgedSourceRange cRange, BridgedNullableTrailingWhereClause cWhereClause,
bool exported, BridgedSpecializationKind cKind,
BridgedDeclNameRef cTargetFunction, BridgedArrayRef cSPIGroups,
BridgedArrayRef cAvailableAttrs);

SWIFT_NAME(
"BridgedSPIAccessControlAttr.createParsed(_:atLoc:range:spiGroupName:)")
BridgedSPIAccessControlAttr BridgedSPIAccessControlAttr_createParsed(
Expand Down
6 changes: 3 additions & 3 deletions include/swift/AST/ASTScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class GenericParamList;
class TrailingWhereClause;
class ParameterList;
class PatternBindingEntry;
class SpecializeAttr;
class AbstractSpecializeAttr;
class GenericContext;
class DeclName;
class StmtConditionElement;
Expand Down Expand Up @@ -1241,10 +1241,10 @@ class TopLevelCodeScope final : public ASTScopeImpl {
/// The \c _@specialize attribute.
class SpecializeAttributeScope final : public ASTScopeImpl {
public:
SpecializeAttr *const specializeAttr;
AbstractSpecializeAttr *const specializeAttr;
AbstractFunctionDecl *const whatWasSpecialized;

SpecializeAttributeScope(SpecializeAttr *specializeAttr,
SpecializeAttributeScope(AbstractSpecializeAttr *specializeAttr,
AbstractFunctionDecl *whatWasSpecialized)
: ASTScopeImpl(ScopeKind::SpecializeAttribute),
specializeAttr(specializeAttr), whatWasSpecialized(whatWasSpecialized) {
Expand Down
205 changes: 165 additions & 40 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

#ifndef SWIFT_ATTR_H
#define SWIFT_ATTR_H

#include "swift/AST/ASTAllocated.h"
#include "swift/AST/AttrKind.h"
#include "swift/AST/AutoDiff.h"
Expand Down Expand Up @@ -203,7 +202,7 @@ class DeclAttribute : public AttributeBase {
ownership : NumReferenceOwnershipBits
);

SWIFT_INLINE_BITFIELD(SpecializeAttr, DeclAttribute, 1+1,
SWIFT_INLINE_BITFIELD(AbstractSpecializeAttr, DeclAttribute, 1+1,
exported : 1,
kind : 1
);
Expand Down Expand Up @@ -1730,15 +1729,17 @@ class SynthesizedProtocolAttr : public DeclAttribute {
}
};

/// The @_specialize attribute, which forces specialization on the specified
/// type list.
class SpecializeAttr final
/// The @_specialize/@specialize attribute, which forces specialization on the
/// specified type list.
template<typename Base, typename...AdditionalTrailingObjects>
using SpecializeAttrTrailingObjects = llvm::TrailingObjects<Base,
Identifier, AvailableAttr *, Type , AdditionalTrailingObjects...>;

class AbstractSpecializeAttr
: public DeclAttribute,
private llvm::TrailingObjects<SpecializeAttr, Identifier,
AvailableAttr *, Type> {
private llvm::trailing_objects_internal::TrailingObjectsBase {
friend class SpecializeAttrTargetDeclRequest;
friend class SerializeAttrGenericSignatureRequest;
friend TrailingObjects;

public:
// NOTE: When adding new kinds, you must update the inline bitfield macro.
Expand All @@ -1759,82 +1760,81 @@ class SpecializeAttr final
size_t numTypeErasedParams;
bool typeErasedParamsInitialized;

SpecializeAttr(SourceLoc atLoc, SourceRange Range,
TrailingWhereClause *clause, bool exported,
protected:
AbstractSpecializeAttr(DeclAttrKind DK, SourceLoc atLoc, SourceRange Range,
TrailingWhereClause *clause,
bool exported,
SpecializationKind kind, GenericSignature specializedSignature,
DeclNameRef targetFunctionName, ArrayRef<Identifier> spiGroups,
ArrayRef<AvailableAttr *> availabilityAttrs,
size_t typeErasedParamsCount);

public:
static SpecializeAttr *
create(ASTContext &Ctx, SourceLoc atLoc, SourceRange Range,
TrailingWhereClause *clause, bool exported, SpecializationKind kind,
DeclNameRef targetFunctionName, ArrayRef<Identifier> spiGroups,
ArrayRef<AvailableAttr *> availabilityAttrs,
GenericSignature specializedSignature = nullptr);

static SpecializeAttr *create(ASTContext &ctx, bool exported,
SpecializationKind kind,
ArrayRef<Identifier> spiGroups,
ArrayRef<AvailableAttr *> availabilityAttrs,
GenericSignature specializedSignature,
DeclNameRef replacedFunction);

static SpecializeAttr *create(ASTContext &ctx, bool exported,
SpecializationKind kind,
ArrayRef<Identifier> spiGroups,
ArrayRef<AvailableAttr *> availabilityAttrs,
ArrayRef<Type> typeErasedParams,
GenericSignature specializedSignature,
DeclNameRef replacedFunction,
LazyMemberLoader *resolver, uint64_t data);

size_t numTrailingObjects(OverloadToken<Identifier>) const {
return numSPIGroups;
}

size_t numTrailingObjects(OverloadToken<AvailableAttr *>) const {
return numAvailableAttrs;
}
// Helper to get the trailing objects of one of the subclasses.
template<typename Type>
const Type *getSubclassTrailingObjects() const;

template<typename Type>
Type *getSubclassTrailingObjects() {
const auto *constThis = this;
return const_cast<Type*>(constThis->getSubclassTrailingObjects<Type>());
}

/// Name of SPIs declared by the attribute.
///
/// Note: A single SPI name per attribute is currently supported but this
/// may change with the syntax change.
ArrayRef<Identifier> getSPIGroups() const {
return { this->template getTrailingObjects<Identifier>(),
return { getSubclassTrailingObjects<Identifier>(),
numSPIGroups };
}

ArrayRef<AvailableAttr *> getAvailableAttrs() const {
return {this->template getTrailingObjects<AvailableAttr *>(),
return {getSubclassTrailingObjects<AvailableAttr *>(),
numAvailableAttrs};
}

ArrayRef<Type> getTypeErasedParams() const {
if (!typeErasedParamsInitialized)
return {};

return {this->template getTrailingObjects<Type>(),
return {getSubclassTrailingObjects<Type>(),
numTypeErasedParams};
}

void setTypeErasedParams(const ArrayRef<Type> typeErasedParams) {
assert(typeErasedParams.size() == numTypeErasedParams);
if (!typeErasedParamsInitialized) {
std::uninitialized_copy(typeErasedParams.begin(), typeErasedParams.end(), getTrailingObjects<Type>());
std::uninitialized_copy(typeErasedParams.begin(), typeErasedParams.end(),
getSubclassTrailingObjects<Type>());
typeErasedParamsInitialized = true;
}
}

void setResolver(LazyMemberLoader *resolver, uint64_t resolverContextData) {
this->resolver = resolver;
this->resolverContextData = resolverContextData;
}

TrailingWhereClause *getTrailingWhereClause() const;

bool isPublic() const {
return getKind() == DeclAttrKind::Specialized;
}

bool isExported() const {
return Bits.SpecializeAttr.exported;
return Bits.AbstractSpecializeAttr.exported;
}

SpecializationKind getSpecializationKind() const {
return SpecializationKind(Bits.SpecializeAttr.kind);
return SpecializationKind(Bits.AbstractSpecializeAttr.kind);
}

bool isFullSpecialization() const {
Expand All @@ -1856,15 +1856,140 @@ class SpecializeAttr final
GenericSignature
getSpecializedSignature(const AbstractFunctionDecl *forDecl) const;

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

UNIMPLEMENTED_CLONE(AbstractSpecializeAttr)

bool isEquivalent(const AbstractSpecializeAttr *other, Decl *attachedTo) const;
};

/// The @_specialize attribute.
class SpecializeAttr final : public AbstractSpecializeAttr,
private SpecializeAttrTrailingObjects<SpecializeAttr> {
friend TrailingObjects;
friend AbstractSpecializeAttr;

// WARNING: Do not add storage here. The base class uses TrailingObjects.
private:
SpecializeAttr(SourceLoc atLoc, SourceRange Range,
TrailingWhereClause *clause,
bool exported,
SpecializationKind kind, GenericSignature specializedSignature,
DeclNameRef targetFunctionName, ArrayRef<Identifier> spiGroups,
ArrayRef<AvailableAttr *> availabilityAttrs,
size_t typeErasedParamsCount) :
AbstractSpecializeAttr(DeclAttrKind::Specialize, atLoc, Range, clause,
exported, kind, specializedSignature, targetFunctionName,
spiGroups, availabilityAttrs, typeErasedParamsCount) {}

public:
static SpecializeAttr *
create(ASTContext &Ctx, SourceLoc atLoc, SourceRange Range,
TrailingWhereClause *clause, bool exported,
SpecializationKind kind,
DeclNameRef targetFunctionName, ArrayRef<Identifier> spiGroups,
ArrayRef<AvailableAttr *> availabilityAttrs,
GenericSignature specializedSignature = nullptr);

static SpecializeAttr *create(ASTContext &ctx, bool exported,
SpecializationKind kind,
ArrayRef<Identifier> spiGroups,
ArrayRef<AvailableAttr *> availabilityAttrs,
GenericSignature specializedSignature,
DeclNameRef replacedFunction);

static SpecializeAttr *create(ASTContext &ctx, bool exported,
SpecializationKind kind,
ArrayRef<Identifier> spiGroups,
ArrayRef<AvailableAttr *> availabilityAttrs,
ArrayRef<Type> typeErasedParams,
GenericSignature specializedSignature,
DeclNameRef replacedFunction,
LazyMemberLoader *resolver, uint64_t data);

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

UNIMPLEMENTED_CLONE(SpecializeAttr)

bool isEquivalent(const SpecializeAttr *other, Decl *attachedTo) const;
bool isEquivalent(const SpecializeAttr *other, Decl *attachedTo) const {
return AbstractSpecializeAttr::isEquivalent(other, attachedTo);
}
};

/// The @specialized attribute.
class SpecializedAttr final : public AbstractSpecializeAttr ,
private SpecializeAttrTrailingObjects<SpecializeAttr> {
friend TrailingObjects;
friend AbstractSpecializeAttr;

// WARNING: Do not add storage here. The base class uses TrailingObjects.
private:

SpecializedAttr(SourceLoc atLoc, SourceRange Range,
TrailingWhereClause *clause,
bool exported,
SpecializationKind kind, GenericSignature specializedSignature,
DeclNameRef targetFunctionName, ArrayRef<Identifier> spiGroups,
ArrayRef<AvailableAttr *> availabilityAttrs,
size_t typeErasedParamsCount) :
AbstractSpecializeAttr(DeclAttrKind::Specialized, atLoc, Range, clause,
exported, kind, specializedSignature, targetFunctionName,
spiGroups, availabilityAttrs, typeErasedParamsCount) {}

public:
static SpecializedAttr *
create(ASTContext &Ctx, SourceLoc atLoc, SourceRange Range,
TrailingWhereClause *clause, bool exported,
SpecializationKind kind,
DeclNameRef targetFunctionName, ArrayRef<Identifier> spiGroups,
ArrayRef<AvailableAttr *> availabilityAttrs,
GenericSignature specializedSignature = nullptr);

static SpecializedAttr *create(ASTContext &ctx, bool exported,
SpecializationKind kind,
ArrayRef<Identifier> spiGroups,
ArrayRef<AvailableAttr *> availabilityAttrs,
GenericSignature specializedSignature,
DeclNameRef replacedFunction);

static SpecializedAttr *create(ASTContext &ctx, bool exported,
SpecializationKind kind,
ArrayRef<Identifier> spiGroups,
ArrayRef<AvailableAttr *> availabilityAttrs,
ArrayRef<Type> typeErasedParams,
GenericSignature specializedSignature,
DeclNameRef replacedFunction,
LazyMemberLoader *resolver, uint64_t data);

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

UNIMPLEMENTED_CLONE(SpecializedAttr)

bool isEquivalent(const SpecializedAttr *other, Decl *attachedTo) const {
return AbstractSpecializeAttr::isEquivalent(other, attachedTo);
}
};

template<typename Type>
const Type *AbstractSpecializeAttr::getSubclassTrailingObjects() const {
if (auto attr = dyn_cast<SpecializedAttr>(this)) {
return attr->getTrailingObjects<Type>();
}
if (auto attr = dyn_cast<SpecializeAttr>(this)) {
return attr->getTrailingObjects<Type>();
}
llvm_unreachable("unhandled AbstractSpecializeAttr subclass?");
}



class StorageRestrictionsAttr final
: public DeclAttribute,
private llvm::TrailingObjects<StorageRestrictionsAttr, Identifier> {
Expand Down
2 changes: 1 addition & 1 deletion include/swift/AST/AvailabilityInference.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class AvailabilityInference {
annotatedAvailableRange(const Decl *D);

static AvailabilityRange
annotatedAvailableRangeForAttr(const Decl *D, const SpecializeAttr *attr,
annotatedAvailableRangeForAttr(const Decl *D, const AbstractSpecializeAttr *attr,
ASTContext &ctx);

/// For the attribute's introduction version, update the platform and version
Expand Down
9 changes: 7 additions & 2 deletions include/swift/AST/DeclAttr.def
Original file line number Diff line number Diff line change
Expand Up @@ -892,8 +892,13 @@ SIMPLE_DECL_ATTR(preEnumExtensibility, PreEnumExtensibility,
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIBreakingToRemove | UnconstrainedInABIAttr,
171)
DECL_ATTR_FEATURE_REQUIREMENT(PreEnumExtensibility, ExtensibleAttribute)

LAST_DECL_ATTR(PreEnumExtensibility)

DECL_ATTR(specialized, Specialized,
OnConstructor | OnFunc | OnAccessor,
AllowMultipleAttributes | LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr,
172)

LAST_DECL_ATTR(Specialized)

#undef DECL_ATTR_ALIAS
#undef CONTEXTUAL_DECL_ATTR_ALIAS
Expand Down
14 changes: 9 additions & 5 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -1746,22 +1746,26 @@ ERROR(attr_warn_unused_result_expected_rparen,none,

// _specialize
ERROR(attr_specialize_missing_colon,none,
"missing ':' after %0 in '_specialize' attribute", (StringRef))
"missing ':' after %0 in %select{'_specialize'|'specialized'}1 attribute", (StringRef, bool))

ERROR(attr_specialize_missing_comma,none,
"missing ',' in '_specialize' attribute", ())
"missing ',' in %select{'_specialize'|'specialized'}0 attribute", (bool))

ERROR(attr_specialize_unknown_parameter_name,none,
"unknown parameter %0 in '_specialize attribute'", (StringRef))
"unknown parameter %0 in %select{'_specialize'|'specialized'}1 attribute", (StringRef, bool))
ERROR(attr_specialize_unsupported_parameter_name,none,
"unsupported parameter %0 in 'specialized attribute'", (StringRef))

ERROR(attr_specialize_expected_bool_value,none,
"expected a boolean true or false value in '_specialize' attribute", ())
"expected a boolean true or false value in %select{'_specialize'|'specialized'}0 attribute", (bool))

ERROR(attr_specialize_missing_parameter_label_or_where_clause,none,
"expected a parameter label or a where clause in '_specialize' attribute", ())
ERROR(attr_specialized_missing_where_clause,none,
"expected a where clause in 'specialized' attribute", ())

ERROR(attr_specialize_parameter_already_defined,none,
"parameter '%0' was already defined in '_specialize' attribute", (StringRef))
"parameter '%0' was already defined in %select{'_specialize'|'specialized'}1 attribute", (StringRef, bool))

ERROR(attr_specialize_expected_partial_or_full,none,
"expected 'partial' or 'full' as values of the 'kind' parameter in '_specialize' attribute", ())
Expand Down
Loading