mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Merge remote-tracking branch 'origin/master' into swift-3-api-guidelines
This commit is contained in:
298
include/swift/Basic/ImmutablePointerSet.h
Normal file
298
include/swift/Basic/ImmutablePointerSet.h
Normal file
@@ -0,0 +1,298 @@
|
||||
//===--- ImmutablePointerSet.h ----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See http://swift.org/LICENSE.txt for license information
|
||||
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
///
|
||||
/// This file contains an implementation of a bump ptr allocated immutable
|
||||
/// pointer set.
|
||||
///
|
||||
/// The target of this data structure are sets of pointers (with N < 100) that
|
||||
/// are propagated through many basic blocks. These pointer sets will be merged
|
||||
/// and copied far more than being created from an array.
|
||||
///
|
||||
/// Thus we assume the following constraints:
|
||||
///
|
||||
/// 1. Our set operations are purely additive. Given a set, one can only add
|
||||
/// elements to it. One can not remove elements to it. This means we only
|
||||
/// support construction of sets from arrays and concatenation of pointer sets.
|
||||
///
|
||||
/// 2. Our sets must always be ordered and be able to be iterated over
|
||||
/// efficiently in that order.
|
||||
///
|
||||
/// 3. An O(log(n)) set contains method.
|
||||
///
|
||||
/// Beyond these constraints, we would like for our data structure to have the
|
||||
/// following properties for performance reasons:
|
||||
///
|
||||
/// 1. Its memory should be bump ptr allocated. We like fast allocation.
|
||||
///
|
||||
/// 2. No destructors need to be called when the bump ptr allocator is being
|
||||
/// destroyed. We like fast destruction and do not want to have to iterate over
|
||||
/// potentially many of these sets and invoke destructors.
|
||||
///
|
||||
/// Thus our design is to represent our sets as bump ptr allocated arrays whose
|
||||
/// elements are sorted and uniqued. The actual uniquing of the arrays
|
||||
/// themselves is performed via folding set node.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SWIFT_BASIC_IMMUTABLEPOINTERSET_H
|
||||
#define SWIFT_BASIC_IMMUTABLEPOINTERSET_H
|
||||
|
||||
#include "swift/Basic/STLExtras.h"
|
||||
#include "swift/Basic/NullablePtr.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/ADT/FoldingSet.h"
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
|
||||
namespace swift {
|
||||
|
||||
template <typename PtrTy> class ImmutablePointerSetFactory;
|
||||
|
||||
/// An immutable set of pointers. It is backed by a tail allocated sorted array
|
||||
/// ref.
|
||||
template <typename T> class ImmutablePointerSet : public llvm::FoldingSetNode {
|
||||
using PtrTy = typename std::add_pointer<T>::type;
|
||||
friend class ImmutablePointerSetFactory<T>;
|
||||
|
||||
NullablePtr<ImmutablePointerSetFactory<T>> ParentFactory;
|
||||
ArrayRef<PtrTy> Data;
|
||||
|
||||
ImmutablePointerSet(ImmutablePointerSetFactory<T> *ParentFactory,
|
||||
ArrayRef<PtrTy> NewData)
|
||||
: ParentFactory(ParentFactory), Data(NewData) {}
|
||||
|
||||
public:
|
||||
~ImmutablePointerSet() = default;
|
||||
ImmutablePointerSet(const ImmutablePointerSet &) = default;
|
||||
ImmutablePointerSet(ImmutablePointerSet &&) = default;
|
||||
|
||||
ImmutablePointerSet &operator=(const ImmutablePointerSet &) = default;
|
||||
ImmutablePointerSet &operator=(ImmutablePointerSet &&) = default;
|
||||
|
||||
bool operator==(const ImmutablePointerSet<T> &P) const {
|
||||
// If this and P have different sizes, we can not be equivalent.
|
||||
if (size() != P.size())
|
||||
return false;
|
||||
|
||||
// Ok, we now know that both have the same size. If one is empty, the other
|
||||
// must be as well, implying equality.
|
||||
if (empty())
|
||||
return true;
|
||||
|
||||
// Ok, both sets are not empty and the same number of elements. Compare
|
||||
// element wise.
|
||||
return std::equal(begin(), end(), P.begin());
|
||||
}
|
||||
|
||||
bool operator!=(const ImmutablePointerSet<T> &P) const {
|
||||
return !(*this == P);
|
||||
}
|
||||
|
||||
unsigned count(PtrTy Ptr) const {
|
||||
// This returns the first element >= Ptr. Since we know that our array is
|
||||
// sorted and uniqued, Ptr must be that element.
|
||||
auto LowerBound = std::lower_bound(begin(), end(), Ptr);
|
||||
|
||||
// If Ptr is > than everything in the array, then we obviously have 0.
|
||||
if (LowerBound == end())
|
||||
return 0;
|
||||
|
||||
// Then check if Ptr is > or Ptr is ==. We only have Ptr if we have == to.
|
||||
return *LowerBound == Ptr;
|
||||
}
|
||||
|
||||
using iterator = typename decltype(Data)::iterator;
|
||||
iterator begin() const { return Data.begin(); }
|
||||
iterator end() const { return Data.end(); }
|
||||
|
||||
unsigned size() const { return Data.size(); }
|
||||
bool empty() const { return Data.empty(); }
|
||||
|
||||
void Profile(llvm::FoldingSetNodeID &ID) const {
|
||||
assert(!Data.empty() && "Should not profile empty ImmutablePointerSet");
|
||||
for (PtrTy P : Data) {
|
||||
ID.AddPointer(P);
|
||||
}
|
||||
}
|
||||
|
||||
ImmutablePointerSet<T> *merge(ImmutablePointerSet<T> *Other) {
|
||||
if (empty())
|
||||
return Other;
|
||||
if (Other->empty())
|
||||
return this;
|
||||
assert(Other->ParentFactory.get() == ParentFactory.get());
|
||||
return ParentFactory.get()->merge(this, Other);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> class ImmutablePointerSetFactory {
|
||||
using PtrTy = typename std::add_pointer<T>::type;
|
||||
|
||||
using PtrSet = ImmutablePointerSet<T>;
|
||||
static constexpr unsigned AllocAlignment =
|
||||
(alignof(PtrSet) > alignof(PtrTy)) ? alignof(PtrSet) : alignof(PtrTy);
|
||||
|
||||
llvm::BumpPtrAllocator &Allocator;
|
||||
llvm::FoldingSetVector<PtrSet> Set;
|
||||
static PtrSet EmptyPtrSet;
|
||||
|
||||
public:
|
||||
ImmutablePointerSetFactory(llvm::BumpPtrAllocator &A) : Allocator(A), Set() {}
|
||||
ImmutablePointerSetFactory(const ImmutablePointerSetFactory &) = delete;
|
||||
ImmutablePointerSetFactory(ImmutablePointerSetFactory &&) = delete;
|
||||
ImmutablePointerSetFactory &
|
||||
operator=(const ImmutablePointerSetFactory &) = delete;
|
||||
ImmutablePointerSetFactory &operator=(ImmutablePointerSetFactory &&) = delete;
|
||||
|
||||
// We use a sentinel value here so that we can create an empty value
|
||||
// statically.
|
||||
static PtrSet *getEmptySet() { return &EmptyPtrSet; }
|
||||
|
||||
/// Given a sorted and uniqued list \p Array, return the ImmutablePointerSet
|
||||
/// containing Array. Asserts if \p Array is not sorted and uniqued.
|
||||
PtrSet *get(ArrayRef<PtrTy> Array) {
|
||||
if (Array.empty())
|
||||
return ImmutablePointerSetFactory::getEmptySet();
|
||||
|
||||
// We expect our users to sort/unique the input array. This is because doing
|
||||
// it here would either require us to allocate more memory than we need or
|
||||
// write into the input Array, which we don't want.
|
||||
assert(is_sorted_and_uniqued(Array));
|
||||
|
||||
llvm::FoldingSetNodeID ID;
|
||||
for (PtrTy Ptr : Array) {
|
||||
ID.AddPointer(Ptr);
|
||||
}
|
||||
|
||||
void *InsertPt;
|
||||
if (auto *PSet = Set.FindNodeOrInsertPos(ID, InsertPt)) {
|
||||
return PSet;
|
||||
}
|
||||
|
||||
size_t NumElts = Array.size();
|
||||
size_t MemSize = sizeof(PtrSet) + sizeof(PtrTy) * NumElts;
|
||||
|
||||
// Allocate the memory.
|
||||
auto *Mem =
|
||||
reinterpret_cast<PtrSet *>(Allocator.Allocate(MemSize, AllocAlignment));
|
||||
|
||||
// Copy in the pointers into the tail allocated memory. We do not need to do
|
||||
// any sorting/uniquing ourselves since we assume that our users perform
|
||||
// this task for us.
|
||||
MutableArrayRef<PtrTy> DataMem(reinterpret_cast<PtrTy *>(&Mem[1]), NumElts);
|
||||
std::copy(Array.begin(), Array.end(), DataMem.begin());
|
||||
|
||||
// Allocate the new node and insert it into the Set.
|
||||
auto *NewNode = new (Mem) PtrSet(this, DataMem);
|
||||
Set.InsertNode(NewNode, InsertPt);
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
PtrSet *merge(PtrSet *S1, ArrayRef<PtrTy> S2) {
|
||||
if (S1->empty())
|
||||
return get(S2);
|
||||
|
||||
if (S2.empty())
|
||||
return S1;
|
||||
|
||||
// We assume that S2 is sorted and uniqued.
|
||||
assert(is_sorted_and_uniqued(S2));
|
||||
|
||||
llvm::FoldingSetNodeID ID;
|
||||
|
||||
// We know that both of our pointer sets are sorted, so we can essentially
|
||||
// perform a sorted set merging algorithm to create the ID. We also count
|
||||
// the number of unique elements for allocation purposes.
|
||||
unsigned NumElts = 0;
|
||||
set_union_for_each(*S1, S2, [&ID, &NumElts](const PtrTy Ptr) -> void {
|
||||
ID.AddPointer(Ptr);
|
||||
NumElts++;
|
||||
});
|
||||
|
||||
// If we find our ID then continue.
|
||||
void *InsertPt;
|
||||
if (auto *PSet = Set.FindNodeOrInsertPos(ID, InsertPt)) {
|
||||
return PSet;
|
||||
}
|
||||
|
||||
unsigned MemSize = sizeof(PtrSet) + sizeof(PtrTy) * NumElts;
|
||||
|
||||
// Allocate the memory.
|
||||
auto *Mem =
|
||||
reinterpret_cast<PtrSet *>(Allocator.Allocate(MemSize, AllocAlignment));
|
||||
|
||||
// Copy in the union of the two pointer sets into the tail allocated
|
||||
// memory. Since we know that our sorted arrays are uniqued, we can use
|
||||
// set_union to get the uniqued sorted array that we want.
|
||||
MutableArrayRef<PtrTy> DataMem(reinterpret_cast<PtrTy *>(&Mem[1]), NumElts);
|
||||
std::set_union(S1->begin(), S1->end(), S2.begin(), S2.end(),
|
||||
DataMem.begin());
|
||||
|
||||
// Allocate the new node, insert it into the Set, and return it.
|
||||
auto *NewNode = new (Mem) PtrSet(this, DataMem);
|
||||
Set.InsertNode(NewNode, InsertPt);
|
||||
return NewNode;
|
||||
}
|
||||
|
||||
PtrSet *merge(PtrSet *S1, PtrSet *S2) {
|
||||
// If either S1 or S2 are the empty PtrSet, just return S2 or S1.
|
||||
if (S1->empty())
|
||||
return S2;
|
||||
if (S2->empty())
|
||||
return S1;
|
||||
|
||||
llvm::FoldingSetNodeID ID;
|
||||
|
||||
// We know that both of our pointer sets are sorted, so we can essentially
|
||||
// perform a sorted set merging algorithm to create the ID. We also count
|
||||
// the number of unique elements for allocation purposes.
|
||||
unsigned NumElts = 0;
|
||||
set_union_for_each(*S1, *S2, [&ID, &NumElts](const PtrTy Ptr) -> void {
|
||||
ID.AddPointer(Ptr);
|
||||
NumElts++;
|
||||
});
|
||||
|
||||
// If we find our ID then continue.
|
||||
void *InsertPt;
|
||||
if (auto *PSet = Set.FindNodeOrInsertPos(ID, InsertPt)) {
|
||||
return PSet;
|
||||
}
|
||||
|
||||
unsigned MemSize = sizeof(PtrSet) + sizeof(PtrTy) * NumElts;
|
||||
|
||||
// Allocate the memory.
|
||||
auto *Mem =
|
||||
reinterpret_cast<PtrSet *>(Allocator.Allocate(MemSize, AllocAlignment));
|
||||
|
||||
// Copy in the union of the two pointer sets into the tail allocated
|
||||
// memory. Since we know that our sorted arrays are uniqued, we can use
|
||||
// set_union to get the uniqued sorted array that we want.
|
||||
MutableArrayRef<PtrTy> DataMem(reinterpret_cast<PtrTy *>(&Mem[1]), NumElts);
|
||||
std::set_union(S1->begin(), S1->end(), S2->begin(), S2->end(),
|
||||
DataMem.begin());
|
||||
|
||||
// Allocate the new node, insert it into the Set, and return it.
|
||||
auto *NewNode = new (Mem) PtrSet(this, DataMem);
|
||||
Set.InsertNode(NewNode, InsertPt);
|
||||
return NewNode;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
ImmutablePointerSet<T> ImmutablePointerSetFactory<T>::EmptyPtrSet =
|
||||
ImmutablePointerSet<T>(nullptr, {});
|
||||
|
||||
} // end swift namespace
|
||||
|
||||
#endif
|
||||
@@ -158,7 +158,7 @@ namespace swift {
|
||||
/// Enable the Swift 3 migration via Fix-Its.
|
||||
bool Swift3Migration = false;
|
||||
|
||||
/// Sets the target we are building for and updates configuration options
|
||||
/// Sets the target we are building for and updates platform conditions
|
||||
/// to match.
|
||||
///
|
||||
/// \returns A pair - the first element is true if the OS was invalid.
|
||||
@@ -187,63 +187,58 @@ namespace swift {
|
||||
return clang::VersionTuple(major, minor, revision);
|
||||
}
|
||||
|
||||
/// Implicit target configuration options. There are currently three
|
||||
/// supported target configuration values:
|
||||
/// os - The active os target (OSX or IOS)
|
||||
/// arch - The active arch target (X64, I386, ARM, ARM64)
|
||||
/// _runtime - Runtime support (_ObjC or _Native)
|
||||
void addTargetConfigOption(StringRef Name, StringRef Value) {
|
||||
/// Sets an implicit platform condition.
|
||||
///
|
||||
/// There are currently three supported platform conditions:
|
||||
/// - os: The active os target (OSX or iOS)
|
||||
/// - arch: The active arch target (x86_64, i386, arm, arm64)
|
||||
/// - _runtime: Runtime support (_ObjC or _Native)
|
||||
void addPlatformConditionValue(StringRef Name, StringRef Value) {
|
||||
assert(!Name.empty() && !Value.empty());
|
||||
TargetConfigOptions.push_back(std::make_pair(Name, Value));
|
||||
PlatformConditionValues.emplace_back(Name, Value);
|
||||
}
|
||||
|
||||
/// Removes all configuration options added with addTargetConfigOption.
|
||||
void clearAllTargetConfigOptions() {
|
||||
TargetConfigOptions.clear();
|
||||
/// Removes all values added with addPlatformConditionValue.
|
||||
void clearAllPlatformConditionValues() {
|
||||
PlatformConditionValues.clear();
|
||||
}
|
||||
|
||||
/// Returns the value for the given target configuration or an empty string.
|
||||
StringRef getTargetConfigOption(StringRef Name) const;
|
||||
|
||||
/// Explicit build configuration options, initialized via the '-D'
|
||||
/// Returns the value for the given platform condition or an empty string.
|
||||
StringRef getPlatformConditionValue(StringRef Name) const;
|
||||
|
||||
/// Explicit conditional compilation flags, initialized via the '-D'
|
||||
/// compiler flag.
|
||||
void addBuildConfigOption(StringRef Name) {
|
||||
void addCustomConditionalCompilationFlag(StringRef Name) {
|
||||
assert(!Name.empty());
|
||||
BuildConfigOptions.push_back(Name);
|
||||
CustomConditionalCompilationFlags.push_back(Name);
|
||||
}
|
||||
|
||||
/// Determines if a given build configuration has been defined.
|
||||
bool hasBuildConfigOption(StringRef Name) const;
|
||||
/// Determines if a given conditional compilation flag has been set.
|
||||
bool isCustomConditionalCompilationFlagSet(StringRef Name) const;
|
||||
|
||||
ArrayRef<std::pair<std::string, std::string>>
|
||||
getTargetConfigOptions() const {
|
||||
return TargetConfigOptions;
|
||||
getPlatformConditionValues() const {
|
||||
return PlatformConditionValues;
|
||||
}
|
||||
|
||||
ArrayRef<std::string> getBuildConfigOptions() const {
|
||||
return BuildConfigOptions;
|
||||
ArrayRef<std::string> getCustomConditionalCompilationFlags() const {
|
||||
return CustomConditionalCompilationFlags;
|
||||
}
|
||||
|
||||
/// The constant list of supported os build configuration arguments.
|
||||
static const std::vector<std::string> SupportedOSBuildConfigArguments;
|
||||
|
||||
/// Returns true if the os build configuration argument represents
|
||||
/// Returns true if the 'os' platform condition argument represents
|
||||
/// a supported target operating system.
|
||||
static bool isOSBuildConfigSupported(StringRef OSName);
|
||||
static bool isPlatformConditionOSSupported(StringRef OSName);
|
||||
|
||||
/// The constant list of supported arch build configuration arguments.
|
||||
static const std::vector<std::string> SupportedArchBuildConfigArguments;
|
||||
|
||||
/// Returns true if the arch build configuration argument represents
|
||||
/// Returns true if the 'arch' platform condition argument represents
|
||||
/// a supported target architecture.
|
||||
static bool isArchBuildConfigSupported(StringRef ArchName);
|
||||
static bool isPlatformConditionArchSupported(StringRef ArchName);
|
||||
|
||||
private:
|
||||
llvm::SmallVector<std::pair<std::string, std::string>, 2>
|
||||
TargetConfigOptions;
|
||||
llvm::SmallVector<std::string, 2> BuildConfigOptions;
|
||||
llvm::SmallVector<std::pair<std::string, std::string>, 3>
|
||||
PlatformConditionValues;
|
||||
llvm::SmallVector<std::string, 2> CustomConditionalCompilationFlags;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LLVM_SWIFT_LANGOPTIONS_H
|
||||
#endif // SWIFT_LANGOPTIONS_H
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "swift/Basic/LLVM.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
@@ -101,6 +102,84 @@ inline void for_each3(const Container1 &c1, const Container2 &c2,
|
||||
for_each3(c1.begin(), c1.end(), c2.begin(), c3.begin(), f);
|
||||
}
|
||||
|
||||
/// The equivalent of std::for_each, but visits the set union of two sorted
|
||||
/// lists without allocating additional memory.
|
||||
///
|
||||
/// This has the following requirements:
|
||||
///
|
||||
/// 1. The ranges must be sorted.
|
||||
/// 2. The elements must have the same type.
|
||||
/// 3. There are no duplicate elements.
|
||||
/// 4. All elements must be comparable with std::less.
|
||||
template <typename InputIt1, typename InputIt2, typename BinaryFunction>
|
||||
inline void set_union_for_each(InputIt1 I1, InputIt1 E1, InputIt2 I2,
|
||||
InputIt2 E2, BinaryFunction f) {
|
||||
static_assert(
|
||||
std::is_same<
|
||||
typename std::iterator_traits<InputIt1>::value_type,
|
||||
typename std::iterator_traits<InputIt2>::value_type
|
||||
>::value,
|
||||
"Expected both iterator types to have the same underlying value type");
|
||||
|
||||
using RefTy = typename std::iterator_traits<InputIt1>::reference;
|
||||
|
||||
while (true) {
|
||||
// If we have reached the end of either list, visit the rest of the other
|
||||
// list, We do not need to worry about duplicates since each array we know
|
||||
// is unique.
|
||||
if (I1 == E1) {
|
||||
std::for_each(I2, E2, f);
|
||||
return;
|
||||
}
|
||||
|
||||
if (I2 == E2) {
|
||||
std::for_each(I1, E1, f);
|
||||
return;
|
||||
}
|
||||
|
||||
// If I1 < I2, then visit I1 and continue.
|
||||
if (std::less<RefTy>()(*I1, *I2)) {
|
||||
f(*I1);
|
||||
++I1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If I2 < I1, visit I2 and continue.
|
||||
if (std::less<RefTy>()(*I2, *I1)) {
|
||||
f(*I2);
|
||||
++I2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, we know that I1 and I2 equal. We know that we can only have
|
||||
// one of each element in each list, so we can just visit I1 and continue.
|
||||
f(*I1);
|
||||
++I1;
|
||||
++I2;
|
||||
}
|
||||
}
|
||||
|
||||
/// A container adapter for set_union_for_each.
|
||||
///
|
||||
/// To see the requirements upon the containers, please see the iterator based
|
||||
/// set_union_for_each.
|
||||
template <typename Container1, typename Container2, typename UnaryFunction>
|
||||
inline void set_union_for_each(const Container1 &C1, const Container2 &C2,
|
||||
UnaryFunction f) {
|
||||
// Make sure that our iterators have the same value type.
|
||||
static_assert(
|
||||
std::is_same<
|
||||
typename std::iterator_traits<
|
||||
typename Container1::iterator
|
||||
>::value_type,
|
||||
typename std::iterator_traits<
|
||||
typename Container2::iterator
|
||||
>::value_type
|
||||
>::value,
|
||||
"Expected both containers to have the same iterator value type");
|
||||
set_union_for_each(C1.begin(), C1.end(), C2.begin(), C2.end(), f);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// A range of iterators.
|
||||
@@ -573,6 +652,45 @@ void sortUnique(
|
||||
C.erase(std::unique(C.begin(), C.end()), C.end());
|
||||
}
|
||||
|
||||
/// Returns true if [II, IE) is a sorted and uniqued array. Returns false
|
||||
/// otherwise.
|
||||
template <typename IterTy>
|
||||
inline bool is_sorted_and_uniqued(IterTy II, IterTy IE) {
|
||||
using RefTy = typename std::iterator_traits<IterTy>::reference;
|
||||
|
||||
// The empty list is always sorted and uniqued.
|
||||
if (II == IE)
|
||||
return true;
|
||||
|
||||
// The list of one element is always sorted and uniqued.
|
||||
auto LastI = II;
|
||||
++II;
|
||||
if (II == IE)
|
||||
return true;
|
||||
|
||||
// Otherwise, until we reach the end of the list...
|
||||
while (II != IE) {
|
||||
// If LastI is greater than II then we know that our array is not sorted. If
|
||||
// LastI equals II, then we know that our array is not unique. If both of
|
||||
// those are conditions are false, then visit the next iterator element.
|
||||
if (std::greater_equal<RefTy>()(*LastI, *II)) {
|
||||
// Return false otherwise.
|
||||
return false;
|
||||
}
|
||||
|
||||
LastI = II;
|
||||
++II;
|
||||
}
|
||||
|
||||
// Success!
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
inline bool is_sorted_and_uniqued(const Container &C) {
|
||||
return is_sorted_and_uniqued(C.begin(), C.end());
|
||||
}
|
||||
|
||||
} // end namespace swift
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
// camelCase names.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SWIFT_BASIC_STRINGEXTRAS_HPP
|
||||
#define SWIFT_BASIC_STRINGEXTRAS_HPP
|
||||
|
||||
#ifndef SWIFT_BASIC_STRINGEXTRAS_H
|
||||
#define SWIFT_BASIC_STRINGEXTRAS_H
|
||||
|
||||
#include "swift/Basic/LLVM.h"
|
||||
#include "swift/Basic/OptionSet.h"
|
||||
|
||||
@@ -90,10 +90,10 @@ public:
|
||||
return Components.empty();
|
||||
}
|
||||
|
||||
/// Parse a _compiler_version build configuration from source code.
|
||||
/// Parse a version in the form used by the _compiler_version \#if condition.
|
||||
static Version parseCompilerVersionString(llvm::StringRef VersionString,
|
||||
SourceLoc Loc,
|
||||
DiagnosticEngine *Diags);
|
||||
SourceLoc Loc,
|
||||
DiagnosticEngine *Diags);
|
||||
|
||||
/// Parse a generic version string of the format [0-9]+(.[0-9]+)*
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user