Skip to content

Commit

Permalink
Merge pull request swiftlang#79792 from AnthonyLatsis/danaus-plexippus-2
Browse files Browse the repository at this point in the history
Introduce adoption mode for Swift features (take 2)
  • Loading branch information
AnthonyLatsis authored Mar 7, 2025
2 parents 38c8cc8 + 5c373a5 commit 5d92f79
Show file tree
Hide file tree
Showing 26 changed files with 1,427 additions and 105 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@
/unittests/AST/ @hborla @slavapestov @xedin
/unittests/AST/*Evaluator* @CodaFi @slavapestov
/unittests/DependencyScan/ @artemcm @cachemeifyoucan
/unittests/FrontendTool/ @artemcm @tshortli
/unittests/Frontend*/ @artemcm @tshortli
/unittests/Parse/ @ahoppen @bnbarham @CodaFi @DougGregor @hamishknight @rintaro
/unittests/Reflection/ @slavapestov
/unittests/SIL/ @jckarter
Expand Down
16 changes: 15 additions & 1 deletion include/swift/AST/DiagnosticsFrontend.def
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 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
Expand Down Expand Up @@ -50,6 +50,20 @@ GROUPED_WARNING(feature_not_experimental, StrictLanguageFeatures, DefaultIgnore,
"use -%select{disable|enable}1-upcoming-feature instead",
(StringRef, bool))

GROUPED_WARNING(invalid_feature_mode, StrictLanguageFeatures, none,
"'%0' is not a recognized mode for feature '%1'"
"%select{|; did you mean '%2'?}3",
(StringRef, StringRef, StringRef, bool))

GROUPED_WARNING(cannot_disable_feature_with_mode, StrictLanguageFeatures, none,
"'%0' argument '%1' cannot specify a mode",
(StringRef, StringRef))

GROUPED_WARNING(feature_does_not_support_adoption_mode, StrictLanguageFeatures,
none,
"feature '%0' does not support adoption mode",
(StringRef))

ERROR(error_unknown_library_level, none,
"unknown library level '%0', "
"expected one of 'api', 'spi', 'ipi', or 'other'", (StringRef))
Expand Down
5 changes: 3 additions & 2 deletions include/swift/Basic/BasicBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2022 Apple Inc. and the Swift project authors
// Copyright (c) 2022 - 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
Expand Down Expand Up @@ -214,7 +214,8 @@ void BridgedData_free(BridgedData data);
//===----------------------------------------------------------------------===//

enum ENUM_EXTENSIBILITY_ATTR(open) BridgedFeature {
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) FeatureName,
#define LANGUAGE_FEATURE(FeatureName, IsAdoptable, SENumber, Description) \
FeatureName,
#include "swift/Basic/Features.def"
};

Expand Down
13 changes: 9 additions & 4 deletions include/swift/Basic/Feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 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
Expand All @@ -21,14 +21,16 @@ namespace swift {
class LangOptions;

/// Enumeration describing all of the named features.
enum class Feature {
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) FeatureName,
enum class Feature : uint16_t {
#define LANGUAGE_FEATURE(FeatureName, IsAdoptable, SENumber, Description) \
FeatureName,
#include "swift/Basic/Features.def"
};

constexpr unsigned numFeatures() {
enum Features {
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) FeatureName,
#define LANGUAGE_FEATURE(FeatureName, IsAdoptable, SENumber, Description) \
FeatureName,
#include "swift/Basic/Features.def"
NumFeatures
};
Expand Down Expand Up @@ -61,6 +63,9 @@ std::optional<Feature> getExperimentalFeature(llvm::StringRef name);
/// \c None if it does not have such a version.
std::optional<unsigned> getFeatureLanguageVersion(Feature feature);

/// Determine whether the given feature supports adoption mode.
bool isFeatureAdoptable(Feature feature);

/// Determine whether this feature should be included in the
/// module interface
bool includeInModuleInterface(Feature feature);
Expand Down
76 changes: 56 additions & 20 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,28 @@
// features.
//
//
// LANGUAGE_FEATURE(FeatureName, SENumber, Description)
// LANGUAGE_FEATURE(FeatureName, IsAdoptable, SENumber, Description)
//
// The LANGUAGE_FEATURE macro describes each named feature that is
// introduced in Swift. It allows Swift code to check for a particular
// feature with "#if $FeatureName" in source code.
//
// FeatureName: The name given to this feature to be used in source code,
// e.g., AsyncAwait.
// IsAdoptable: Whether the feature implements adoption mode.
//
// If the feature is upcoming (source-breaking) and provides for a
// mechanical code migration, it should implement adoption mode.
//
// Adoption mode is a feature-oriented code migration mechanism: a mode
// of operation that should produce compiler warnings with attached
// fix-its that can be applied to preserve the behavior of the code once
// the upcoming feature is enacted.
// These warnings must belong to a diagnostic group named after the
// feature. Adoption mode itself *and* the fix-its it produces must be
// source and binary compatible with how the code is compiled when the
// feature is disabled.
//
// SENumber: The number assigned to this feature in the Swift Evolution
// process, or 0 if there isn't one.
// Description: A string literal describing the feature.
Expand Down Expand Up @@ -91,13 +105,14 @@
#endif

#ifndef SUPPRESSIBLE_LANGUAGE_FEATURE
# define SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
LANGUAGE_FEATURE(FeatureName, SENumber, Description)
#define SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
LANGUAGE_FEATURE(FeatureName, /*IsAdoptable=*/false, SENumber, \
Description)
#endif

#ifndef OPTIONAL_LANGUAGE_FEATURE
# define OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
LANGUAGE_FEATURE(FeatureName, SENumber, Description)
#define OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
LANGUAGE_FEATURE(FeatureName, /*IsAdoptable=*/false, SENumber, Description)
#endif

// A feature that's both conditionally-suppressible and experimental.
Expand All @@ -116,20 +131,35 @@
#endif

#ifndef CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE
# define CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
LANGUAGE_FEATURE(FeatureName, SENumber, Description)
#define CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, \
Description) \
LANGUAGE_FEATURE(FeatureName, /*IsAdoptable=*/false, SENumber, \
Description)
#endif

// An upcoming feature that supports adoption mode.
#ifndef ADOPTABLE_UPCOMING_FEATURE
#if defined(UPCOMING_FEATURE)
#define ADOPTABLE_UPCOMING_FEATURE(FeatureName, SENumber, Version) \
UPCOMING_FEATURE(FeatureName, SENumber, Version)
#else
#define ADOPTABLE_UPCOMING_FEATURE(FeatureName, SENumber, Version) \
LANGUAGE_FEATURE(FeatureName, /*IsAdoptable=*/true, SENumber, \
#FeatureName)
#endif
#endif

#ifndef UPCOMING_FEATURE
# define UPCOMING_FEATURE(FeatureName, SENumber, Version) \
LANGUAGE_FEATURE(FeatureName, SENumber, #FeatureName)
#define UPCOMING_FEATURE(FeatureName, SENumber, Version) \
LANGUAGE_FEATURE(FeatureName, /*IsAdoptable=*/false, SENumber, \
#FeatureName)
#endif

#ifndef EXPERIMENTAL_FEATURE
// Warning: setting `AvailableInProd` to `true` on a feature means that the flag
// cannot be dropped in the future.
# define EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \
LANGUAGE_FEATURE(FeatureName, 0, #FeatureName)
// Warning: setting `AvailableInProd` to `true` on a feature means that the
// flag cannot be dropped in the future.
#define EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \
LANGUAGE_FEATURE(FeatureName, /*IsAdoptable=*/false, 0, #FeatureName)
#endif

#ifndef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
Expand All @@ -138,8 +168,9 @@
#endif

#ifndef BASELINE_LANGUAGE_FEATURE
# define BASELINE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
LANGUAGE_FEATURE(FeatureName, SENumber, Description)
#define BASELINE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
LANGUAGE_FEATURE(FeatureName, /*IsAdoptable=*/false, SENumber, \
Description)
#endif

BASELINE_LANGUAGE_FEATURE(AsyncAwait, 296, "async/await")
Expand Down Expand Up @@ -210,10 +241,14 @@ BASELINE_LANGUAGE_FEATURE(BodyMacros, 415, "Function body macros")
SUPPRESSIBLE_LANGUAGE_FEATURE(SendingArgsAndResults, 430, "Sending arg and results")
BASELINE_LANGUAGE_FEATURE(BorrowingSwitch, 432, "Noncopyable type pattern matching")
CONDITIONALLY_SUPPRESSIBLE_LANGUAGE_FEATURE(IsolatedAny, 431, "@isolated(any) function types")
LANGUAGE_FEATURE(IsolatedAny2, 431, "@isolated(any) function types")
LANGUAGE_FEATURE(ObjCImplementation, 436, "@objc @implementation extensions")
LANGUAGE_FEATURE(NonescapableTypes, 446, "Nonescapable types")
LANGUAGE_FEATURE(BuiltinEmplaceTypedThrows, 0, "Builtin.emplace typed throws")
LANGUAGE_FEATURE(IsolatedAny2, /*IsAdoptable=*/false, 431,
"@isolated(any) function types")
LANGUAGE_FEATURE(ObjCImplementation, /*IsAdoptable=*/false, 436,
"@objc @implementation extensions")
LANGUAGE_FEATURE(NonescapableTypes, /*IsAdoptable=*/false, 446,
"Nonescapable types")
LANGUAGE_FEATURE(BuiltinEmplaceTypedThrows, /*IsAdoptable=*/false, 0,
"Builtin.emplace typed throws")
SUPPRESSIBLE_LANGUAGE_FEATURE(MemorySafetyAttributes, 458, "@unsafe attribute")

// Swift 6
Expand All @@ -234,7 +269,7 @@ UPCOMING_FEATURE(NonfrozenEnumExhaustivity, 192, 6)
UPCOMING_FEATURE(GlobalActorIsolatedTypesUsability, 0434, 6)

// Swift 7
UPCOMING_FEATURE(ExistentialAny, 335, 7)
ADOPTABLE_UPCOMING_FEATURE(ExistentialAny, 335, 7)
UPCOMING_FEATURE(InternalImportsByDefault, 409, 7)
UPCOMING_FEATURE(MemberImportVisibility, 444, 7)

Expand Down Expand Up @@ -485,6 +520,7 @@ EXPERIMENTAL_FEATURE(CompileTimeValues, true)
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
#undef EXPERIMENTAL_FEATURE
#undef UPCOMING_FEATURE
#undef ADOPTABLE_UPCOMING_FEATURE
#undef BASELINE_LANGUAGE_FEATURE
#undef OPTIONAL_LANGUAGE_FEATURE
#undef CONDITIONALLY_SUPPRESSIBLE_EXPERIMENTAL_FEATURE
Expand Down
82 changes: 63 additions & 19 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 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
Expand All @@ -20,7 +20,6 @@

#include "swift/Basic/CXXStdlibKind.h"
#include "swift/Basic/Feature.h"
#include "swift/Basic/FixedBitSet.h"
#include "swift/Basic/FunctionBodySkipping.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/PlaygroundOption.h"
Expand Down Expand Up @@ -725,19 +724,6 @@ namespace swift {
return cxxInteropCompatVersion.isVersionAtLeast(major, minor);
}

/// Determine whether the given feature is enabled.
bool hasFeature(Feature feature) const;

/// Determine whether the given feature is enabled, looking up the feature
/// by name.
bool hasFeature(llvm::StringRef featureName) const;

/// Enable the given feature.
void enableFeature(Feature feature) { Features.insert(feature); }

/// Disable the given feature.
void disableFeature(Feature feature) { Features.remove(feature); }

/// Sets the "_hasAtomicBitWidth" conditional.
void setHasAtomicBitWidth(llvm::Triple triple);

Expand Down Expand Up @@ -822,10 +808,68 @@ namespace swift {
PlatformConditionValues;
llvm::SmallVector<std::string, 2> CustomConditionalCompilationFlags;

/// The set of features that have been enabled. Doesn't include upcoming
/// features, which are checked against the language version in
/// `hasFeature`.
FixedBitSet<numFeatures(), Feature> Features;
public:
// MARK: Features
// =========================================================================

/// A wrapper around the feature state enumeration.
struct FeatureState {
enum Kind : uint8_t { Off, EnabledForAdoption, Enabled };

private:
Feature feature;
Kind state;

public:
FeatureState(Feature feature, Kind state)
: feature(feature), state(state) {}

/// Returns whether the feature is enabled.
bool isEnabled() const;

/// Returns whether the feature is enabled in adoption mode. Should only
/// be called if the feature is known to support this mode.
bool isEnabledForAdoption() const;

operator Kind() const { return state; }
};

private:
class FeatureStateStorage {
std::vector<FeatureState::Kind> states;

public:
FeatureStateStorage();

/// Sets the given state for the given feature.
void setState(Feature feature, FeatureState::Kind state);

/// Retrieves the state of the given feature.
FeatureState getState(Feature feature) const;
};

/// The states of language features.
FeatureStateStorage featureStates;

public:
/// Retrieve the state of the given feature.
FeatureState getFeatureState(Feature feature) const;

/// Returns whether the given feature is enabled.
bool hasFeature(Feature feature) const;

/// Returns whether a feature with the given name is enabled. Returns
/// `false` if a feature by this name is not known.
bool hasFeature(llvm::StringRef featureName) const;

/// Enables the given feature (enables in adoption mode if `forAdoption` is
/// `true`).
void enableFeature(Feature feature, bool forAdoption = false);

/// Disables the given feature.
void disableFeature(Feature feature);

// =========================================================================
};

class TypeCheckerOptions final {
Expand Down
4 changes: 2 additions & 2 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 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
Expand Down Expand Up @@ -3267,7 +3267,7 @@ suppressingFeatureExecutionAttribute(PrintOptions &options,
static void suppressingFeature(PrintOptions &options, Feature feature,
llvm::function_ref<void()> action) {
switch (feature) {
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
#define LANGUAGE_FEATURE(FeatureName, IsAdoptable, SENumber, Description) \
case Feature::FeatureName: \
llvm_unreachable("not a suppressible feature");
#define SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
Expand Down
6 changes: 3 additions & 3 deletions lib/AST/FeatureSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift project authors
// 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
Expand Down Expand Up @@ -45,7 +45,7 @@ static bool usesTypeMatching(Decl *decl, llvm::function_ref<bool(Type)> fn) {

#define BASELINE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
static bool usesFeature##FeatureName(Decl *decl) { return false; }
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description)
#define LANGUAGE_FEATURE(FeatureName, IsAdoptable, SENumber, Description)
#include "swift/Basic/Features.def"

#define UNINTERESTING_FEATURE(FeatureName) \
Expand Down Expand Up @@ -551,7 +551,7 @@ void FeatureSet::collectFeaturesUsed(Decl *decl, InsertOrRemove operation) {

// Go through each of the features, checking whether the
// declaration uses that feature.
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
#define LANGUAGE_FEATURE(FeatureName, IsAdoptable, SENumber, Description) \
if (CHECK(usesFeature##FeatureName)) \
collectRequiredFeature(Feature::FeatureName, operation);
#define SUPPRESSIBLE_LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
Expand Down
Loading

0 comments on commit 5d92f79

Please sign in to comment.