Skip to content

Introduce adoption mode for Swift features #79649

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 9 commits into from
Mar 4, 2025
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 @@ -476,6 +511,7 @@ EXPERIMENTAL_FEATURE(IsolatedConformances, 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 @@ -542,7 +542,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