Skip to content

Commit 1437acd

Browse files
committed
Implement conditional feature suppression.
Our standard conception of suppressible features assumes we should always suppress the feature if the compiler doesn't support it. This presumes that there's no harm in suppressing the feature, and that's a fine assumption for features that are just adding information or suppressing new diagnostics. Features that are semantically relevant, maybe even ABI-breaking, are not a good fit for this, and so instead of reprinting the decl with the feature suppressed, we just have to hide the decl entirely. The missing middle here is that it's sometimes useful to be able to adopt a type change to an existing declaration, and we'd like older compilers to be able to use the older version of the declaration. Making a type change this way is, of course, only really acceptable for @_alwaysEmitIntoClient declarations; but those represent quite a few declarations that we'd like to be able to refine the types of. Rather than trying to come up with heuristics based on @_alwaysEmitIntoClient or other sources of information, this design just requires the declaration to opt in with a new attribute, @_allowFeatureSuppress. When a declaration opts in to suppression for a conditionally-suppressible feature, the printer uses the suppression serially-print-with-downgraded-options approach; otherwise it uses the print-only-if-feature-is-available approach.
1 parent 976f149 commit 1437acd

20 files changed

+289
-49
lines changed

include/swift/AST/ASTBridging.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,14 @@ BridgedAlignmentAttr_createParsed(BridgedASTContext cContext,
481481
BridgedSourceLoc cAtLoc,
482482
BridgedSourceRange cRange, size_t cValue);
483483

484+
SWIFT_NAME("BridgedAllowFeatureSuppressionAttr.createParsed(_:atLoc:range:features:)")
485+
BridgedAllowFeatureSuppressionAttr
486+
BridgedAllowFeatureSuppressionAttr_createParsed(
487+
BridgedASTContext cContext,
488+
BridgedSourceLoc cAtLoc,
489+
BridgedSourceRange cRange,
490+
BridgedArrayRef cFeatures);
491+
484492
SWIFT_NAME("BridgedCDeclAttr.createParsed(_:atLoc:range:name:)")
485493
BridgedCDeclAttr BridgedCDeclAttr_createParsed(BridgedASTContext cContext,
486494
BridgedSourceLoc cAtLoc,

include/swift/AST/Attr.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,11 @@ class DeclAttribute : public AttributeBase {
191191
SWIFT_INLINE_BITFIELD(NonisolatedAttr, DeclAttribute, 1,
192192
isUnsafe : 1
193193
);
194+
195+
SWIFT_INLINE_BITFIELD_FULL(AllowFeatureSuppressionAttr, DeclAttribute, 32,
196+
: NumPadBits,
197+
NumFeatures : 32
198+
);
194199
} Bits;
195200
// clang-format on
196201

@@ -2636,6 +2641,32 @@ template <typename ATTR, bool AllowInvalid> struct ToAttributeKind {
26362641
}
26372642
};
26382643

2644+
/// The @_allowFeatureSuppression(Foo, Bar) attribute. The feature
2645+
/// names are intentionally not validated, and the attribute itself is
2646+
/// not printed when rendering a module interface.
2647+
class AllowFeatureSuppressionAttr final
2648+
: public DeclAttribute,
2649+
private llvm::TrailingObjects<AllowFeatureSuppressionAttr, Identifier> {
2650+
friend TrailingObjects;
2651+
2652+
/// Create an implicit @objc attribute with the given (optional) name.
2653+
AllowFeatureSuppressionAttr(SourceLoc atLoc, SourceRange range,
2654+
bool implicit, ArrayRef<Identifier> features);
2655+
public:
2656+
static AllowFeatureSuppressionAttr *create(ASTContext &ctx, SourceLoc atLoc,
2657+
SourceRange range, bool implicit,
2658+
ArrayRef<Identifier> features);
2659+
2660+
ArrayRef<Identifier> getSuppressedFeatures() const {
2661+
return {getTrailingObjects<Identifier>(),
2662+
Bits.AllowFeatureSuppressionAttr.NumFeatures};
2663+
}
2664+
2665+
static bool classof(const DeclAttribute *DA) {
2666+
return DA->getKind() == DeclAttrKind::AllowFeatureSuppression;
2667+
}
2668+
};
2669+
26392670
/// Attributes that may be applied to declarations.
26402671
class DeclAttributes {
26412672
/// Linked list of declaration attributes.

include/swift/AST/DeclAttr.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,10 @@ SIMPLE_DECL_ATTR(_noObjCBridging, NoObjCBridging,
488488
DECL_ATTR(_distributedThunkTarget, DistributedThunkTarget,
489489
OnAbstractFunction | UserInaccessible | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
490490
156)
491-
LAST_DECL_ATTR(DistributedThunkTarget)
491+
DECL_ATTR(_allowFeatureSuppression, AllowFeatureSuppression,
492+
OnAnyDecl | UserInaccessible | NotSerialized | ABIStableToAdd | APIStableToAdd | ABIStableToRemove | APIStableToRemove,
493+
157)
494+
LAST_DECL_ATTR(AllowFeatureSuppression)
492495

493496
#undef DECL_ATTR_ALIAS
494497
#undef CONTEXTUAL_DECL_ATTR_ALIAS

include/swift/AST/DiagnosticsParse.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,6 +1889,10 @@ ERROR(attr_rawlayout_expected_params,none,
18891889

18901890
ERROR(attr_extern_expected_label,none,
18911891
"expected %0 argument to @_extern attribute", (StringRef))
1892+
1893+
ERROR(attr_expected_feature_name,none,
1894+
"expected feature name in @%0 attribute", (StringRef))
1895+
18921896
//------------------------------------------------------------------------------
18931897
// MARK: Generics parsing diagnostics
18941898
//------------------------------------------------------------------------------

include/swift/AST/PrintOptions.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,9 @@ struct PrintOptions {
379379
/// List of attribute kinds that should not be printed.
380380
std::vector<AnyAttrKind> ExcludeAttrList = {
381381
DeclAttrKind::Transparent, DeclAttrKind::Effects,
382-
DeclAttrKind::FixedLayout, DeclAttrKind::ShowInInterface};
382+
DeclAttrKind::FixedLayout, DeclAttrKind::ShowInInterface,
383+
DeclAttrKind::AllowFeatureSuppression
384+
};
383385

384386
/// List of attribute kinds that should be printed exclusively.
385387
/// Empty means allow all.

include/swift/Basic/Feature.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,6 @@ constexpr unsigned numFeatures() {
3535
return NumFeatures;
3636
}
3737

38-
/// Determine whether the given feature is suppressible.
39-
bool isSuppressibleFeature(Feature feature);
40-
4138
/// Check whether the given feature is available in production compilers.
4239
bool isFeatureAvailableInProduction(Feature feature);
4340

include/swift/Basic/Features.def

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@
3434
// imply the existence of earlier features. (This only needs to apply to
3535
// suppressible features.)
3636
//
37+
// If suppressing a feature in general is problematic, but it's okay to
38+
// suppress it for specific declarations, the feature can be made
39+
// conditionally suppressible. Declarations opt in to suppression with
40+
// the @_allowFeatureSuppression attribute.
41+
//
3742
// BASELINE_LANGUAGE_FEATURE is the same as LANGUAGE_FEATURE, but is used
3843
// for features that can be assumed to be available in any Swift compiler that
3944
// will be used to process the textual interface files produced by this
@@ -44,11 +49,46 @@
4449
# error define LANGUAGE_FEATURE before including Features.def
4550
#endif
4651

52+
// A feature that's both suppressible and experimental.
53+
// Delegates to whichever the includer defines.
54+
#ifndef SUPPRESSIBLE_EXPERIMENTAL_FEATURE
55+
# if defined(SUPPRESSIBLE_LANGUAGE_FEATURE) && \
56+
defined(EXPERIMENTAL_FEATURE)
57+
# error ambiguous defines when including Features.def
58+
# elif defined(SUPPRESSIBLE_LANGUAGE_FEATURE)
59+
# define SUPPRESSIBLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \
60+
SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, 0, #FeatureName)
61+
# else
62+
# define SUPPRESSIBLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \
63+
EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd)
64+
# endif
65+
#endif
66+
4767
#ifndef SUPPRESSIBLE_LANGUAGE_FEATURE
48-
#define SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
68+
# define SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
4969
LANGUAGE_FEATURE(FeatureName, SENumber, Description)
5070
#endif
5171

72+
// A feature that's both conditionally-suppressible and experimental.
73+
// Delegates to whichever the includer defines.
74+
#ifndef CONDITIONALLY_SUPPRESSIBLE_EXPERIMENTAL_FEATURE
75+
# if defined(CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE) && \
76+
defined(EXPERIMENTAL_FEATURE)
77+
# error ambiguous defines when including Features.def
78+
# elif defined(CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE)
79+
# define CONDITIONALLY_SUPPRESSIBLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \
80+
CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, 0, #FeatureName)
81+
# else
82+
# define CONDITIONALLY_SUPPRESSIBLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \
83+
EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd)
84+
# endif
85+
#endif
86+
87+
#ifndef CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE
88+
# define CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
89+
LANGUAGE_FEATURE(FeatureName, SENumber, Description)
90+
#endif
91+
5292
#ifndef UPCOMING_FEATURE
5393
# define UPCOMING_FEATURE(FeatureName, SENumber, Version) \
5494
LANGUAGE_FEATURE(FeatureName, SENumber, #FeatureName)
@@ -301,5 +341,8 @@ EXPERIMENTAL_FEATURE(IsolatedAny, true)
301341
#undef EXPERIMENTAL_FEATURE
302342
#undef UPCOMING_FEATURE
303343
#undef BASELINE_LANGUAGE_FEATURE
344+
#undef CONDITIONALLY_SUPPRESSIBLE_EXPERIMENTAL_FEATURE
345+
#undef CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE
346+
#undef SUPPRESSIBLE_EXPERIMENTAL_FEATURE
304347
#undef SUPPRESSIBLE_LANGUAGE_FEATURE
305348
#undef LANGUAGE_FEATURE

include/swift/Parse/Parser.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,14 @@ class Parser {
10721072
SmallVector<SourceLoc, 4> &NameLocs,
10731073
bool &IsNullarySelector);
10741074

1075+
/// Parse a parenthesized and comma-separated list of attribute arguments.
1076+
///
1077+
/// \returns false on success, true on error.
1078+
ParserStatus
1079+
parseAttributeArguments(SourceLoc attrLoc, StringRef attrName,
1080+
bool isAttrModifier, SourceRange &parensRange,
1081+
llvm::function_ref<ParserStatus()> parseAttr);
1082+
10751083
/// Parse the @_specialize attribute.
10761084
/// \p closingBrace is the expected closing brace, which can be either ) or ]
10771085
/// \p Attr is where to store the parsed attribute
@@ -1151,6 +1159,9 @@ class Parser {
11511159
parseDocumentationAttributeArgument(std::optional<StringRef> &Metadata,
11521160
std::optional<AccessLevel> &Visibility);
11531161

1162+
ParserResult<AllowFeatureSuppressionAttr>
1163+
parseAllowFeatureSuppressionAttribute(SourceLoc atLoc, SourceLoc loc);
1164+
11541165
/// Parse the @attached or @freestanding attribute that specifies a macro
11551166
/// role.
11561167
ParserResult<MacroRoleAttr> parseMacroRoleAttribute(

lib/AST/ASTBridging.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,18 @@ BridgedAlignmentAttr_createParsed(BridgedASTContext cContext,
429429
cValue, cAtLoc.unbridged(), cRange.unbridged(), /*Implicit=*/false);
430430
}
431431

432+
BridgedAllowFeatureSuppressionAttr
433+
BridgedAllowFeatureSuppressionAttr_createParsed(BridgedASTContext cContext,
434+
BridgedSourceLoc cAtLoc,
435+
BridgedSourceRange cRange,
436+
BridgedArrayRef cFeatures) {
437+
SmallVector<Identifier> features;
438+
for (auto elem : cFeatures.unbridged<BridgedIdentifier>())
439+
features.push_back(elem.unbridged());
440+
return AllowFeatureSuppressionAttr::create(cContext.unbridged(),
441+
cAtLoc.unbridged(), cRange.unbridged(), /*implicit*/ false, features);
442+
}
443+
432444
BridgedCDeclAttr BridgedCDeclAttr_createParsed(BridgedASTContext cContext,
433445
BridgedSourceLoc cAtLoc,
434446
BridgedSourceRange cRange,

lib/AST/ASTPrinter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3112,6 +3112,8 @@ static void suppressingFeature(PrintOptions &options, Feature feature,
31123112
case Feature::FeatureName: \
31133113
suppressingFeature##FeatureName(options, action); \
31143114
return;
3115+
#define CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
3116+
SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description)
31153117
#include "swift/Basic/Features.def"
31163118
}
31173119
llvm_unreachable("exhaustive switch");

lib/AST/Attr.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1918,6 +1918,8 @@ StringRef DeclAttribute::getAttrName() const {
19181918
return "_rawLayout";
19191919
case DeclAttrKind::Extern:
19201920
return "_extern";
1921+
case DeclAttrKind::AllowFeatureSuppression:
1922+
return "_allowFeatureSuppression";
19211923
}
19221924
llvm_unreachable("bad DeclAttrKind");
19231925
}
@@ -2901,6 +2903,26 @@ StorageRestrictionsAttr::getAccessesProperties(AccessorDecl *attachedTo) const {
29012903
{});
29022904
}
29032905

2906+
AllowFeatureSuppressionAttr::AllowFeatureSuppressionAttr(SourceLoc atLoc,
2907+
SourceRange range,
2908+
bool implicit,
2909+
ArrayRef<Identifier> features)
2910+
: DeclAttribute(DeclAttrKind::AllowFeatureSuppression,
2911+
atLoc, range, implicit) {
2912+
Bits.AllowFeatureSuppressionAttr.NumFeatures = features.size();
2913+
std::uninitialized_copy(features.begin(), features.end(),
2914+
getTrailingObjects<Identifier>());
2915+
}
2916+
2917+
AllowFeatureSuppressionAttr *
2918+
AllowFeatureSuppressionAttr::create(ASTContext &ctx, SourceLoc atLoc,
2919+
SourceRange range, bool implicit,
2920+
ArrayRef<Identifier> features) {
2921+
unsigned size = totalSizeToAlloc<Identifier>(features.size());
2922+
auto *mem = ctx.Allocate(size, alignof(AllowFeatureSuppressionAttr));
2923+
return new (mem) AllowFeatureSuppressionAttr(atLoc, range, implicit, features);
2924+
}
2925+
29042926
void swift::simple_display(llvm::raw_ostream &out, const DeclAttribute *attr) {
29052927
if (attr)
29062928
attr->print(out);

lib/AST/FeatureSet.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -677,17 +677,27 @@ static bool usesFeatureIsolatedAny(Decl *decl) {
677677

678678
void FeatureSet::collectRequiredFeature(Feature feature,
679679
InsertOrRemove operation) {
680-
assert(!isSuppressibleFeature(feature));
681680
required.insertOrRemove(feature, operation == Insert);
682681
}
683682

684683
void FeatureSet::collectSuppressibleFeature(Feature feature,
685684
InsertOrRemove operation) {
686-
assert(isSuppressibleFeature(feature));
687685
suppressible.insertOrRemove(numFeatures() - size_t(feature),
688686
operation == Insert);
689687
}
690688

689+
static bool shouldSuppressFeature(StringRef featureName, Decl *decl) {
690+
auto attr = decl->getAttrs().getAttribute<AllowFeatureSuppressionAttr>();
691+
if (!attr) return false;
692+
693+
for (auto suppressedFeature : attr->getSuppressedFeatures()) {
694+
if (suppressedFeature.is(featureName))
695+
return true;
696+
}
697+
698+
return false;
699+
}
700+
691701
/// Go through all the features used by the given declaration and
692702
/// either add or remove them to this set.
693703
void FeatureSet::collectFeaturesUsed(Decl *decl, InsertOrRemove operation) {
@@ -699,6 +709,13 @@ void FeatureSet::collectFeaturesUsed(Decl *decl, InsertOrRemove operation) {
699709
#define SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
700710
if (usesFeature##FeatureName(decl)) \
701711
collectSuppressibleFeature(Feature::FeatureName, operation);
712+
#define CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
713+
if (usesFeature##FeatureName(decl)) { \
714+
if (shouldSuppressFeature(#FeatureName, decl)) \
715+
collectSuppressibleFeature(Feature::FeatureName, operation); \
716+
else \
717+
collectRequiredFeature(Feature::FeatureName, operation); \
718+
}
702719
#include "swift/Basic/Features.def"
703720
}
704721

lib/ASTGen/Sources/ASTGen/DeclAttrs.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ extension ASTGenVisitor {
185185
fatalError("unimplemented")
186186
case .unavailableFromAsync:
187187
fatalError("unimplemented")
188+
case .allowFeatureSuppression:
189+
return self.generateAllowFeatureSuppressionAttr(attribute: node)?.asDeclAttribute
188190

189191
// Simple attributes.
190192
case .alwaysEmitConformanceMetadata,
@@ -336,6 +338,32 @@ extension ASTGenVisitor {
336338
)
337339
}
338340

341+
func generateAllowFeatureSuppressionAttr(attribute node: AttributeSyntax) -> BridgedAllowFeatureSuppressionAttr? {
342+
guard case .argumentList(let args) = node.arguments
343+
else {
344+
// TODO: Diagnose.
345+
return nil
346+
}
347+
348+
let features = args.compactMap(in: self) { arg -> BridgedIdentifier? in
349+
guard arg.label == nil,
350+
let declNameExpr = arg.expression.as(DeclReferenceExprSyntax.self),
351+
declNameExpr.argumentNames == nil
352+
else {
353+
// TODO: Diagnose.
354+
return nil
355+
}
356+
357+
return generateIdentifier(declNameExpr.baseName)
358+
}
359+
360+
return .createParsed(
361+
self.ctx,
362+
atLoc: self.generateSourceLoc(node.atSign),
363+
range: self.generateSourceRange(node),
364+
features: features)
365+
}
366+
339367
func generateCDeclAttr(attribute node: AttributeSyntax) -> BridgedCDeclAttr? {
340368
guard
341369
// `@_cdecl` attribute has `.string(StringLiteralExprSyntax)` arguments.

lib/Basic/LangOptions.cpp

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -598,19 +598,6 @@ llvm::StringRef swift::getFeatureName(Feature feature) {
598598
llvm_unreachable("covered switch");
599599
}
600600

601-
bool swift::isSuppressibleFeature(Feature feature) {
602-
switch (feature) {
603-
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
604-
case Feature::FeatureName: \
605-
return false;
606-
#define SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
607-
case Feature::FeatureName: \
608-
return true;
609-
#include "swift/Basic/Features.def"
610-
}
611-
llvm_unreachable("covered switch");
612-
}
613-
614601
bool swift::isFeatureAvailableInProduction(Feature feature) {
615602
switch (feature) {
616603
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \

0 commit comments

Comments
 (0)