mirror of
https://github.com/apple/swift.git
synced 2026-06-20 15:42:51 +02:00
425cd0ebd7
**Overview**: This PR introduces the basic infrastructure needed to eventually migrate derived macros generation from hand-crafted AST nodes to macros. No conformance have been migrated yet. **Motivation**: Derived conformances (e.g. `Equatable`, `Hashable`, `Codable`, ...) are currently implemented as a special case in the compiler, producing synthetic AST nodes directly. Migrating this to macros will hopefully unify the code path with the existing macro expansion infrastructure, make conformance synthesis easier to extend and test as well as reducing the amount of special cases in the compiler. **Changes**: - New experimental feature flag `DeriveConformancesViaMacros`: Introduces the flag that will eventually gate the new derived conformance code paths. It does not control any behaviour for the moment as none have been migrated yet but this enables future changes to be built incrementally. - New GeneratedSourceInfo and SourceFile kinds `SyntheticMacro`: Introduces new GSI and SourceFile kinds named `SyntheticMacro` to represent macros synthesized by the compiler. Since macros need a real buffer to expand, this is the kind of source file and GSI associated with those buffers. - Conformance derivation via macros API: Introduces the `deriveRequirementViaMacro` function that produces the required witness via macro expansion. See https://github.com/swiftlang/llvm-project/pull/13124 for llvm-related changes. **Next steps**: - Macros do not contain any semantic information, especially regarding types. Therefore it is necessary to provide them with type information as an argument so they can eventually derive the conformances. A separate PR is being created to generate this type information as strings containing swift-parsable code for easy parsing on the macro end. - Implement derived conformance synthesis for individual protocols using the new infrastructure, like `Equatable` or `Hashable` for starters. - Wire the experimental flag to gate the new path once an implementation exists --------- Co-authored-by: Hamish Knight <hamish_knight@apple.com>
811 lines
27 KiB
C++
811 lines
27 KiB
C++
//===--- FeatureSet.cpp - Language feature support --------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2024 - 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "FeatureSet.h"
|
|
|
|
#include "swift/AST/ASTWalker.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/ExistentialLayout.h"
|
|
#include "swift/AST/GenericParamList.h"
|
|
#include "swift/AST/NameLookup.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/Pattern.h"
|
|
#include "swift/AST/ProtocolConformance.h"
|
|
#include "clang/AST/DeclObjC.h"
|
|
#include "swift/AST/TypeCheckRequests.h"
|
|
|
|
#include "swift/Basic/Assertions.h"
|
|
|
|
using namespace swift;
|
|
|
|
/// Calls \p callback for each type in each requirement provided by
|
|
/// \p source.
|
|
///
|
|
/// @returns true after short-circuiting if the callback returned true for
|
|
/// any of the types
|
|
static bool
|
|
forAllRequirementTypes(WhereClauseOwner &&source,
|
|
llvm::function_ref<bool(Type, TypeRepr *)> callback) {
|
|
return std::move(source).visitRequirements(
|
|
TypeResolutionStage::Interface,
|
|
[&](const Requirement &req, RequirementRepr *reqRepr) {
|
|
switch (req.getKind()) {
|
|
case RequirementKind::SameShape:
|
|
case RequirementKind::Conformance:
|
|
case RequirementKind::SameType:
|
|
case RequirementKind::Superclass:
|
|
if (callback(req.getFirstType(),
|
|
RequirementRepr::getFirstTypeRepr(reqRepr)))
|
|
return true;
|
|
|
|
if (callback(req.getSecondType(),
|
|
RequirementRepr::getSecondTypeRepr(reqRepr)))
|
|
return true;
|
|
|
|
break;
|
|
|
|
case RequirementKind::Layout:
|
|
if (callback(req.getFirstType(),
|
|
RequirementRepr::getFirstTypeRepr(reqRepr)))
|
|
return true;
|
|
break;
|
|
}
|
|
return false;
|
|
});
|
|
}
|
|
|
|
/// Does the interface of this declaration use a type for which the
|
|
/// given predicate returns true?
|
|
static bool usesTypeMatching(const Decl *decl,
|
|
llvm::function_ref<bool(Type)> fn) {
|
|
if (auto value = dyn_cast<ValueDecl>(decl)) {
|
|
if (Type type = value->getInterfaceType()) {
|
|
return type.findIf(fn);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Does the where-clause use a type for which the given predicate returns true?
|
|
static bool usesTypeMatching(WhereClauseOwner &&source,
|
|
llvm::function_ref<bool(Type)> fn) {
|
|
return forAllRequirementTypes(
|
|
std::move(source),
|
|
[&](Type ty, TypeRepr *_unused_) { return ty.findIf(fn); });
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: - Standard Features
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/// Functions to determine which features a particular declaration uses. The
|
|
/// usesFeatureNNN functions correspond to the features in Features.def.
|
|
|
|
#define BASELINE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
|
|
static bool usesFeature##FeatureName(Decl *decl) { return false; }
|
|
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description)
|
|
#include "swift/Basic/Features.def"
|
|
|
|
#define UNINTERESTING_FEATURE(FeatureName) \
|
|
static bool usesFeature##FeatureName(Decl *decl) { return false; }
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: - Upcoming Features
|
|
// ----------------------------------------------------------------------------
|
|
|
|
UNINTERESTING_FEATURE(ConciseMagicFile)
|
|
UNINTERESTING_FEATURE(ForwardTrailingClosures)
|
|
UNINTERESTING_FEATURE(StrictConcurrency)
|
|
UNINTERESTING_FEATURE(BareSlashRegexLiterals)
|
|
UNINTERESTING_FEATURE(DeprecateApplicationMain)
|
|
UNINTERESTING_FEATURE(ImportObjcForwardDeclarations)
|
|
UNINTERESTING_FEATURE(DisableOutwardActorInference)
|
|
UNINTERESTING_FEATURE(InternalImportsByDefault)
|
|
UNINTERESTING_FEATURE(IsolatedDefaultValues)
|
|
UNINTERESTING_FEATURE(GlobalConcurrency)
|
|
UNINTERESTING_FEATURE(FullTypedThrows)
|
|
UNINTERESTING_FEATURE(ExistentialAny)
|
|
UNINTERESTING_FEATURE(InferSendableFromCaptures)
|
|
UNINTERESTING_FEATURE(ImplicitOpenExistentials)
|
|
UNINTERESTING_FEATURE(MemberImportVisibility)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: - Experimental Features
|
|
// ----------------------------------------------------------------------------
|
|
|
|
UNINTERESTING_FEATURE(StaticAssert)
|
|
UNINTERESTING_FEATURE(NamedOpaqueTypes)
|
|
UNINTERESTING_FEATURE(FlowSensitiveConcurrencyCaptures)
|
|
UNINTERESTING_FEATURE(CodeItemMacros)
|
|
UNINTERESTING_FEATURE(PreambleMacros)
|
|
UNINTERESTING_FEATURE(TupleConformances)
|
|
UNINTERESTING_FEATURE(LazyImmediate)
|
|
UNINTERESTING_FEATURE(MoveOnlyClasses)
|
|
UNINTERESTING_FEATURE(NoImplicitCopy)
|
|
UNINTERESTING_FEATURE(OldOwnershipOperatorSpellings)
|
|
UNINTERESTING_FEATURE(MoveOnlyEnumDeinits)
|
|
UNINTERESTING_FEATURE(MoveOnlyTuples)
|
|
UNINTERESTING_FEATURE(MoveOnlyPartialReinitialization)
|
|
UNINTERESTING_FEATURE(AccessLevelOnImport)
|
|
UNINTERESTING_FEATURE(AllowNonResilientAccessInPackage)
|
|
UNINTERESTING_FEATURE(ClientBypassResilientAccessInPackage)
|
|
UNINTERESTING_FEATURE(LayoutStringValueWitnesses)
|
|
UNINTERESTING_FEATURE(LayoutStringValueWitnessesInstantiation)
|
|
UNINTERESTING_FEATURE(DifferentiableProgramming)
|
|
UNINTERESTING_FEATURE(ForwardModeDifferentiation)
|
|
UNINTERESTING_FEATURE(AdditiveArithmeticDerivedConformances)
|
|
UNINTERESTING_FEATURE(SendableCompletionHandlers)
|
|
UNINTERESTING_FEATURE(OpaqueTypeErasure)
|
|
UNINTERESTING_FEATURE(PackageCMO)
|
|
UNINTERESTING_FEATURE(ParserRoundTrip)
|
|
UNINTERESTING_FEATURE(ParserValidation)
|
|
UNINTERESTING_FEATURE(UnqualifiedLookupValidation)
|
|
UNINTERESTING_FEATURE(ImplicitSome)
|
|
UNINTERESTING_FEATURE(ParserASTGen)
|
|
UNINTERESTING_FEATURE(BuiltinMacros)
|
|
UNINTERESTING_FEATURE(GenerateBindingsForThrowingFunctionsInCXX)
|
|
UNINTERESTING_FEATURE(DeprecateCompatMemberwiseInit)
|
|
UNINTERESTING_FEATURE(ReferenceBindings)
|
|
UNINTERESTING_FEATURE(BuiltinModule)
|
|
UNINTERESTING_FEATURE(RegionBasedIsolation)
|
|
UNINTERESTING_FEATURE(PlaygroundExtendedCallbacks)
|
|
UNINTERESTING_FEATURE(ThenStatements)
|
|
UNINTERESTING_FEATURE(DoExpressions)
|
|
UNINTERESTING_FEATURE(ForExpressions)
|
|
UNINTERESTING_FEATURE(ImplicitLastExprResults)
|
|
UNINTERESTING_FEATURE(RawLayout)
|
|
UNINTERESTING_FEATURE(Embedded)
|
|
UNINTERESTING_FEATURE(Volatile)
|
|
UNINTERESTING_FEATURE(SuppressedAssociatedTypes)
|
|
UNINTERESTING_FEATURE(SuppressedAssociatedTypesWithDefaults)
|
|
UNINTERESTING_FEATURE(SourceWarningControl)
|
|
UNINTERESTING_FEATURE(StructLetDestructuring)
|
|
UNINTERESTING_FEATURE(MacrosOnImports)
|
|
UNINTERESTING_FEATURE(NonisolatedNonsendingByDefault)
|
|
UNINTERESTING_FEATURE(KeyPathWithMethodMembers)
|
|
UNINTERESTING_FEATURE(ImportMacroAliases)
|
|
UNINTERESTING_FEATURE(NoExplicitNonIsolated)
|
|
UNINTERESTING_FEATURE(EmbeddedDynamicExclusivity)
|
|
UNINTERESTING_FEATURE(TypedAllocation)
|
|
|
|
static bool usesFeatureUnderscoreOwned(Decl *D) {
|
|
return D->getAttrs().hasAttribute<OwnedAttr>();
|
|
}
|
|
|
|
// TODO: Return true for inlinable function bodies with module selectors in them
|
|
UNINTERESTING_FEATURE(ModuleSelector)
|
|
|
|
static bool usesFeatureInlineArrayTypeSugar(Decl *D) {
|
|
return usesTypeMatching(D, [&](Type ty) {
|
|
return isa<InlineArrayType>(ty.getPointer());
|
|
});
|
|
}
|
|
|
|
UNINTERESTING_FEATURE(StaticExclusiveOnly)
|
|
UNINTERESTING_FEATURE(ManualOwnership)
|
|
UNINTERESTING_FEATURE(ExtractConstantsFromMembers)
|
|
UNINTERESTING_FEATURE(SameElementRequirements)
|
|
UNINTERESTING_FEATURE(SendingArgsAndResults)
|
|
UNINTERESTING_FEATURE(CheckImplementationOnly)
|
|
UNINTERESTING_FEATURE(CheckImplementationOnlyStrict)
|
|
UNINTERESTING_FEATURE(EnforceSPIOperatorGroup)
|
|
|
|
static bool usesFeatureCAttribute(Decl *decl) {
|
|
for (auto attr : decl->getAttrs()) {
|
|
if (auto cdeclAttr = dyn_cast<CDeclAttr>(attr))
|
|
if (!cdeclAttr->Underscored)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool findLifetimeAttr(Decl *decl, bool findUnderscored) {
|
|
auto hasLifetimeAttr = [&](Decl *decl) {
|
|
if (!decl->getAttrs().hasAttribute<LifetimeAttr>()) {
|
|
return false;
|
|
}
|
|
// Since we ban mixing @lifetime and @_lifetime on the same decl, checking
|
|
// any one LifetimeAttr on the decl is sufficient.
|
|
// FIXME: Implement the ban.
|
|
if (findUnderscored) {
|
|
return decl->getAttrs().getAttribute<LifetimeAttr>()->isUnderscored();
|
|
}
|
|
return !decl->getAttrs().getAttribute<LifetimeAttr>()->isUnderscored();
|
|
};
|
|
|
|
switch (decl->getKind()) {
|
|
case DeclKind::Var: {
|
|
auto *var = cast<VarDecl>(decl);
|
|
return llvm::any_of(var->getAllAccessors(), hasLifetimeAttr);
|
|
}
|
|
default:
|
|
return hasLifetimeAttr(decl);
|
|
}
|
|
}
|
|
|
|
static bool usesFeatureLifetimeDependence(Decl *decl) {
|
|
if (findLifetimeAttr(decl, /*findUnderscored*/ false)) {
|
|
return true;
|
|
}
|
|
|
|
// Guard inferred lifetime dependencies with LifetimeDependence if it was
|
|
// enabled.
|
|
if (!decl->getASTContext().LangOpts.hasFeature(Feature::LifetimeDependence)) {
|
|
return false;
|
|
}
|
|
|
|
// Check for inferred lifetime dependencies
|
|
if (auto *afd = dyn_cast<AbstractFunctionDecl>(decl)) {
|
|
return afd->getInterfaceType()
|
|
->getAs<AnyFunctionType>()
|
|
->hasLifetimeDependencies();
|
|
}
|
|
if (auto *varDecl = dyn_cast<VarDecl>(decl)) {
|
|
return !varDecl->getTypeInContext()->isEscapable();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool usesFeatureLifetimes(Decl *decl) {
|
|
if (findLifetimeAttr(decl, /*findUnderscored*/ true)) {
|
|
return true;
|
|
}
|
|
|
|
// Guard inferred lifetime dependencies with Lifetimes if it was enabled.
|
|
if (!decl->getASTContext().LangOpts.hasFeature(Feature::Lifetimes)) {
|
|
return false;
|
|
}
|
|
|
|
// Check for inferred lifetime dependencies
|
|
if (auto *afd = dyn_cast<AbstractFunctionDecl>(decl)) {
|
|
return afd->getInterfaceType()
|
|
->getAs<AnyFunctionType>()
|
|
->hasLifetimeDependencies();
|
|
}
|
|
if (auto *varDecl = dyn_cast<VarDecl>(decl)) {
|
|
return !varDecl->getTypeInContext()->isEscapable();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static PreInverseGenericsAttr *getPreInverseGenericsExcept(Decl *decl) {
|
|
if (auto pbd = dyn_cast<PatternBindingDecl>(decl))
|
|
for (auto i : range(pbd->getNumPatternEntries()))
|
|
if (auto anchorVar = pbd->getAnchoringVarDecl(i))
|
|
return getPreInverseGenericsExcept(anchorVar);
|
|
|
|
return decl->getAttrs().getAttribute<PreInverseGenericsAttr>();
|
|
}
|
|
|
|
static bool usesFeaturePreInverseGenericsExcept(Decl *decl) {
|
|
if (auto *attr = getPreInverseGenericsExcept(decl))
|
|
return attr->hasExcept(decl);
|
|
return false;
|
|
}
|
|
|
|
static bool hasLifetimeDependencies(Type type) {
|
|
if (auto *aft = type->getAs<AnyFunctionType>()) {
|
|
return aft->hasExplicitLifetimeDependencies();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Search for any types within decl with lifetime dependencies. Ignore
|
|
/// lifetimes on AbstractFunctionDecl decls, since those are supported by the
|
|
/// Lifetimes feature, not ClosureLifetimes.
|
|
static bool findClosureLifetimes(Decl *decl) {
|
|
// Search for any Decl that uses a type with lifetime dependencies, possibly
|
|
// restricting this to explicit dependencies.
|
|
class ClosureLifetimesWalker : public ASTWalker {
|
|
|
|
public:
|
|
bool useFound = false;
|
|
|
|
PreWalkAction walkToDeclPre(Decl *D) override {
|
|
if (auto *afd = dyn_cast<AbstractFunctionDecl>(D)) {
|
|
// Check the parameters and result, but not the AFD itself, since
|
|
// lifetimes on AFDs are supported by the Lifetimes feature.
|
|
Type resultType =
|
|
afd->getInterfaceType()->getAs<AnyFunctionType>()->getResult();
|
|
useFound = resultType.findIf(hasLifetimeDependencies);
|
|
return Action::StopIf(useFound);
|
|
}
|
|
|
|
// Check for lifetime dependence info on param and type decls.
|
|
if (isa<ParamDecl>(D) || isa<TypeDecl>(D)) {
|
|
useFound = usesTypeMatching(D, hasLifetimeDependencies);
|
|
return Action::StopIf(useFound);
|
|
}
|
|
|
|
// Any other Decl kinds are irrelevant.
|
|
return Action::SkipChildren();
|
|
}
|
|
};
|
|
|
|
ClosureLifetimesWalker walker;
|
|
decl->walk(walker);
|
|
return walker.useFound;
|
|
}
|
|
|
|
static bool usesFeatureClosureLifetimes(Decl *decl) {
|
|
// This will find function types with lifetimes & closures with lifetimes,
|
|
// since it walks the AST rooted at decl, checking types.
|
|
return findClosureLifetimes(decl);
|
|
}
|
|
|
|
static bool usesFeatureInoutLifetimeDependence(Decl *decl) {
|
|
auto hasInoutLifetimeDependence = [](Decl *decl) {
|
|
for (auto attr : decl->getAttrs().getAttributes<LifetimeAttr>()) {
|
|
for (auto source : attr->getLifetimeEntry()->getSources()) {
|
|
if (source.getParsedLifetimeDependenceKind() ==
|
|
ParsedLifetimeDependenceKind::Inout) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
switch (decl->getKind()) {
|
|
case DeclKind::Var: {
|
|
auto *var = cast<VarDecl>(decl);
|
|
return llvm::any_of(var->getAllAccessors(), hasInoutLifetimeDependence);
|
|
}
|
|
default:
|
|
return hasInoutLifetimeDependence(decl);
|
|
}
|
|
}
|
|
|
|
static bool usesFeatureLifetimeDependenceMutableAccessors(Decl *decl) {
|
|
if (!isa<VarDecl>(decl)) {
|
|
return false;
|
|
}
|
|
auto var = cast<VarDecl>(decl);
|
|
return var->isGetterMutating() && !var->getTypeInContext()->isEscapable();
|
|
}
|
|
|
|
static bool usesFeatureNonescapableAccessorOnTrivial(Decl *decl) {
|
|
if (!isa<VarDecl>(decl)) {
|
|
return false;
|
|
}
|
|
auto var = cast<VarDecl>(decl);
|
|
if (!var->hasParsedAccessors()) {
|
|
return false;
|
|
}
|
|
// Check for properties that are both non-Copyable and non-Escapable
|
|
// (MutableSpan).
|
|
if (var->getTypeInContext()->isNoncopyable()
|
|
&& !var->getTypeInContext()->isEscapable()) {
|
|
auto selfTy = var->getDeclContext()->getSelfTypeInContext();
|
|
// Consider 'self' trivial if it is BitwiseCopyable and Escapable
|
|
// (UnsafeMutableBufferPointer).
|
|
return selfTy->isBitwiseCopyable() && selfTy->isEscapable();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
UNINTERESTING_FEATURE(DynamicActorIsolation)
|
|
UNINTERESTING_FEATURE(NonfrozenEnumExhaustivity)
|
|
UNINTERESTING_FEATURE(ClosureIsolation)
|
|
UNINTERESTING_FEATURE(Extern)
|
|
UNINTERESTING_FEATURE(ConsumeSelfInDeinit)
|
|
|
|
static bool usesFeatureAddressableParameters(Decl *d) {
|
|
if (d->getAttrs().hasAttribute<AddressableSelfAttr>()) {
|
|
return true;
|
|
}
|
|
|
|
auto fd = dyn_cast<AbstractFunctionDecl>(d);
|
|
if (!fd) {
|
|
return false;
|
|
}
|
|
|
|
for (auto pd : *fd->getParameters()) {
|
|
if (pd->isAddressable()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool usesFeatureAddressableTypes(Decl *d) {
|
|
if (d->getAttrs().hasAttribute<AddressableForDependenciesAttr>()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
UNINTERESTING_FEATURE(IsolatedAny2)
|
|
UNINTERESTING_FEATURE(GlobalActorIsolatedTypesUsability)
|
|
UNINTERESTING_FEATURE(ObjCImplementation)
|
|
UNINTERESTING_FEATURE(ObjCImplementationWithResilientStorage)
|
|
UNINTERESTING_FEATURE(Sensitive)
|
|
UNINTERESTING_FEATURE(DebugDescriptionMacro)
|
|
UNINTERESTING_FEATURE(ReinitializeConsumeInMultiBlockDefer)
|
|
UNINTERESTING_FEATURE(TrailingComma)
|
|
UNINTERESTING_FEATURE(RawIdentifiers)
|
|
UNINTERESTING_FEATURE(InferIsolatedConformances)
|
|
|
|
static ABIAttr *getABIAttr(Decl *decl) {
|
|
if (auto pbd = dyn_cast<PatternBindingDecl>(decl))
|
|
for (auto i : range(pbd->getNumPatternEntries()))
|
|
if (auto anchorVar = pbd->getAnchoringVarDecl(i))
|
|
return getABIAttr(anchorVar);
|
|
// FIXME: EnumCaseDecl/EnumElementDecl
|
|
|
|
return decl->getAttrs().getAttribute<ABIAttr>();
|
|
}
|
|
|
|
static bool usesFeatureABIAttributeSE0479(Decl *decl) {
|
|
return getABIAttr(decl) != nullptr;
|
|
}
|
|
|
|
static bool usesFeatureIsolatedConformances(Decl *decl) {
|
|
// FIXME: Check conformances associated with this decl?
|
|
return false;
|
|
}
|
|
|
|
UNINTERESTING_FEATURE(ConcurrencySyntaxSugar)
|
|
|
|
static bool usesFeatureCompileTimeValues(Decl *decl) {
|
|
return decl->getAttrs().hasAttribute<ConstValAttr>() ||
|
|
decl->getAttrs().hasAttribute<ConstInitializedAttr>();
|
|
}
|
|
|
|
UNINTERESTING_FEATURE(ClosureBodyMacro)
|
|
UNINTERESTING_FEATURE(BuiltinConcurrencyStackNesting)
|
|
UNINTERESTING_FEATURE(BuiltinTaskCancellationShield)
|
|
UNINTERESTING_FEATURE(BuiltinAddTaskLocalValue)
|
|
UNINTERESTING_FEATURE(BuiltinContinuationNonCopyableSuccess)
|
|
UNINTERESTING_FEATURE(CompileTimeValuesPreview)
|
|
UNINTERESTING_FEATURE(LiteralExpressions)
|
|
UNINTERESTING_FEATURE(StrictMemorySafety)
|
|
UNINTERESTING_FEATURE(LibraryEvolution)
|
|
UNINTERESTING_FEATURE(SafeInteropWrappers)
|
|
UNINTERESTING_FEATURE(AssumeResilientCxxTypes)
|
|
UNINTERESTING_FEATURE(ImportNonPublicCxxMembers)
|
|
UNINTERESTING_FEATURE(ImportCxxMembersLazily)
|
|
UNINTERESTING_FEATURE(ForeignReferenceTypeInheritance)
|
|
UNINTERESTING_FEATURE(CoroutineAccessorsUnwindOnCallerError)
|
|
UNINTERESTING_FEATURE(AllowRuntimeSymbolDeclarations)
|
|
|
|
static bool usesFeatureCoroutineAccessors(Decl *decl) {
|
|
auto accessorDeclUsesFeatureCoroutineAccessors = [](AccessorDecl *accessor) {
|
|
return requiresFeatureCoroutineAccessors(accessor->getAccessorKind());
|
|
};
|
|
switch (decl->getKind()) {
|
|
case DeclKind::Var: {
|
|
auto *var = cast<VarDecl>(decl);
|
|
return llvm::any_of(var->getAllAccessors(),
|
|
accessorDeclUsesFeatureCoroutineAccessors);
|
|
}
|
|
case DeclKind::Accessor: {
|
|
auto *accessor = cast<AccessorDecl>(decl);
|
|
return accessorDeclUsesFeatureCoroutineAccessors(accessor);
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
UNINTERESTING_FEATURE(GeneralizedIsSameMetaTypeBuiltin)
|
|
UNINTERESTING_FEATURE(CustomAvailability)
|
|
UNINTERESTING_FEATURE(BuiltinMarkDependence)
|
|
|
|
static bool usesFeatureAsyncExecutionBehaviorAttributes(Decl *decl) {
|
|
// Explicit `@concurrent` attribute on the declaration.
|
|
if (decl->getAttrs().hasAttribute<ConcurrentAttr>())
|
|
return true;
|
|
|
|
// Explicit `nonisolated(nonsending)` attribute on the declaration.
|
|
if (auto *nonisolated = decl->getAttrs().getAttribute<NonisolatedAttr>()) {
|
|
if (nonisolated->isNonSending())
|
|
return true;
|
|
}
|
|
|
|
auto hasNonisolatedNonsendingAttr = [](TypeRepr *R) {
|
|
if (!R)
|
|
return false;
|
|
|
|
return R->findIf([](TypeRepr *repr) {
|
|
if (isa<NonisolatedNonsendingTypeRepr>(repr))
|
|
return true;
|
|
|
|
// We don't check for @concurrent here because it's
|
|
// not printed in type positions since it indicates
|
|
// old "nonisolated" state.
|
|
|
|
return false;
|
|
});
|
|
};
|
|
|
|
auto *VD = dyn_cast<ValueDecl>(decl);
|
|
if (!VD)
|
|
return false;
|
|
|
|
// The declaration is going to be printed with `nonisolated(nonsending)`
|
|
// attribute.
|
|
if (getActorIsolation(VD).isNonisolatedNonsending())
|
|
return true;
|
|
|
|
// Check if any parameters that have `nonisolated(nonsending)` attribute.
|
|
if (auto *PL = VD->getParameterList()) {
|
|
if (llvm::any_of(*PL, [&](const ParamDecl *P) {
|
|
return hasNonisolatedNonsendingAttr(P->getTypeRepr());
|
|
}))
|
|
return true;
|
|
}
|
|
|
|
// Check if result type has explicit `nonisolated(nonsending)` attribute.
|
|
if (hasNonisolatedNonsendingAttr(VD->getResultTypeRepr()))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool usesFeatureNonexhaustiveAttribute(Decl *decl) {
|
|
return decl->getAttrs().hasAttribute<NonexhaustiveAttr>();
|
|
}
|
|
|
|
static bool usesFeatureAlwaysInheritActorContext(Decl *decl) {
|
|
auto *VD = dyn_cast<ValueDecl>(decl);
|
|
if (!VD)
|
|
return false;
|
|
|
|
if (auto *PL = VD->getParameterList()) {
|
|
return llvm::any_of(*PL, [&](const ParamDecl *P) {
|
|
auto *attr = P->getAttrs().getAttribute<InheritActorContextAttr>();
|
|
return attr && attr->isAlways();
|
|
});
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool usesFeatureDefaultIsolationPerFile(Decl *D) {
|
|
return isa<UsingDecl>(D);
|
|
}
|
|
|
|
UNINTERESTING_FEATURE(BuiltinSelect)
|
|
UNINTERESTING_FEATURE(BuiltinInterleave)
|
|
UNINTERESTING_FEATURE(BuiltinVectorsExternC)
|
|
UNINTERESTING_FEATURE(AddressOfProperty2)
|
|
UNINTERESTING_FEATURE(ImmutableWeakCaptures)
|
|
UNINTERESTING_FEATURE(BorrowingForLoop)
|
|
|
|
static bool usesFeatureBorrowAndMutateAccessors(Decl *decl) {
|
|
auto accessorDeclUsesFeatureBorrowAndMutateAccessors =
|
|
[](AccessorDecl *accessor) {
|
|
return requiresFeatureBorrowAndMutateAccessors(
|
|
accessor->getAccessorKind());
|
|
};
|
|
switch (decl->getKind()) {
|
|
case DeclKind::Var: {
|
|
auto *var = cast<VarDecl>(decl);
|
|
return llvm::any_of(var->getAllAccessors(),
|
|
accessorDeclUsesFeatureBorrowAndMutateAccessors);
|
|
}
|
|
case DeclKind::Accessor: {
|
|
auto *accessor = cast<AccessorDecl>(decl);
|
|
return accessorDeclUsesFeatureBorrowAndMutateAccessors(accessor);
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool usesFeatureInlineAlways(Decl *decl) {
|
|
if (auto *inlineAttr = decl->getAttrs().getAttribute<InlineAttr>()) {
|
|
return inlineAttr->getKind() == InlineKind::Always;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
UNINTERESTING_FEATURE(SwiftRuntimeAvailability)
|
|
UNINTERESTING_FEATURE(StandaloneSwiftAvailability)
|
|
|
|
static bool usesFeatureTildeSendable(Decl *decl) {
|
|
auto *TD = dyn_cast<TypeDecl>(decl);
|
|
if (!TD)
|
|
return false;
|
|
|
|
return llvm::any_of(
|
|
TD->getInherited().getEntries(), [&decl](const auto &entry) {
|
|
if (!entry.isSuppressed())
|
|
return false;
|
|
|
|
auto T = entry.getType();
|
|
if (!T)
|
|
return false;
|
|
|
|
auto &C = decl->getASTContext();
|
|
return C.getProtocol(KnownProtocolKind::Sendable) == T->getAnyNominal();
|
|
});
|
|
}
|
|
|
|
static bool usesFeatureReparenting(Decl *decl) {
|
|
auto reparentableProto = [](Type ty) {
|
|
if (!ty)
|
|
return false;
|
|
|
|
if (auto protoTy = ty->getAs<ProtocolType>()) {
|
|
if (protoTy->getDecl()->getAttrs().hasAttribute<ReparentableAttr>())
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
// Search the decl or extension for mentions of a reparentable protocol.
|
|
if (isa<ValueDecl>(decl)) {
|
|
if (usesTypeMatching(decl, reparentableProto))
|
|
return true;
|
|
} else if (auto *gc = decl->getAsGenericContext()) {
|
|
bool foundInWhereClause = usesTypeMatching(
|
|
WhereClauseOwner(const_cast<GenericContext *>(gc)), reparentableProto);
|
|
|
|
if (foundInWhereClause)
|
|
return true;
|
|
}
|
|
|
|
// Check the inheritance clause for mentions of a reparentable protocol.
|
|
InheritedTypes inherited(decl);
|
|
for (auto const &entry : inherited.getEntries()) {
|
|
if (entry.isReparented())
|
|
return true;
|
|
|
|
if (Type ty = entry.getType())
|
|
if (ty.findIf(reparentableProto))
|
|
return true;
|
|
}
|
|
|
|
// Check if this decl itself is a reparentable protocol (of extension of).
|
|
if (auto ext = dyn_cast<ExtensionDecl>(decl)) {
|
|
decl = ext->getExtendedNominal();
|
|
}
|
|
|
|
if (auto proto = dyn_cast<ProtocolDecl>(decl)) {
|
|
if (proto->getAttrs().hasAttribute<ReparentableAttr>())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
UNINTERESTING_FEATURE(StrictAccessControl)
|
|
UNINTERESTING_FEATURE(BorrowingSequence)
|
|
UNINTERESTING_FEATURE(AbstractStoredPropertyLayout)
|
|
UNINTERESTING_FEATURE(FlowIsolationGlobalActor)
|
|
|
|
UNINTERESTING_FEATURE(DeriveConformancesViaMacros)
|
|
|
|
static bool usesFeatureBorrowInout(Decl *decl) {
|
|
auto &ctx = decl->getASTContext();
|
|
|
|
if (auto ext = dyn_cast<ExtensionDecl>(decl)) {
|
|
decl = ext->getExtendedNominal();
|
|
}
|
|
|
|
return decl == ctx.getRefDecl() || decl == ctx.getMutableRefDecl();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MARK: - FeatureSet
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void FeatureSet::collectRequiredFeature(Feature feature,
|
|
InsertOrRemove operation) {
|
|
required.insertOrRemove(feature, operation == Insert);
|
|
}
|
|
|
|
void FeatureSet::collectSuppressibleFeature(Feature feature,
|
|
InsertOrRemove operation) {
|
|
suppressible.insertOrRemove(Feature::getNumFeatures() - size_t(feature),
|
|
operation == Insert);
|
|
}
|
|
|
|
static bool hasFeatureSuppressionAttribute(Decl *decl, StringRef featureName,
|
|
bool inverted) {
|
|
auto attr = decl->getAttrs().getAttribute<AllowFeatureSuppressionAttr>();
|
|
if (!attr)
|
|
return false;
|
|
|
|
if (attr->getInverted() != inverted)
|
|
return false;
|
|
|
|
for (auto suppressedFeature : attr->getSuppressedFeatures()) {
|
|
if (suppressedFeature.is(featureName))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// These functions are only used when there suppressible language features
|
|
// defined, so suppress warnings about them being unused to avoid spam when
|
|
// there are none.
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wunused"
|
|
static bool disallowFeatureSuppression(StringRef featureName, Decl *decl) {
|
|
return hasFeatureSuppressionAttribute(decl, featureName, true);
|
|
}
|
|
|
|
static bool allowFeatureSuppression(StringRef featureName, Decl *decl) {
|
|
return hasFeatureSuppressionAttribute(decl, featureName, false);
|
|
}
|
|
#pragma clang diagnostic pop
|
|
|
|
/// Go through all the features used by the given declaration and
|
|
/// either add or remove them to this set.
|
|
void FeatureSet::collectFeaturesUsed(Decl *decl, InsertOrRemove operation) {
|
|
// Count feature usage in an ABI decl as feature usage by the API, not itself,
|
|
// since we can't use `#if` inside an @abi attribute.
|
|
Decl *abiDecl = nullptr;
|
|
if (auto abiAttr = getABIAttr(decl)) {
|
|
abiDecl = abiAttr->abiDecl;
|
|
}
|
|
|
|
#define CHECK(Function) (Function(decl) || (abiDecl && Function(abiDecl)))
|
|
#define CHECK_ARG(Function, Arg) (Function(Arg, decl) || (abiDecl && Function(Arg, abiDecl)))
|
|
|
|
// Go through each of the features, checking whether the
|
|
// declaration uses that feature.
|
|
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
|
|
if (CHECK(usesFeature##FeatureName)) \
|
|
collectRequiredFeature(Feature::FeatureName, operation);
|
|
#define SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
|
|
if (CHECK(usesFeature##FeatureName)) { \
|
|
if (CHECK_ARG(disallowFeatureSuppression, #FeatureName)) \
|
|
collectRequiredFeature(Feature::FeatureName, operation); \
|
|
else \
|
|
collectSuppressibleFeature(Feature::FeatureName, operation); \
|
|
}
|
|
#define CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
|
|
if (CHECK(usesFeature##FeatureName)) { \
|
|
if (CHECK_ARG(allowFeatureSuppression, #FeatureName)) \
|
|
collectSuppressibleFeature(Feature::FeatureName, operation); \
|
|
else \
|
|
collectRequiredFeature(Feature::FeatureName, operation); \
|
|
}
|
|
#include "swift/Basic/Features.def"
|
|
#undef CHECK
|
|
#undef CHECK_ARG
|
|
}
|
|
|
|
FeatureSet swift::getUniqueFeaturesUsed(Decl *decl) {
|
|
// Add all the features used by this declaration.
|
|
FeatureSet features;
|
|
features.collectFeaturesUsed(decl, FeatureSet::Insert);
|
|
|
|
// Remove all the features used by all enclosing declarations.
|
|
Decl *enclosingDecl = decl;
|
|
while (!features.empty()) {
|
|
// If we were in an @abi attribute, collect from the API counterpart.
|
|
auto abiRole = ABIRoleInfo(enclosingDecl);
|
|
if (!abiRole.providesAPI() && abiRole.getCounterpart())
|
|
enclosingDecl = abiRole.getCounterpart();
|
|
// Find the next outermost enclosing declaration.
|
|
else if (auto accessor = dyn_cast<AccessorDecl>(enclosingDecl))
|
|
enclosingDecl = accessor->getStorage();
|
|
else
|
|
enclosingDecl = enclosingDecl->getDeclContext()->getAsDecl();
|
|
if (!enclosingDecl)
|
|
break;
|
|
|
|
features.collectFeaturesUsed(enclosingDecl, FeatureSet::Remove);
|
|
}
|
|
|
|
return features;
|
|
}
|