Skip to content

Commit

Permalink
Make Feature a struct enum so we can put methods on it.
Browse files Browse the repository at this point in the history
Just noticed this as I was looking at making other changes.
  • Loading branch information
gottesmm committed Mar 11, 2025
1 parent 4c60e03 commit 7f32376
Show file tree
Hide file tree
Showing 15 changed files with 111 additions and 91 deletions.
90 changes: 55 additions & 35 deletions include/swift/Basic/Feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_BASIC_FEATURES_H
#define SWIFT_BASIC_FEATURES_H
#ifndef SWIFT_BASIC_FEATURE_H
#define SWIFT_BASIC_FEATURE_H

#include "swift/Basic/LLVM.h"

#include "llvm/ADT/StringRef.h"
#include <optional>
Expand All @@ -21,53 +23,71 @@ namespace swift {
class LangOptions;

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

InnerKind kind;

constexpr unsigned numFeatures() {
enum Features {
constexpr Feature(InnerKind kind) : kind(kind) {}
constexpr Feature(unsigned inputKind) : kind(InnerKind(inputKind)) {}

constexpr operator InnerKind() const { return kind; }
constexpr explicit operator unsigned() const { return unsigned(kind); }
constexpr explicit operator size_t() const { return size_t(kind); }

static constexpr unsigned getNumFeatures() {
enum Features {
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) FeatureName,
#include "swift/Basic/Features.def"
NumFeatures
};
return NumFeatures;
}
NumFeatures
};
return NumFeatures;
}

/// Check whether the given feature is available in production compilers.
bool isFeatureAvailableInProduction(Feature feature);
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
static const Feature FeatureName;
#include "swift/Basic/Features.def"

/// Determine the in-source name of the given feature.
llvm::StringRef getFeatureName(Feature feature);
/// Check whether the given feature is available in production compilers.
bool isAvailableInProduction() const;

/// Determine whether the first feature is more recent (and thus implies
/// the existence of) the second feature. Only meaningful for suppressible
/// features.
inline bool featureImpliesFeature(Feature feature, Feature implied) {
// Suppressible features are expected to be listed in order of
// addition in Features.def.
return (unsigned) feature < (unsigned) implied;
}
/// Determine the in-source name of the given feature.
llvm::StringRef getName() const;

/// Get the feature corresponding to this "future" feature, if there is one.
std::optional<Feature> getUpcomingFeature(llvm::StringRef name);
/// Determine whether the given feature supports adoption mode.
bool isAdoptable() const;

/// Get the feature corresponding to this "experimental" feature, if there is
/// one.
std::optional<Feature> getExperimentalFeature(llvm::StringRef name);
/// Determine whether this feature should be included in the
/// module interface
bool includeInModuleInterface() const;

/// Get the major language version in which this feature was introduced, or
/// \c None if it does not have such a version.
std::optional<unsigned> getFeatureLanguageVersion(Feature feature);
/// Determine whether the first feature is more recent (and thus implies
/// the existence of) the second feature. Only meaningful for suppressible
/// features.
constexpr bool featureImpliesFeature(Feature implied) const {
// Suppressible features are expected to be listed in order of
// addition in Features.def.
return (unsigned)kind < (unsigned)implied.kind;
}

/// Determine whether the given feature supports adoption mode.
bool isFeatureAdoptable(Feature feature);
/// Get the feature corresponding to this "future" feature, if there is one.
static std::optional<Feature> getUpcomingFeature(StringRef name);

/// Determine whether this feature should be included in the
/// module interface
bool includeInModuleInterface(Feature feature);
/// Get the feature corresponding to this "experimental" feature, if there is
/// one.
static std::optional<Feature> getExperimentalFeature(StringRef name);

/// Get the major language version in which this feature was introduced, or
/// \c None if it does not have such a version.
std::optional<unsigned> getLanguageVersion() const;
};

#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
constexpr Feature Feature::FeatureName = Feature::InnerKind::FeatureName;
#include "swift/Basic/Features.def"
}

#endif // SWIFT_BASIC_FEATURES_H
3 changes: 2 additions & 1 deletion include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -809,8 +809,9 @@ namespace swift {
llvm::SmallVector<std::string, 2> CustomConditionalCompilationFlags;

public:
//==========================================================================
// MARK: Features
// =========================================================================
//==========================================================================

/// A wrapper around the feature state enumeration.
struct FeatureState {
Expand Down
2 changes: 1 addition & 1 deletion include/swift/SILOptimizer/OptimizerBridgingImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ bool BridgedPassContext::enableStackProtection() const {

bool BridgedPassContext::hasFeature(BridgedFeature feature) const {
swift::SILModule *mod = invocation->getPassManager()->getModule();
return mod->getASTContext().LangOpts.hasFeature((swift::Feature)feature);
return mod->getASTContext().LangOpts.hasFeature(swift::Feature(feature));
}

bool BridgedPassContext::enableMoveInoutStackProtection() const {
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3297,7 +3297,7 @@ static void printCompatibilityCheckIf(ASTPrinter &printer, bool isElseIf,
} else {
first = false;
}
printer << "$" << getFeatureName(feature);
printer << "$" << Feature(feature).getName();
}

#ifndef NDEBUG
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/FeatureSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ void FeatureSet::collectRequiredFeature(Feature feature,

void FeatureSet::collectSuppressibleFeature(Feature feature,
InsertOrRemove operation) {
suppressible.insertOrRemove(numFeatures() - size_t(feature),
suppressible.insertOrRemove(Feature::getNumFeatures() - size_t(feature),
operation == Insert);
}

Expand Down
7 changes: 4 additions & 3 deletions lib/AST/FeatureSet.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

namespace swift {

using BasicFeatureSet = FixedBitSet<numFeatures(), Feature>;
using BasicFeatureSet =
FixedBitSet<Feature::getNumFeatures(), Feature::InnerKind>;

class FeatureSet {
BasicFeatureSet required;
Expand All @@ -30,7 +31,7 @@ class FeatureSet {
// This is the easiest way of letting us iterate from largest to
// smallest, i.e. from the newest to the oldest feature, which is
// the order in which we need to emit #if clauses.
using SuppressibleFeatureSet = FixedBitSet<numFeatures(), size_t>;
using SuppressibleFeatureSet = FixedBitSet<Feature::getNumFeatures(), size_t>;
SuppressibleFeatureSet suppressible;

public:
Expand All @@ -42,7 +43,7 @@ class FeatureSet {

public:
bool empty() const { return i == e; }
Feature next() { return Feature(numFeatures() - *i++); }
Feature next() { return Feature(Feature::getNumFeatures() - *i++); }
};

bool empty() const { return required.empty() && suppressible.empty(); }
Expand Down
24 changes: 12 additions & 12 deletions lib/Basic/Feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

using namespace swift;

bool swift::isFeatureAvailableInProduction(Feature feature) {
switch (feature) {
bool Feature::isAvailableInProduction() const {
switch (kind) {
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
case Feature::FeatureName: \
return true;
Expand All @@ -29,8 +29,8 @@ bool swift::isFeatureAvailableInProduction(Feature feature) {
llvm_unreachable("covered switch");
}

llvm::StringRef swift::getFeatureName(Feature feature) {
switch (feature) {
llvm::StringRef Feature::getName() const {
switch (kind) {
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
case Feature::FeatureName: \
return #FeatureName;
Expand All @@ -39,7 +39,7 @@ llvm::StringRef swift::getFeatureName(Feature feature) {
llvm_unreachable("covered switch");
}

std::optional<Feature> swift::getUpcomingFeature(llvm::StringRef name) {
std::optional<Feature> Feature::getUpcomingFeature(llvm::StringRef name) {
return llvm::StringSwitch<std::optional<Feature>>(name)
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description)
#define UPCOMING_FEATURE(FeatureName, SENumber, Version) \
Expand All @@ -48,7 +48,7 @@ std::optional<Feature> swift::getUpcomingFeature(llvm::StringRef name) {
.Default(std::nullopt);
}

std::optional<Feature> swift::getExperimentalFeature(llvm::StringRef name) {
std::optional<Feature> Feature::getExperimentalFeature(llvm::StringRef name) {
return llvm::StringSwitch<std::optional<Feature>>(name)
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description)
#define EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd) \
Expand All @@ -57,8 +57,8 @@ std::optional<Feature> swift::getExperimentalFeature(llvm::StringRef name) {
.Default(std::nullopt);
}

std::optional<unsigned> swift::getFeatureLanguageVersion(Feature feature) {
switch (feature) {
std::optional<unsigned> Feature::getLanguageVersion() const {
switch (kind) {
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description)
#define UPCOMING_FEATURE(FeatureName, SENumber, Version) \
case Feature::FeatureName: \
Expand All @@ -69,8 +69,8 @@ std::optional<unsigned> swift::getFeatureLanguageVersion(Feature feature) {
}
}

bool swift::isFeatureAdoptable(Feature feature) {
switch (feature) {
bool Feature::isAdoptable() const {
switch (kind) {
#define ADOPTABLE_UPCOMING_FEATURE(FeatureName, SENumber, Version)
#define ADOPTABLE_EXPERIMENTAL_FEATURE(FeatureName, AvailableInProd)
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
Expand All @@ -87,8 +87,8 @@ bool swift::isFeatureAdoptable(Feature feature) {
}
}

bool swift::includeInModuleInterface(Feature feature) {
switch (feature) {
bool Feature::includeInModuleInterface() const {
switch (kind) {
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description) \
case Feature::FeatureName: \
return true;
Expand Down
27 changes: 13 additions & 14 deletions lib/Basic/LangOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,18 +295,17 @@ bool LangOptions::isCustomConditionalCompilationFlagSet(StringRef Name) const {
}

bool LangOptions::FeatureState::isEnabled() const {
return this->state == FeatureState::Kind::Enabled;
return state == FeatureState::Kind::Enabled;
}

bool LangOptions::FeatureState::isEnabledForAdoption() const {
ASSERT(isFeatureAdoptable(this->feature) &&
"You forgot to make the feature adoptable!");
ASSERT(feature.isAdoptable() && "You forgot to make the feature adoptable!");

return this->state == FeatureState::Kind::EnabledForAdoption;
return state == FeatureState::Kind::EnabledForAdoption;
}

LangOptions::FeatureStateStorage::FeatureStateStorage()
: states(numFeatures(), FeatureState::Kind::Off) {}
: states(Feature::getNumFeatures(), FeatureState::Kind::Off) {}

void LangOptions::FeatureStateStorage::setState(Feature feature,
FeatureState::Kind state) {
Expand All @@ -323,12 +322,12 @@ LangOptions::FeatureStateStorage::getState(Feature feature) const {
}

LangOptions::FeatureState LangOptions::getFeatureState(Feature feature) const {
auto state = this->featureStates.getState(feature);
auto state = featureStates.getState(feature);
if (state.isEnabled())
return state;

if (auto version = getFeatureLanguageVersion(feature)) {
if (this->isSwiftVersionAtLeast(*version)) {
if (auto version = feature.getLanguageVersion()) {
if (isSwiftVersionAtLeast(*version)) {
return FeatureState(feature, FeatureState::Kind::Enabled);
}
}
Expand All @@ -340,7 +339,7 @@ bool LangOptions::hasFeature(Feature feature) const {
if (this->featureStates.getState(feature).isEnabled())
return true;

if (auto version = getFeatureLanguageVersion(feature))
if (auto version = feature.getLanguageVersion())
return isSwiftVersionAtLeast(*version);

return false;
Expand All @@ -360,12 +359,12 @@ bool LangOptions::hasFeature(llvm::StringRef featureName) const {

void LangOptions::enableFeature(Feature feature, bool forAdoption) {
if (forAdoption) {
ASSERT(isFeatureAdoptable(feature));
this->featureStates.setState(feature,
FeatureState::Kind::EnabledForAdoption);
} else {
this->featureStates.setState(feature, FeatureState::Kind::Enabled);
ASSERT(feature.isAdoptable());
featureStates.setState(feature, FeatureState::Kind::EnabledForAdoption);
return;
}

featureStates.setState(feature, FeatureState::Kind::Enabled);
}

void LangOptions::disableFeature(Feature feature) {
Expand Down
6 changes: 3 additions & 3 deletions lib/DriverTool/sil_opt_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -749,14 +749,14 @@ int sil_opt_main(ArrayRef<const char *> argv, void *MainAddr) {
}

for (auto &featureName : options.UpcomingFeatures) {
auto feature = getUpcomingFeature(featureName);
auto feature = Feature::getUpcomingFeature(featureName);
if (!feature) {
llvm::errs() << "error: unknown upcoming feature "
<< QuotedString(featureName) << "\n";
exit(-1);
}

if (auto firstVersion = getFeatureLanguageVersion(*feature)) {
if (auto firstVersion = feature->getLanguageVersion()) {
if (Invocation.getLangOptions().isSwiftVersionAtLeast(*firstVersion)) {
llvm::errs() << "error: upcoming feature " << QuotedString(featureName)
<< " is already enabled as of Swift version "
Expand All @@ -768,7 +768,7 @@ int sil_opt_main(ArrayRef<const char *> argv, void *MainAddr) {
}

for (auto &featureName : options.ExperimentalFeatures) {
if (auto feature = getExperimentalFeature(featureName)) {
if (auto feature = Feature::getExperimentalFeature(featureName)) {
Invocation.getLangOptions().enableFeature(*feature);
} else {
llvm::errs() << "error: unknown experimental feature "
Expand Down
Loading

0 comments on commit 7f32376

Please sign in to comment.