Skip to content

Allow declarations to opt in to suppressing @isolated(any) instead of suppressing the entire declaration #72008

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 3 commits into from
Mar 2, 2024
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
8 changes: 8 additions & 0 deletions include/swift/AST/ASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,14 @@ BridgedAlignmentAttr_createParsed(BridgedASTContext cContext,
BridgedSourceLoc cAtLoc,
BridgedSourceRange cRange, size_t cValue);

SWIFT_NAME("BridgedAllowFeatureSuppressionAttr.createParsed(_:atLoc:range:features:)")
BridgedAllowFeatureSuppressionAttr
BridgedAllowFeatureSuppressionAttr_createParsed(
BridgedASTContext cContext,
BridgedSourceLoc cAtLoc,
BridgedSourceRange cRange,
BridgedArrayRef cFeatures);

SWIFT_NAME("BridgedCDeclAttr.createParsed(_:atLoc:range:name:)")
BridgedCDeclAttr BridgedCDeclAttr_createParsed(BridgedASTContext cContext,
BridgedSourceLoc cAtLoc,
Expand Down
31 changes: 31 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ class DeclAttribute : public AttributeBase {
SWIFT_INLINE_BITFIELD(NonisolatedAttr, DeclAttribute, 1,
isUnsafe : 1
);

SWIFT_INLINE_BITFIELD_FULL(AllowFeatureSuppressionAttr, DeclAttribute, 32,
: NumPadBits,
NumFeatures : 32
);
} Bits;
// clang-format on

Expand Down Expand Up @@ -2636,6 +2641,32 @@ template <typename ATTR, bool AllowInvalid> struct ToAttributeKind {
}
};

/// The @_allowFeatureSuppression(Foo, Bar) attribute. The feature
/// names are intentionally not validated, and the attribute itself is
/// not printed when rendering a module interface.
class AllowFeatureSuppressionAttr final
: public DeclAttribute,
private llvm::TrailingObjects<AllowFeatureSuppressionAttr, Identifier> {
friend TrailingObjects;

/// Create an implicit @objc attribute with the given (optional) name.
AllowFeatureSuppressionAttr(SourceLoc atLoc, SourceRange range,
bool implicit, ArrayRef<Identifier> features);
public:
static AllowFeatureSuppressionAttr *create(ASTContext &ctx, SourceLoc atLoc,
SourceRange range, bool implicit,
ArrayRef<Identifier> features);

ArrayRef<Identifier> getSuppressedFeatures() const {
return {getTrailingObjects<Identifier>(),
Bits.AllowFeatureSuppressionAttr.NumFeatures};
}

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

/// Attributes that may be applied to declarations.
class DeclAttributes {
/// Linked list of declaration attributes.
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 @@ -488,7 +488,10 @@ SIMPLE_DECL_ATTR(_noObjCBridging, NoObjCBridging,
DECL_ATTR(_distributedThunkTarget, DistributedThunkTarget,
OnAbstractFunction | UserInaccessible | ABIStableToAdd | ABIBreakingToRemove | APIStableToAdd | APIStableToRemove,
156)
LAST_DECL_ATTR(DistributedThunkTarget)
DECL_ATTR(_allowFeatureSuppression, AllowFeatureSuppression,
OnAnyDecl | UserInaccessible | NotSerialized | ABIStableToAdd | APIStableToAdd | ABIStableToRemove | APIStableToRemove,
157)
LAST_DECL_ATTR(AllowFeatureSuppression)

#undef DECL_ATTR_ALIAS
#undef CONTEXTUAL_DECL_ATTR_ALIAS
Expand Down
126 changes: 121 additions & 5 deletions include/swift/AST/DiagnosticEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ namespace swift {
};
}

template <class... ArgTypes>
using DiagArgTuple =
std::tuple<typename detail::PassArgument<ArgTypes>::type...>;

/// A family of wrapper types for compiler data types that forces its
/// underlying data to be formatted with full qualification.
///
Expand Down Expand Up @@ -476,20 +480,28 @@ namespace swift {
friend DiagnosticEngine;
friend class InFlightDiagnostic;

Diagnostic(DiagID ID) : ID(ID) {}

public:
// All constructors are intentionally implicit.
template<typename ...ArgTypes>
Diagnostic(Diag<ArgTypes...> ID,
typename detail::PassArgument<ArgTypes>::type... VArgs)
: ID(ID.ID) {
DiagnosticArgument DiagArgs[] = {
DiagnosticArgument(0), std::move(VArgs)...
};
Args.append(DiagArgs + 1, DiagArgs + 1 + sizeof...(VArgs));
: Diagnostic(ID.ID) {
Args.reserve(sizeof...(ArgTypes));
gatherArgs(VArgs...);
}

/*implicit*/Diagnostic(DiagID ID, ArrayRef<DiagnosticArgument> Args)
: ID(ID), Args(Args.begin(), Args.end()) {}

template <class... ArgTypes>
static Diagnostic fromTuple(Diag<ArgTypes...> id,
const DiagArgTuple<ArgTypes...> &tuple) {
Diagnostic result(id.ID);
result.gatherArgsFromTuple<DiagArgTuple<ArgTypes...>, 0, ArgTypes...>(tuple);
return result;
}

// Accessors.
DiagID getID() const { return ID; }
Expand Down Expand Up @@ -528,6 +540,37 @@ namespace swift {

void addChildNote(Diagnostic &&D);
void insertChildNote(unsigned beforeIndex, Diagnostic &&D);

private:
// gatherArgs could just be `Args.emplace_back(args)...;` if C++
// allowed pack expansions in statement context.

// Base case.
void gatherArgs() {}

// Pull one off the pack.
template <class ArgType, class... RemainingArgTypes>
void gatherArgs(ArgType arg, RemainingArgTypes... remainingArgs) {
Args.emplace_back(arg);
gatherArgs(remainingArgs...);
}

// gatherArgsFromTuple could just be
// `Args.emplace_back(std::get<packIndexOf<ArgTypes>>(tuple))...;`
// in a better world.

// Base case.
template <class Tuple, size_t Index>
void gatherArgsFromTuple(const Tuple &tuple) {}

// Pull one off the pack.
template <class Tuple, size_t Index,
class ArgType, class... RemainingArgTypes>
void gatherArgsFromTuple(const Tuple &tuple) {
Args.emplace_back(std::move(std::get<Index>(tuple)));
gatherArgsFromTuple<Tuple, Index + 1, RemainingArgTypes...>(
std::move(tuple));
}
};

/// A diagnostic that has no input arguments, so it is trivially-destructable.
Expand Down Expand Up @@ -866,6 +909,73 @@ namespace swift {
DiagnosticState &operator=(DiagnosticState &&) = default;
};

/// A lightweight reference to a diagnostic that's been fully applied to
/// its arguments. This allows a general routine (in the parser, say) to
/// be customized to emit an arbitrary diagnostic without needing to
/// eagerly construct a full Diagnostic. Like ArrayRef and function_ref,
/// this stores a reference to what's likely to be a temporary, so it
/// should only be used as a function parameter. If you need to persist
/// the diagnostic, you'll have to call createDiagnostic().
///
/// You can initialize a DiagRef parameter in one of two ways:
/// - passing a Diag<> as the argument, e.g.
/// diag::circular_reference
/// or
/// - constructing it with a Diag and its arguments, e.g.
/// {diag::circular_protocol_def, {proto->getName()}}
///
/// It'd be nice to let people write `{diag::my_error, arg0, arg1}`
/// instead of `{diag::my_error, {arg0, arg1}}`, but we can't: the
/// temporary needs to be created in the calling context.
class DiagRef {
DiagID id;

/// If this is null, then id is a Diag<> and there are no arguments.
Diagnostic (*createFn)(DiagID id, const void *opaqueStorage);
const void *opaqueStorage;

public:
/// Construct a diagnostic from a diagnostic ID that's known to not take
/// arguments.
DiagRef(Diag<> id)
: id(id.ID), createFn(nullptr), opaqueStorage(nullptr) {}

/// Construct a diagnostic from a diagnostic ID and its arguments.
template <class... ArgTypes>
DiagRef(Diag<ArgTypes...> id, const DiagArgTuple<ArgTypes...> &tuple)
: id(id.ID),
createFn(&createFromTuple<ArgTypes...>),
opaqueStorage(&tuple) {}

// A specialization of the general constructor above for diagnostics
// with no arguments; this is a useful optimization when a DiagRef
// is constructed generically.
DiagRef(Diag<> id, const DiagArgTuple<> &tuple)
: DiagRef(id) {}

/// Return the diagnostic ID that this will emit.
DiagID getID() const {
return id;
}

/// Create a full Diagnostic. It's safe to do this multiple times on
/// a single DiagRef.
Diagnostic createDiagnostic() {
if (!createFn) {
return Diagnostic(Diag<> {id});
} else {
return createFn(id, opaqueStorage);
}
}

private:
template <class... ArgTypes>
static Diagnostic createFromTuple(DiagID id, const void *opaqueStorage) {
auto tuple = static_cast<const DiagArgTuple<ArgTypes...> *>(opaqueStorage);
return Diagnostic::fromTuple(Diag<ArgTypes...> {id}, *tuple);
}
};

/// Class responsible for formatting diagnostics and presenting them
/// to the user.
class DiagnosticEngine {
Expand Down Expand Up @@ -1113,6 +1223,12 @@ namespace swift {
return diagnose(Loc, Diagnostic(ID, std::move(Args)...));
}

/// Emit the given lazily-applied diagnostic at the specified
/// source location.
InFlightDiagnostic diagnose(SourceLoc loc, DiagRef diag) {
return diagnose(loc, diag.createDiagnostic());
}

/// Delete an API that may lead clients to avoid specifying source location.
template<typename ...ArgTypes>
InFlightDiagnostic
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -1889,6 +1889,10 @@ ERROR(attr_rawlayout_expected_params,none,

ERROR(attr_extern_expected_label,none,
"expected %0 argument to @_extern attribute", (StringRef))

ERROR(attr_expected_feature_name,none,
"expected feature name in @%0 attribute", (StringRef))

//------------------------------------------------------------------------------
// MARK: Generics parsing diagnostics
//------------------------------------------------------------------------------
Expand Down
6 changes: 5 additions & 1 deletion include/swift/AST/PrintOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -376,10 +376,14 @@ struct PrintOptions {
/// `AsyncIteratorProtocol` for backward-compatibility reasons.
bool AsyncSequenceRethrows = false;

/// Suppress the @isolated(any) attribute.
bool SuppressIsolatedAny = false;

/// List of attribute kinds that should not be printed.
std::vector<AnyAttrKind> ExcludeAttrList = {
DeclAttrKind::Transparent, DeclAttrKind::Effects,
DeclAttrKind::FixedLayout, DeclAttrKind::ShowInInterface};
DeclAttrKind::FixedLayout, DeclAttrKind::ShowInInterface,
};

/// List of attribute kinds that should be printed exclusively.
/// Empty means allow all.
Expand Down
3 changes: 0 additions & 3 deletions include/swift/Basic/Feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ constexpr unsigned numFeatures() {
return NumFeatures;
}

/// Determine whether the given feature is suppressible.
bool isSuppressibleFeature(Feature feature);

/// Check whether the given feature is available in production compilers.
bool isFeatureAvailableInProduction(Feature feature);

Expand Down
47 changes: 45 additions & 2 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
// imply the existence of earlier features. (This only needs to apply to
// suppressible features.)
//
// If suppressing a feature in general is problematic, but it's okay to
// suppress it for specific declarations, the feature can be made
// conditionally suppressible. Declarations opt in to suppression with
// the @_allowFeatureSuppression attribute.
//
// BASELINE_LANGUAGE_FEATURE is the same as LANGUAGE_FEATURE, but is used
// for features that can be assumed to be available in any Swift compiler that
// will be used to process the textual interface files produced by this
Expand All @@ -44,11 +49,46 @@
# error define LANGUAGE_FEATURE before including Features.def
#endif

// A feature that's both suppressible and experimental.
// Delegates to whichever the includer defines.
#ifndef SUPPRESSIBLE_EXPERIMENTAL_FEATURE
# if defined(SUPPRESSIBLE_LANGUAGE_FEATURE) && \
defined(EXPERIMENTAL_FEATURE)
# error ambiguous defines when including Features.def
# elif defined(SUPPRESSIBLE_LANGUAGE_FEATURE)
# define SUPPRESSIBLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \
SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, 0, #FeatureName)
# else
# define SUPPRESSIBLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \
EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd)
# endif
#endif

#ifndef SUPPRESSIBLE_LANGUAGE_FEATURE
#define SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
# define SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
LANGUAGE_FEATURE(FeatureName, SENumber, Description)
#endif

// A feature that's both conditionally-suppressible and experimental.
// Delegates to whichever the includer defines.
#ifndef CONDITIONALLY_SUPPRESSIBLE_EXPERIMENTAL_FEATURE
# if defined(CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE) && \
defined(EXPERIMENTAL_FEATURE)
# error ambiguous defines when including Features.def
# elif defined(CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE)
# define CONDITIONALLY_SUPPRESSIBLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \
CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, 0, #FeatureName)
# else
# define CONDITIONALLY_SUPPRESSIBLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \
EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd)
# endif
#endif

#ifndef CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE
# define CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
LANGUAGE_FEATURE(FeatureName, SENumber, Description)
#endif

#ifndef UPCOMING_FEATURE
# define UPCOMING_FEATURE(FeatureName, SENumber, Version) \
LANGUAGE_FEATURE(FeatureName, SENumber, #FeatureName)
Expand Down Expand Up @@ -295,11 +335,14 @@ EXPERIMENTAL_FEATURE(DynamicActorIsolation, false)
EXPERIMENTAL_FEATURE(BorrowingSwitch, true)

// Enable isolated(any) attribute on function types.
EXPERIMENTAL_FEATURE(IsolatedAny, true)
CONDITIONALLY_SUPPRESSIBLE_EXPERIMENTAL_FEATURE(IsolatedAny, true)

#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
#undef EXPERIMENTAL_FEATURE
#undef UPCOMING_FEATURE
#undef BASELINE_LANGUAGE_FEATURE
#undef CONDITIONALLY_SUPPRESSIBLE_EXPERIMENTAL_FEATURE
#undef CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE
#undef SUPPRESSIBLE_EXPERIMENTAL_FEATURE
#undef SUPPRESSIBLE_LANGUAGE_FEATURE
#undef LANGUAGE_FEATURE
Loading