Skip to content

Commit 2771f36

Browse files
committed
LangOptions: Refactor feature state and API to account for adoption mode
1 parent 2a88419 commit 2771f36

File tree

9 files changed

+247
-123
lines changed

9 files changed

+247
-123
lines changed

include/swift/Basic/Feature.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace swift {
2121
class LangOptions;
2222

2323
/// Enumeration describing all of the named features.
24-
enum class Feature {
24+
enum class Feature : uint16_t {
2525
#define LANGUAGE_FEATURE(FeatureName, IsAdoptable, SENumber, Description) \
2626
FeatureName,
2727
#include "swift/Basic/Features.def"

include/swift/Basic/LangOptions.h

Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
@@ -20,7 +20,6 @@
2020

2121
#include "swift/Basic/CXXStdlibKind.h"
2222
#include "swift/Basic/Feature.h"
23-
#include "swift/Basic/FixedBitSet.h"
2423
#include "swift/Basic/FunctionBodySkipping.h"
2524
#include "swift/Basic/LLVM.h"
2625
#include "swift/Basic/PlaygroundOption.h"
@@ -725,19 +724,6 @@ namespace swift {
725724
return cxxInteropCompatVersion.isVersionAtLeast(major, minor);
726725
}
727726

728-
/// Determine whether the given feature is enabled.
729-
bool hasFeature(Feature feature) const;
730-
731-
/// Determine whether the given feature is enabled, looking up the feature
732-
/// by name.
733-
bool hasFeature(llvm::StringRef featureName) const;
734-
735-
/// Enable the given feature.
736-
void enableFeature(Feature feature) { Features.insert(feature); }
737-
738-
/// Disable the given feature.
739-
void disableFeature(Feature feature) { Features.remove(feature); }
740-
741727
/// Sets the "_hasAtomicBitWidth" conditional.
742728
void setHasAtomicBitWidth(llvm::Triple triple);
743729

@@ -822,10 +808,68 @@ namespace swift {
822808
PlatformConditionValues;
823809
llvm::SmallVector<std::string, 2> CustomConditionalCompilationFlags;
824810

825-
/// The set of features that have been enabled. Doesn't include upcoming
826-
/// features, which are checked against the language version in
827-
/// `hasFeature`.
828-
FixedBitSet<numFeatures(), Feature> Features;
811+
public:
812+
// MARK: Features
813+
// =========================================================================
814+
815+
/// A wrapper around the feature state enumeration.
816+
struct FeatureState {
817+
enum Kind : uint8_t { Off, EnabledForAdoption, Enabled };
818+
819+
private:
820+
Feature feature;
821+
Kind state;
822+
823+
public:
824+
FeatureState(Feature feature, Kind state)
825+
: feature(feature), state(state) {}
826+
827+
/// Returns whether the feature is enabled.
828+
bool isEnabled() const;
829+
830+
/// Returns whether the feature is enabled in adoption mode. Should only
831+
/// be called if the feature is known to support this mode.
832+
bool isEnabledForAdoption() const;
833+
834+
operator Kind() const { return state; }
835+
};
836+
837+
private:
838+
class FeatureStateStorage {
839+
std::vector<FeatureState::Kind> states;
840+
841+
public:
842+
FeatureStateStorage();
843+
844+
/// Sets the given state for the given feature.
845+
void setState(Feature feature, FeatureState::Kind state);
846+
847+
/// Retrieves the state of the given feature.
848+
FeatureState getState(Feature feature) const;
849+
};
850+
851+
/// The states of language features.
852+
FeatureStateStorage featureStates;
853+
854+
public:
855+
/// Retrieve the state of the given feature.
856+
FeatureState getFeatureState(Feature feature) const;
857+
858+
/// Returns whether the given feature is enabled.
859+
bool hasFeature(Feature feature) const;
860+
861+
/// Returns whether a feature with the given name is enabled. Returns
862+
/// `false` if a feature by this name is not known.
863+
bool hasFeature(llvm::StringRef featureName) const;
864+
865+
/// Enables the given feature (enables in adoption mode if `forAdoption` is
866+
/// `true`).
867+
void enableFeature(Feature feature, bool forAdoption = false);
868+
869+
/// Disables the given feature.
870+
void disableFeature(Feature feature);
871+
872+
// =========================================================================
829873
};
830874

831875
class TypeCheckerOptions final {

lib/Basic/LangOptions.cpp

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "swift/Basic/LangOptions.h"
1919
#include "swift/AST/DiagnosticEngine.h"
20+
#include "swift/Basic/Assertions.h"
2021
#include "swift/Basic/Feature.h"
2122
#include "swift/Basic/FileTypes.h"
2223
#include "swift/Basic/Platform.h"
@@ -35,7 +36,7 @@ using namespace swift;
3536
LangOptions::LangOptions() {
3637
// Add all promoted language features
3738
#define LANGUAGE_FEATURE(FeatureName, IsAdoptable, SENumber, Description) \
38-
Features.insert(Feature::FeatureName);
39+
this->enableFeature(Feature::FeatureName);
3940
#define UPCOMING_FEATURE(FeatureName, SENumber, Version)
4041
#define EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd)
4142
#define OPTIONAL_LANGUAGE_FEATURE(FeatureName, SENumber, Description)
@@ -44,14 +45,16 @@ LangOptions::LangOptions() {
4445
// Special case: remove macro support if the compiler wasn't built with a
4546
// host Swift.
4647
#if !SWIFT_BUILD_SWIFT_SYNTAX
47-
Features.removeAll({Feature::Macros, Feature::FreestandingExpressionMacros,
48-
Feature::AttachedMacros, Feature::ExtensionMacros});
48+
this->disableFeature(Feature::Macros);
49+
this->disableFeature(Feature::FreestandingExpressionMacros);
50+
this->disableFeature(Feature::AttachedMacros);
51+
this->disableFeature(Feature::ExtensionMacros);
4952
#endif
5053

5154
// Note: Introduce default-on language options here.
52-
Features.insert(Feature::NoncopyableGenerics);
53-
Features.insert(Feature::BorrowingSwitch);
54-
Features.insert(Feature::MoveOnlyPartialConsumption);
55+
this->enableFeature(Feature::NoncopyableGenerics);
56+
this->enableFeature(Feature::BorrowingSwitch);
57+
this->enableFeature(Feature::MoveOnlyPartialConsumption);
5558

5659
// Enable any playground options that are enabled by default.
5760
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
@@ -291,8 +294,50 @@ bool LangOptions::isCustomConditionalCompilationFlagSet(StringRef Name) const {
291294
!= CustomConditionalCompilationFlags.end();
292295
}
293296

297+
bool LangOptions::FeatureState::isEnabled() const {
298+
return this->state != FeatureState::Off;
299+
}
300+
301+
bool LangOptions::FeatureState::isEnabledForAdoption() const {
302+
ASSERT(isFeatureAdoptable(this->feature) &&
303+
"You forgot to make the feature adoptable!");
304+
305+
return this->state == FeatureState::EnabledForAdoption;
306+
}
307+
308+
LangOptions::FeatureStateStorage::FeatureStateStorage()
309+
: states(numFeatures(), FeatureState::Off) {}
310+
311+
void LangOptions::FeatureStateStorage::setState(Feature feature,
312+
FeatureState::Kind state) {
313+
auto index = size_t(feature);
314+
315+
this->states[index] = state;
316+
}
317+
318+
LangOptions::FeatureState
319+
LangOptions::FeatureStateStorage::getState(Feature feature) const {
320+
auto index = size_t(feature);
321+
322+
return FeatureState(feature, this->states[index]);
323+
}
324+
325+
LangOptions::FeatureState LangOptions::getFeatureState(Feature feature) const {
326+
auto state = this->featureStates.getState(feature);
327+
if (state.isEnabled())
328+
return state;
329+
330+
if (auto version = getFeatureLanguageVersion(feature)) {
331+
if (this->isSwiftVersionAtLeast(*version)) {
332+
return FeatureState(feature, FeatureState::Enabled);
333+
}
334+
}
335+
336+
return state;
337+
}
338+
294339
bool LangOptions::hasFeature(Feature feature) const {
295-
if (Features.contains(feature))
340+
if (this->featureStates.getState(feature).isEnabled())
296341
return true;
297342

298343
if (auto version = getFeatureLanguageVersion(feature))
@@ -313,6 +358,19 @@ bool LangOptions::hasFeature(llvm::StringRef featureName) const {
313358
return false;
314359
}
315360

361+
void LangOptions::enableFeature(Feature feature, bool forAdoption) {
362+
if (forAdoption) {
363+
ASSERT(isFeatureAdoptable(feature));
364+
this->featureStates.setState(feature, FeatureState::EnabledForAdoption);
365+
} else {
366+
this->featureStates.setState(feature, FeatureState::Enabled);
367+
}
368+
}
369+
370+
void LangOptions::disableFeature(Feature feature) {
371+
this->featureStates.setState(feature, FeatureState::Off);
372+
}
373+
316374
void LangOptions::setHasAtomicBitWidth(llvm::Triple triple) {
317375
// We really want to use Clang's getMaxAtomicInlineWidth(), but that requires
318376
// a Clang::TargetInfo and we're setting up lang opts very early in the

lib/Frontend/CompilerInvocation.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -847,9 +847,6 @@ static bool ParseEnabledFeatureArgs(LangOptions &Opts, ArgList &Args,
847847
option.getPrefixedName(), argValue);
848848
continue;
849849
}
850-
851-
// Adoption mode is not plumbed through; ignore it.
852-
continue;
853850
}
854851

855852
// Skip features that are already enabled or disabled.
@@ -858,7 +855,7 @@ static bool ParseEnabledFeatureArgs(LangOptions &Opts, ArgList &Args,
858855

859856
// Enable the feature if requested.
860857
if (isEnableFeatureFlag)
861-
Opts.enableFeature(*feature);
858+
Opts.enableFeature(*feature, /*forAdoption=*/featureMode.has_value());
862859
}
863860

864861
// Since pseudo-features don't have a boolean on/off state, process them in

lib/Sema/TypeCheckType.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "swift/AST/TypeResolutionStage.h"
4747
#include "swift/Basic/Assertions.h"
4848
#include "swift/Basic/EnumMap.h"
49+
#include "swift/Basic/FixedBitSet.h"
4950
#include "swift/Basic/SourceManager.h"
5051
#include "swift/Basic/Statistic.h"
5152
#include "swift/Basic/StringExtras.h"

lib/Sema/TypeCheckType.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
#include "swift/AST/Type.h"
2020
#include "swift/AST/TypeResolutionStage.h"
2121
#include "swift/AST/Types.h"
22-
#include "swift/Basic/LangOptions.h"
2322

2423
namespace swift {
2524

unittests/Frontend/FeatureParsingTest.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,22 @@ void swift::PrintTo(const StrictConcurrency &value, std::ostream *os) {
4141
break;
4242
}
4343
}
44+
45+
void swift::PrintTo(const LangOptions::FeatureState &value, std::ostream *os) {
46+
PrintTo((const LangOptions::FeatureState::Kind &)value, os);
47+
}
48+
49+
void swift::PrintTo(const LangOptions::FeatureState::Kind &value,
50+
std::ostream *os) {
51+
switch (value) {
52+
case LangOptions::FeatureState::Off:
53+
*os << "Off";
54+
break;
55+
case LangOptions::FeatureState::EnabledForAdoption:
56+
*os << "EnabledForAdoption";
57+
break;
58+
case LangOptions::FeatureState::Enabled:
59+
*os << "Enabled";
60+
break;
61+
}
62+
}

unittests/Frontend/FeatureParsingTest.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ struct FeatureWrapper final {
3838
// the type they take.
3939
namespace swift {
4040
void PrintTo(const StrictConcurrency &, std::ostream *);
41+
void PrintTo(const LangOptions::FeatureState &, std::ostream *);
42+
void PrintTo(const LangOptions::FeatureState::Kind &, std::ostream *);
4143
} // end namespace swift
4244

4345
#endif // FEATURE_PARSING_TEST_H

0 commit comments

Comments
 (0)