Skip to content

Fix and generalize the printing of suppressible features #41399

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 2 commits into from
Feb 17, 2022
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
12 changes: 8 additions & 4 deletions include/swift/AST/ASTPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -385,10 +385,14 @@ void getInheritedForPrinting(

StringRef getAccessorKindString(AccessorKind value);

bool printCompatibilityFeatureChecksPre(ASTPrinter &printer, Decl *decl);
void printCompatibilityFeatureChecksPost(
ASTPrinter &printer,
llvm::function_ref<void()> printElse = []() -> void {});
/// Call the given function nested appropriately within #if checks
/// for the compiler features that it uses. Note that printBody
/// may be called multiple times if the declaration uses suppressible
/// features.
bool printWithCompatibilityFeatureChecks(ASTPrinter &printer,
PrintOptions &options,
Decl *decl,
llvm::function_ref<void()> printBody);

} // namespace swift

Expand Down
22 changes: 22 additions & 0 deletions include/swift/Basic/Feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,31 @@ enum class Feature {
#include "swift/Basic/Features.def"
};

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

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

/// Determine the in-source name of the given feature.
llvm::StringRef getFeatureName(Feature feature);

/// 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;
}

}

#endif // SWIFT_BASIC_FEATURES_H
19 changes: 17 additions & 2 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,26 @@
// Option: either a reference to a language option in the form
// "langOpts.<option name>" or "true" to indicate that it's always
// enabled.
//
// Suppressible language features can be suppressed when printing
// an interface without having to suppress the entire declaration they're
// contained within. The declaration will be emitted multiple times,
// each with a subset of the suppressible features. To avoid combinatoric
// re-emission, we assume a linear history: later features in this file
// imply the existence of earlier features. (This only needs to apply to
// suppressible features.)
//
//===----------------------------------------------------------------------===//

#ifndef LANGUAGE_FEATURE
# error define LANGUAGE_FEATURE before including Features.def
#endif

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

LANGUAGE_FEATURE(StaticAssert, 0, "#assert", langOpts.EnableExperimentalStaticAssert)
LANGUAGE_FEATURE(AsyncAwait, 296, "async/await", true)
LANGUAGE_FEATURE(EffectfulProp, 310, "Effectful properties", true)
Expand All @@ -57,8 +71,9 @@ LANGUAGE_FEATURE(BuiltinCreateAsyncTaskInGroup, 0, "MainActor executor building
LANGUAGE_FEATURE(BuiltinMove, 0, "Builtin.move()", true)
LANGUAGE_FEATURE(BuiltinCopy, 0, "Builtin.copy()", true)
LANGUAGE_FEATURE(BuiltinStackAlloc, 0, "Builtin.stackAlloc", true)
LANGUAGE_FEATURE(SpecializeAttributeWithAvailability, 0, "@_specialize attribute with availability", true)
SUPPRESSIBLE_LANGUAGE_FEATURE(SpecializeAttributeWithAvailability, 0, "@_specialize attribute with availability", true)
LANGUAGE_FEATURE(BuiltinAssumeAlignment, 0, "Builtin.assumeAlignment", true)
LANGUAGE_FEATURE(UnsafeInheritExecutor, 0, "@_unsafeInheritExecutor", true)
SUPPRESSIBLE_LANGUAGE_FEATURE(UnsafeInheritExecutor, 0, "@_unsafeInheritExecutor", true)

#undef SUPPRESSIBLE_LANGUAGE_FEATURE
#undef LANGUAGE_FEATURE
257 changes: 257 additions & 0 deletions include/swift/Basic/FixedBitSet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
//===- llvm/ADT/FixedBitSet.h - Fixed-length bitset -------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the FixedBitSet template, which is basically
// just std::bitset (a fixed-size inline-allocated bit vector) but with:
//
// - a cleaner interface for using the type to implement a set,
// especially a set of an enum type
//
// - a more useful set of operations, such as the ability to iterate
// over the set instead of scanning over all possible elements
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_ADT_FIXEDBITSET_H
#define LLVM_ADT_FIXEDBITSET_H

#include <assert.h>
#include <inttypes.h>
#include <initializer_list>
#include "llvm/Support/MathExtras.h"

namespace swift {

namespace detail {
// In principle, long sets would be happier if we chunked
// at the pointer size instead of capping at 32, but we expect this
// to be used with relatively short Sets where larger chunks
// would introduce more padding.
template <size_t numElements,
bool fitsInUInt8 = (numElements <= 8),
bool fitsInUInt16 = (numElements <= 16)>
struct FixedBitSetStorageType;

template <size_t numElements>
struct FixedBitSetStorageType<numElements, true, true> {
using type = uint8_t;
};

template <size_t numElements>
struct FixedBitSetStorageType<numElements, false, true> {
using type = uint16_t;
};

template <size_t numElements>
struct FixedBitSetStorageType<numElements, false, false> {
using type = uint32_t;
};

} // end namespace detail

/// A set of integral elements, all of which must be less than
/// numElements. Iteration produces elements in a sorted
/// (numerically increasing) order.
template <size_t numElements, class ValueType = size_t>
class FixedBitSet {
static_assert(std::is_integral<ValueType>::value ||
std::is_enum<ValueType>::value,
"value type is not an integer or enum type");

using ChunkType = typename detail::FixedBitSetStorageType<numElements>::type;

static constexpr size_t chunkSize = CHAR_BIT * sizeof(ChunkType);
static constexpr size_t numChunks =
(numElements + chunkSize - 1) / chunkSize;

/// We represent the elements as an inline array of chunks, with
/// earlier chunks representing lower indices. Any padding bits
/// in the last chunk (if the number of elements isn't an even
/// multiple of the chunk size) are always clear.
ChunkType chunks[numChunks] = {};

static size_t chunkIndex(ValueType i) {
return size_t(i) / chunkSize;
}
static size_t chunkMask(ValueType i) {
return ChunkType(1) << (size_t(i) % chunkSize);
}

public:
/// Build an empty set.
FixedBitSet() {}

/// Build a set containing the given elements.
FixedBitSet(std::initializer_list<ValueType> elements) {
for (const auto &elt : elements)
insert(elt);
}

/// Return true if the set is empty.
bool empty() const {
for (auto chunk : chunks)
if (chunk != 0) return false;
return true;
}

/// Return whether the given element is present in the set.
bool contains(ValueType i) const {
assert(size_t(i) < numElements);
return chunks[chunkIndex(i)] & chunkMask(i);
}

/// Either insert or remove the given element.
void insertOrRemove(ValueType i, bool shouldInsert) {
if (shouldInsert)
insert(i);
else
remove(i);
}

/// Insert the given element.
void insert(ValueType i) {
assert(size_t(i) < numElements);
chunks[chunkIndex(i)] |= chunkMask(i);
}

/// Remove the given element from the set.
void remove(ValueType i) {
assert(size_t(i) < numElements);
chunks[chunkIndex(i)] &= ~chunkMask(i);
}

/// Add every element in the range to this set.
void insertAll() {
// Our invariant is that any padding bits are clear, so
// we need to set bits in the most significant chunk only
// for the bits that are set.
constexpr size_t partialBits = (numElements % chunkSize);
constexpr size_t firstIncompleteChunk =
partialBits == 0 ? numChunks : numChunks - 1;

for (size_t i = 0; i != firstIncompleteChunk; ++i)
chunks[i] = ~ChunkType(0);

if (partialBits != 0)
chunks[numChunks - 1] = (ChunkType(1) << partialBits) - 1;
}

/// Remove all of the elements in this set.
void removeAll() {
for (size_t i = 0; i != numChunks; ++i)
chunks[i] = 0;
}

/// Add all of the elements in the given set.
void insertAll(const FixedBitSet &other) {
for (size_t i = 0; i != numChunks; ++i) {
chunks[i] |= other.chunks[i];
}
}

/// Remove all of the elements that aren't in the given set.
void removeAllExcept(const FixedBitSet &other) {
for (size_t i = 0; i != numChunks; ++i) {
chunks[i] &= other.chunks[i];
}
}

/// Remove all of the elements that are also in the given set.
void removeAll(const FixedBitSet &other) {
for (size_t i = 0; i != numChunks; ++i) {
chunks[i] &= ~other.chunks[i];
}
}

class iterator {
const ChunkType *chunks;
size_t chunkIndex;

/// Our possibly-edited copy of the current chunk. As we iterate
/// past elements, we clear the corresponding bit here and then find
/// the next chunk that has a bit set. The invariant is that either
/// this is non-zero or chunkIndex == numChunks.
size_t remainingChunk;

friend class FixedBitSet;

// Constructor for begin().
iterator(const ChunkType *chunks, size_t chunkIndex,
size_t remainingChunk)
: chunks(chunks), chunkIndex(chunkIndex),
remainingChunk(remainingChunk) {
advance();
}

/// Constructor for end().
iterator(const ChunkType *chunks)
: chunks(chunks), chunkIndex(numChunks), remainingChunk(0) {}

/// Find the next element, if any, or else set chunkIndex to numChunks.
void advance() {
while (!remainingChunk) {
assert(chunkIndex < numChunks);
if (++chunkIndex == numChunks) break;
remainingChunk = chunks[chunkIndex];
}
}

public:
iterator &operator++() {
assert(remainingChunk && "incrementing a completed iterator");
// rc = aaaaaaaa100
// rc - 1 = aaaaaaaa011
remainingChunk &= (remainingChunk - 1);
advance();
return *this;
}
iterator operator++(int) {
iterator copy = *this;
++*this;
return copy;
}

ValueType operator*() const {
assert(remainingChunk && "dereferencing a completed iterator");
return ValueType(chunkIndex * chunkSize
+ llvm::findFirstSet(remainingChunk,
llvm::ZB_Undefined));
}

bool operator==(const iterator &other) const {
assert(chunks == other.chunks &&
"comparing iterators from different bit Sets");
return chunkIndex == other.chunkIndex
&& remainingChunk == other.remainingChunk;
}
bool operator!=(const iterator &other) const {
return !(*this == other);
}
};

iterator begin() const {
return iterator(chunks, 0, chunks[0]);
}
iterator end() const {
return iterator(chunks);
}

bool operator==(const FixedBitSet &other) const {
for (size_t i = 0; i != numChunks; ++i)
if (chunks[i] != other.chunks[i])
return false;
return true;
}
bool operator!=(const FixedBitSet &other) const {
return !(*this == other);
}
};

} // end namespace swift

#endif
Loading