Merge pull request #18820 from rjmccall/devirtualize-begin-apply
Update SIL devirtualization to handle begin_apply instructions
diff --git a/include/swift/Basic/LLVM.h b/include/swift/Basic/LLVM.h
index 7411d4b..1bd8a16 100644
--- a/include/swift/Basic/LLVM.h
+++ b/include/swift/Basic/LLVM.h
@@ -37,6 +37,7 @@
template <typename T> class SmallVectorImpl;
template <typename T, unsigned N> class SmallVector;
template <unsigned N> class SmallString;
+ template <typename T, unsigned N> class SmallSetVector;
template<typename T> class ArrayRef;
template<typename T> class MutableArrayRef;
template<typename T> class TinyPtrVector;
@@ -73,6 +74,7 @@
using llvm::MutableArrayRef;
using llvm::TinyPtrVector;
using llvm::PointerUnion;
+ using llvm::SmallSetVector;
// Other common classes.
using llvm::raw_ostream;
diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp
index 5a4d1db..59b5508 100644
--- a/lib/Sema/CSApply.cpp
+++ b/lib/Sema/CSApply.cpp
@@ -7764,6 +7764,8 @@
llvm::SmallDenseMap<Expr *, SmallVector<const ConstraintFix *, 4>>
fixesPerExpr;
+ applySolution(solution);
+
for (auto *fix : solution.Fixes)
fixesPerExpr[fix->getAnchor()].push_back(fix);
diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp
index 1c3c255..e829eb8 100644
--- a/lib/Sema/CSDiag.cpp
+++ b/lib/Sema/CSDiag.cpp
@@ -738,7 +738,7 @@
.fixItReplaceChars(startLoc, endLoc, scratch);
}
-static void diagnoseSubElementFailure(Expr *destExpr,
+void swift::diagnoseSubElementFailure(Expr *destExpr,
SourceLoc loc,
ConstraintSystem &CS,
Diag<StringRef> diagID,
@@ -883,7 +883,8 @@
}
}
- TC.diagnose(loc, unknownDiagID, CS.getType(destExpr))
+ auto type = destExpr->getType() ?: CS.simplifyType(CS.getType(destExpr));
+ TC.diagnose(loc, unknownDiagID, type)
.highlight(immInfo.first->getSourceRange());
}
@@ -6153,33 +6154,6 @@
if (!argExpr)
return true; // already diagnosed.
- // A common error is to apply an operator that only has inout forms (e.g. +=)
- // to non-lvalues (e.g. a local let). Produce a nice diagnostic for this
- // case.
- if (calleeInfo.closeness == CC_NonLValueInOut) {
- Diag<StringRef> subElementDiagID;
- Diag<Type> rvalueDiagID;
- Expr *diagExpr = nullptr;
-
- if (isa<PrefixUnaryExpr>(callExpr) || isa<PostfixUnaryExpr>(callExpr)) {
- subElementDiagID = diag::cannot_apply_lvalue_unop_to_subelement;
- rvalueDiagID = diag::cannot_apply_lvalue_unop_to_rvalue;
- diagExpr = argExpr;
- } else if (isa<BinaryExpr>(callExpr)) {
- subElementDiagID = diag::cannot_apply_lvalue_binop_to_subelement;
- rvalueDiagID = diag::cannot_apply_lvalue_binop_to_rvalue;
-
- if (auto argTuple = dyn_cast<TupleExpr>(argExpr))
- diagExpr = argTuple->getElement(0);
- }
-
- if (diagExpr) {
- diagnoseSubElementFailure(diagExpr, callExpr->getFn()->getLoc(), CS,
- subElementDiagID, rvalueDiagID);
- return true;
- }
- }
-
// Handle argument label mismatches when we have multiple candidates.
if (calleeInfo.closeness == CC_ArgumentLabelMismatch) {
auto args = decomposeArgType(CS.getType(argExpr), argLabels);
diff --git a/lib/Sema/CSDiag.h b/lib/Sema/CSDiag.h
index 82eb774..d278402 100644
--- a/lib/Sema/CSDiag.h
+++ b/lib/Sema/CSDiag.h
@@ -26,7 +26,12 @@
/// UnresolvedType.
Type replaceTypeParametersWithUnresolved(Type ty);
Type replaceTypeVariablesWithUnresolved(Type ty);
-
+
+ /// Diagnose lvalue expr error.
+ void diagnoseSubElementFailure(Expr *destExpr, SourceLoc loc,
+ constraints::ConstraintSystem &CS,
+ Diag<StringRef> diagID,
+ Diag<Type> unknownDiagID);
};
#endif /* SWIFT_SEMA_CSDIAG_H */
diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp
index 1b1d9cd..7031109 100644
--- a/lib/Sema/CSDiagnostics.cpp
+++ b/lib/Sema/CSDiagnostics.cpp
@@ -16,6 +16,7 @@
#include "CSDiagnostics.h"
#include "ConstraintSystem.h"
+#include "CSDiag.h"
#include "MiscDiagnostics.h"
#include "swift/AST/Expr.h"
#include "swift/AST/GenericSignature.h"
@@ -532,3 +533,44 @@
.fixItReplace({tryExpr->getTryLoc(), tryExpr->getQuestionLoc()}, "try!");
return true;
}
+
+bool RValueTreatedAsLValueFailure::diagnose() {
+ Diag<StringRef> subElementDiagID;
+ Diag<Type> rvalueDiagID;
+ Expr *diagExpr = getLocator()->getAnchor();
+ SourceLoc loc;
+
+ auto callExpr = dyn_cast<ApplyExpr>(diagExpr);
+ if (!callExpr)
+ return false; // currently only creating these for args, so should be
+ // unreachable
+
+ Expr *argExpr = callExpr->getArg();
+ loc = callExpr->getFn()->getLoc();
+
+ if (isa<PrefixUnaryExpr>(callExpr) || isa<PostfixUnaryExpr>(callExpr)) {
+ subElementDiagID = diag::cannot_apply_lvalue_unop_to_subelement;
+ rvalueDiagID = diag::cannot_apply_lvalue_unop_to_rvalue;
+ diagExpr = argExpr;
+ } else if (isa<BinaryExpr>(callExpr)) {
+ subElementDiagID = diag::cannot_apply_lvalue_binop_to_subelement;
+ rvalueDiagID = diag::cannot_apply_lvalue_binop_to_rvalue;
+ auto argTuple = dyn_cast<TupleExpr>(argExpr);
+ diagExpr = argTuple->getElement(0);
+ } else {
+ auto lastPathElement = getLocator()->getPath().back();
+ assert(lastPathElement.getKind() ==
+ ConstraintLocator::PathElementKind::ApplyArgToParam);
+
+ subElementDiagID = diag::cannot_pass_rvalue_inout_subelement;
+ rvalueDiagID = diag::cannot_pass_rvalue_inout;
+ if (auto argTuple = dyn_cast<TupleExpr>(argExpr))
+ diagExpr = argTuple->getElement(lastPathElement.getValue());
+ else if (auto parens = dyn_cast<ParenExpr>(argExpr))
+ diagExpr = parens->getSubExpr();
+ }
+
+ diagnoseSubElementFailure(diagExpr, loc, getConstraintSystem(),
+ subElementDiagID, rvalueDiagID);
+ return true;
+}
diff --git a/lib/Sema/CSDiagnostics.h b/lib/Sema/CSDiagnostics.h
index 934163c..6101e00 100644
--- a/lib/Sema/CSDiagnostics.h
+++ b/lib/Sema/CSDiagnostics.h
@@ -345,6 +345,17 @@
bool diagnose() override;
};
+/// Diagnose errors associated with rvalues in positions
+/// where an lvalue is required, such as inout arguments.
+class RValueTreatedAsLValueFailure final : public FailureDiagnostic {
+
+public:
+ RValueTreatedAsLValueFailure(const Solution &solution, ConstraintLocator *locator)
+ : FailureDiagnostic(nullptr, solution, locator) {}
+
+ bool diagnose() override;
+};
+
} // end namespace constraints
} // end namespace swift
diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp
index 84e5915..bb837ef 100644
--- a/lib/Sema/CSFix.cpp
+++ b/lib/Sema/CSFix.cpp
@@ -104,6 +104,17 @@
return new (cs.getAllocator()) AddAddressOf(locator);
}
+bool TreatRValueAsLValue::diagnose(Expr *root, const Solution &solution) const {
+ RValueTreatedAsLValueFailure failure(solution, getLocator());
+ return failure.diagnose();
+}
+
+TreatRValueAsLValue *TreatRValueAsLValue::create(ConstraintSystem &cs,
+ ConstraintLocator *locator) {
+ return new (cs.getAllocator()) TreatRValueAsLValue(locator);
+}
+
+
bool CoerceToCheckedCast::diagnose(Expr *root, const Solution &solution) const {
MissingForcedDowncastFailure failure(root, solution, getLocator());
return failure.diagnose();
diff --git a/lib/Sema/CSFix.h b/lib/Sema/CSFix.h
index 155396b..84ecc8f 100644
--- a/lib/Sema/CSFix.h
+++ b/lib/Sema/CSFix.h
@@ -68,6 +68,9 @@
/// Add a new conformance to the type to satisfy a requirement.
AddConformance,
+
+ /// Treat rvalue as lvalue
+ TreatRValueAsLValue,
};
class ConstraintFix {
@@ -168,6 +171,20 @@
static AddAddressOf *create(ConstraintSystem &cs, ConstraintLocator *locator);
};
+// Treat rvalue as if it was an lvalue
+class TreatRValueAsLValue final : public ConstraintFix {
+ TreatRValueAsLValue(ConstraintLocator *locator)
+ : ConstraintFix(FixKind::TreatRValueAsLValue, locator) {}
+
+public:
+ std::string getName() const override { return "treat rvalue as lvalue"; }
+
+ bool diagnose(Expr *root, const Solution &solution) const override;
+
+ static TreatRValueAsLValue *create(ConstraintSystem &cs, ConstraintLocator *locator);
+};
+
+
/// Replace a coercion ('as') with a forced checked cast ('as!').
class CoerceToCheckedCast final : public ConstraintFix {
CoerceToCheckedCast(ConstraintLocator *locator)
diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp
index bd80bc3..92fe7e9 100644
--- a/lib/Sema/CSSimplify.cpp
+++ b/lib/Sema/CSSimplify.cpp
@@ -2439,10 +2439,16 @@
ForceDowncast::create(*this, type2, getConstraintLocator(locator)));
}
- // If we're converting an lvalue to an inout type, add the missing '&'.
- if (type2->getRValueType()->is<InOutType>() && type1->is<LValueType>()) {
- conversionsOrFixes.push_back(
+ if (type2->getRValueType()->is<InOutType>()) {
+ if (type1->is<LValueType>()) {
+ // If we're converting an lvalue to an inout type, add the missing '&'.
+ conversionsOrFixes.push_back(
AddAddressOf::create(*this, getConstraintLocator(locator)));
+ } else if (!isTypeVarOrMember1) {
+ // If we have a concrete type that's an rvalue, "fix" it.
+ conversionsOrFixes.push_back(
+ TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
+ }
}
}
@@ -4980,6 +4986,16 @@
return result;
}
+ case FixKind::TreatRValueAsLValue: {
+ auto result = matchTypes(LValueType::get(type1), type2,
+ matchKind, subflags, locator);
+ if (result == SolutionKind::Solved)
+ if (recordFix(fix))
+ return SolutionKind::Error;
+
+ return result;
+ }
+
case FixKind::ExplicitlyEscaping:
case FixKind::CoerceToCheckedCast:
case FixKind::RelabelArguments:
diff --git a/lib/Sema/ConstraintLocator.h b/lib/Sema/ConstraintLocator.h
index bd0c193..52b0ff5 100644
--- a/lib/Sema/ConstraintLocator.h
+++ b/lib/Sema/ConstraintLocator.h
@@ -343,7 +343,7 @@
return PathElement(NamedTupleElement, position);
}
- /// Retrieve a patch element for an argument/parameter comparison in a
+ /// Retrieve a path element for an argument/parameter comparison in a
/// function application.
static PathElement getApplyArgToParam(unsigned argIdx, unsigned paramIdx) {
return PathElement(ApplyArgToParam, argIdx, paramIdx);
diff --git a/lib/Sema/TypeCheckProtocol.cpp b/lib/Sema/TypeCheckProtocol.cpp
index 1c575c2..2e52012 100644
--- a/lib/Sema/TypeCheckProtocol.cpp
+++ b/lib/Sema/TypeCheckProtocol.cpp
@@ -3486,6 +3486,10 @@
auto witness = Conformance->getWitness(requirement, nullptr).getDecl();
if (!witness) return;
+ // Make sure that we finalize the witness, so we can emit this
+ // witness table.
+ TC.DeclsToFinalize.insert(witness);
+
// Objective-C checking for @objc requirements.
if (requirement->isObjC() &&
requirement->getFullName() == witness->getFullName() &&
diff --git a/stdlib/public/core/RangeReplaceableCollection.swift b/stdlib/public/core/RangeReplaceableCollection.swift
index b2c22bf..e42a205 100644
--- a/stdlib/public/core/RangeReplaceableCollection.swift
+++ b/stdlib/public/core/RangeReplaceableCollection.swift
@@ -343,7 +343,16 @@
/// - Complexity: O(*n*), where *n* is the length of the collection.
mutating func removeAll(keepingCapacity keepCapacity: Bool /*= false*/)
- /// Removes from the collection all elements that satisfy the given predicate.
+ /// Removes all the elements that satisfy the given predicate.
+ ///
+ /// Use this method to remove every element in a collection that meets
+ /// particular criteria. The order of the remaining elements is preserved.
+ /// This example removes all the odd values from an
+ /// array of numbers:
+ ///
+ /// var numbers = [5, 6, 7, 8, 9, 10, 11]
+ /// numbers.removeAll(where: { $0 % 2 != 0 })
+ /// // numbers == [6, 8, 10]
///
/// - Parameter shouldBeRemoved: A closure that takes an element of the
/// sequence as its argument and returns a Boolean value indicating
@@ -1083,11 +1092,12 @@
/// Removes all the elements that satisfy the given predicate.
///
/// Use this method to remove every element in a collection that meets
- /// particular criteria. This example removes all the odd values from an
+ /// particular criteria. The order of the remaining elements is preserved.
+ /// This example removes all the odd values from an
/// array of numbers:
///
/// var numbers = [5, 6, 7, 8, 9, 10, 11]
- /// numbers.removeAll(where: { $0 % 2 == 1 })
+ /// numbers.removeAll(where: { $0 % 2 != 0 })
/// // numbers == [6, 8, 10]
///
/// - Parameter shouldBeRemoved: A closure that takes an element of the
@@ -1108,7 +1118,8 @@
/// Removes all the elements that satisfy the given predicate.
///
/// Use this method to remove every element in a collection that meets
- /// particular criteria. This example removes all the vowels from a string:
+ /// particular criteria. The order of the remaining elements is preserved.
+ /// This example removes all the vowels from a string:
///
/// var phrase = "The rain in Spain stays mainly in the plain."
///
diff --git a/stdlib/public/core/Sort.swift b/stdlib/public/core/Sort.swift
index 5d8ddfe..f3d3fe1 100644
--- a/stdlib/public/core/Sort.swift
+++ b/stdlib/public/core/Sort.swift
@@ -45,9 +45,7 @@
/// - Complexity: O(*n* log *n*), where *n* is the length of the sequence.
@inlinable
public func sorted() -> [Element] {
- var result = ContiguousArray(self)
- result.sort()
- return Array(result)
+ return sorted(by: <)
}
}
@@ -142,7 +140,6 @@
extension MutableCollection
where Self: RandomAccessCollection, Element: Comparable {
-
/// Sorts the collection in place.
///
/// You can sort any mutable collection of elements that conform to the
@@ -171,13 +168,7 @@
/// - Complexity: O(*n* log *n*), where *n* is the length of the collection.
@inlinable
public mutating func sort() {
- let didSortUnsafeBuffer = _withUnsafeMutableBufferPointerIfSupported {
- buffer -> Void? in
- buffer.sort()
- }
- if didSortUnsafeBuffer == nil {
- _introSort(&self, subRange: startIndex..<endIndex, by: <)
- }
+ sort(by: <)
}
}
@@ -261,333 +252,306 @@
try buffer.sort(by: areInIncreasingOrder)
}
if didSortUnsafeBuffer == nil {
- try _introSort(
- &self,
- subRange: startIndex..<endIndex,
- by: areInIncreasingOrder)
+ try _introSort(within: startIndex..<endIndex, by: areInIncreasingOrder)
}
}
}
-@inlinable
-internal func _insertionSort<C: MutableCollection & BidirectionalCollection>(
- _ elements: inout C,
- subRange range: Range<C.Index>,
- by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool
-) rethrows {
+extension MutableCollection where Self: BidirectionalCollection {
+ @inlinable
+ internal mutating func _insertionSort(
+ within range: Range<Index>,
+ by areInIncreasingOrder: (Element, Element) throws -> Bool
+ ) rethrows {
- if !range.isEmpty {
+ guard !range.isEmpty else { return }
+
let start = range.lowerBound
- // Keep track of the end of the initial sequence of sorted
- // elements.
- var sortedEnd = start
+ // Keep track of the end of the initial sequence of sorted elements. One
+ // element is trivially already-sorted, thus pre-increment Continue until
+ // the sorted elements cover the whole sequence
+ var sortedEnd = index(after: start)
- // One element is trivially already-sorted, thus pre-increment
- // Continue until the sorted elements cover the whole sequence
- elements.formIndex(after: &sortedEnd)
while sortedEnd != range.upperBound {
// get the first unsorted element
- let x: C.Element = elements[sortedEnd]
+ // FIXME: by stashing the element, instead of using indexing and swapAt,
+ // this method won't work for collections of move-only types.
+ let x = self[sortedEnd]
// Look backwards for x's position in the sorted sequence,
// moving elements forward to make room.
var i = sortedEnd
repeat {
- let predecessor: C.Element = elements[elements.index(before: i)]
+ let j = index(before: i)
+ let predecessor = self[j]
- // If clouser throws the error, We catch the error put the element at right
- // place and rethrow the error.
+ // If closure throws, put the element at right place and rethrow.
do {
// if x doesn't belong before y, we've found its position
- if !(try areInIncreasingOrder(x, predecessor)) {
+ if try !areInIncreasingOrder(x, predecessor) {
break
}
} catch {
- elements[i] = x
+ self[i] = x
throw error
}
// Move y forward
- elements[i] = predecessor
- elements.formIndex(before: &i)
+ self[i] = predecessor
+ i = j
} while i != start
if i != sortedEnd {
// Plop x into position
- elements[i] = x
+ self[i] = x
}
- elements.formIndex(after: &sortedEnd)
+ formIndex(after: &sortedEnd)
}
}
}
-/// Sorts the elements at `elements[a]`, `elements[b]`, and `elements[c]`.
-/// Stable.
-///
-/// The indices passed as `a`, `b`, and `c` do not need to be consecutive, but
-/// must be in strict increasing order.
-///
-/// - Precondition: `a < b && b < c`
-/// - Postcondition: `elements[a] <= elements[b] && elements[b] <= elements[c]`
-@inlinable
-public // @testable
-func _sort3<C: MutableCollection & RandomAccessCollection>(
- _ elements: inout C,
- _ a: C.Index, _ b: C.Index, _ c: C.Index,
- by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool
-) rethrows {
- // There are thirteen possible permutations for the original ordering of
- // the elements at indices `a`, `b`, and `c`. The comments in the code below
- // show the relative ordering of the three elements using a three-digit
- // number as shorthand for the position and comparative relationship of
- // each element. For example, "312" indicates that the element at `a` is the
- // largest of the three, the element at `b` is the smallest, and the element
- // at `c` is the median. This hypothetical input array has a 312 ordering for
- // `a`, `b`, and `c`:
- //
- // [ 7, 4, 3, 9, 2, 0, 3, 7, 6, 5 ]
- // ^ ^ ^
- // a b c
- //
- // - If each of the three elements is distinct, they could be ordered as any
- // of the permutations of 1, 2, and 3: 123, 132, 213, 231, 312, or 321.
- // - If two elements are equivalent and one is distinct, they could be
- // ordered as any permutation of 1, 1, and 2 or 1, 2, and 2: 112, 121, 211,
- // 122, 212, or 221.
- // - If all three elements are equivalent, they are already in order: 111.
+extension MutableCollection {
+ /// Sorts the elements at `elements[a]`, `elements[b]`, and `elements[c]`.
+ /// Stable.
+ ///
+ /// The indices passed as `a`, `b`, and `c` do not need to be consecutive, but
+ /// must be in strict increasing order.
+ ///
+ /// - Precondition: `a < b && b < c`
+ /// - Postcondition: `self[a] <= self[b] && self[b] <= self[c]`
+ @inlinable
+ public // @testable
+ mutating func _sort3(
+ _ a: Index, _ b: Index, _ c: Index,
+ by areInIncreasingOrder: (Element, Element) throws -> Bool
+ ) rethrows {
+ // There are thirteen possible permutations for the original ordering of
+ // the elements at indices `a`, `b`, and `c`. The comments in the code below
+ // show the relative ordering of the three elements using a three-digit
+ // number as shorthand for the position and comparative relationship of
+ // each element. For example, "312" indicates that the element at `a` is the
+ // largest of the three, the element at `b` is the smallest, and the element
+ // at `c` is the median. This hypothetical input array has a 312 ordering for
+ // `a`, `b`, and `c`:
+ //
+ // [ 7, 4, 3, 9, 2, 0, 3, 7, 6, 5 ]
+ // ^ ^ ^
+ // a b c
+ //
+ // - If each of the three elements is distinct, they could be ordered as any
+ // of the permutations of 1, 2, and 3: 123, 132, 213, 231, 312, or 321.
+ // - If two elements are equivalent and one is distinct, they could be
+ // ordered as any permutation of 1, 1, and 2 or 1, 2, and 2: 112, 121, 211,
+ // 122, 212, or 221.
+ // - If all three elements are equivalent, they are already in order: 111.
- switch ((try areInIncreasingOrder(elements[b], elements[a])),
- (try areInIncreasingOrder(elements[c], elements[b]))) {
- case (false, false):
- // 0 swaps: 123, 112, 122, 111
- break
-
- case (true, true):
- // 1 swap: 321
- // swap(a, c): 312->123
- elements.swapAt(a, c)
-
- case (true, false):
- // 1 swap: 213, 212 --- 2 swaps: 312, 211
- // swap(a, b): 213->123, 212->122, 312->132, 211->121
- elements.swapAt(a, b)
-
- if (try areInIncreasingOrder(elements[c], elements[b])) {
- // 132 (started as 312), 121 (started as 211)
- // swap(b, c): 132->123, 121->112
- elements.swapAt(b, c)
- }
-
- case (false, true):
- // 1 swap: 132, 121 --- 2 swaps: 231, 221
- // swap(b, c): 132->123, 121->112, 231->213, 221->212
- elements.swapAt(b, c)
-
- if (try areInIncreasingOrder(elements[b], elements[a])) {
- // 213 (started as 231), 212 (started as 221)
- // swap(a, b): 213->123, 212->122
- elements.swapAt(a, b)
- }
- }
-}
-
-/// Reorders `elements` and returns an index `p` such that every element in
-/// `elements[range.lowerBound..<p]` is less than every element in
-/// `elements[p..<range.upperBound]`.
-///
-/// - Precondition: The count of `range` must be >= 3:
-/// `elements.distance(from: range.lowerBound, to: range.upperBound) >= 3`
-@inlinable
-internal func _partition<C: MutableCollection & RandomAccessCollection>(
- _ elements: inout C,
- subRange range: Range<C.Index>,
- by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool
-) rethrows -> C.Index {
- var lo = range.lowerBound
- var hi = elements.index(before: range.upperBound)
-
- // Sort the first, middle, and last elements, then use the middle value
- // as the pivot for the partition.
- let half = numericCast(elements.distance(from: lo, to: hi)) as UInt / 2
- let mid = elements.index(lo, offsetBy: numericCast(half))
- try _sort3(&elements, lo, mid, hi
- , by: areInIncreasingOrder)
- let pivot = elements[mid]
-
- // Loop invariants:
- // * lo < hi
- // * elements[i] < pivot, for i in range.lowerBound..<lo
- // * pivot <= elements[i] for i in hi..<range.upperBound
- Loop: while true {
- FindLo: do {
- elements.formIndex(after: &lo)
- while lo != hi {
- if !(try areInIncreasingOrder(elements[lo], pivot)) { break FindLo }
- elements.formIndex(after: &lo)
- }
- break Loop
- }
-
- FindHi: do {
- elements.formIndex(before: &hi)
- while hi != lo {
- if (try areInIncreasingOrder(elements[hi], pivot)) { break FindHi }
- elements.formIndex(before: &hi)
- }
- break Loop
- }
-
- elements.swapAt(lo, hi)
- }
-
- return lo
-}
-
-@inlinable
-public // @testable
-func _introSort<C: MutableCollection & RandomAccessCollection>(
- _ elements: inout C,
- subRange range: Range<C.Index>
- , by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool
-) rethrows {
-
- let count = elements.distance(from: range.lowerBound, to: range.upperBound)
- if count < 2 {
- return
- }
- // Set max recursion depth to 2*floor(log(N)), as suggested in the introsort
- // paper: http://www.cs.rpi.edu/~musser/gp/introsort.ps
- let depthLimit = 2 * count._binaryLogarithm()
- try _introSortImpl(
- &elements,
- subRange: range,
- by: areInIncreasingOrder,
- depthLimit: depthLimit)
-}
-
-@inlinable
-internal func _introSortImpl<C: MutableCollection & RandomAccessCollection>(
- _ elements: inout C,
- subRange range: Range<C.Index>
- , by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool,
- depthLimit: Int
-) rethrows {
-
- // Insertion sort is better at handling smaller regions.
- if elements.distance(from: range.lowerBound, to: range.upperBound) < 20 {
- try _insertionSort(
- &elements,
- subRange: range
- , by: areInIncreasingOrder)
- return
- }
- if depthLimit == 0 {
- try _heapSort(
- &elements,
- subRange: range
- , by: areInIncreasingOrder)
- return
- }
-
- // Partition and sort.
- // We don't check the depthLimit variable for underflow because this variable
- // is always greater than zero (see check above).
- let partIdx: C.Index = try _partition(
- &elements,
- subRange: range
- , by: areInIncreasingOrder)
- try _introSortImpl(
- &elements,
- subRange: range.lowerBound..<partIdx,
- by: areInIncreasingOrder,
- depthLimit: depthLimit &- 1)
- try _introSortImpl(
- &elements,
- subRange: partIdx..<range.upperBound,
- by: areInIncreasingOrder,
- depthLimit: depthLimit &- 1)
-}
-
-@inlinable
-internal func _siftDown<C: MutableCollection & RandomAccessCollection>(
- _ elements: inout C,
- index: C.Index,
- subRange range: Range<C.Index>,
- by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool
-) rethrows {
- var i = index
- var countToIndex = elements.distance(from: range.lowerBound, to: i)
- var countFromIndex = elements.distance(from: i, to: range.upperBound)
- // Check if left child is within bounds. If not, stop iterating, because there are
- // no children of the given node in the heap.
- while countToIndex + 1 < countFromIndex {
- let left = elements.index(i, offsetBy: countToIndex + 1)
- var largest = i
- if try areInIncreasingOrder(elements[largest], elements[left]) {
- largest = left
- }
- // Check if right child is also within bounds before trying to examine it.
- if countToIndex + 2 < countFromIndex {
- let right = elements.index(after: left)
- if try areInIncreasingOrder(elements[largest], elements[right]) {
- largest = right
- }
- }
- // If a child is bigger than the current node, swap them and continue sifting
- // down.
- if largest != i {
- elements.swapAt(index, largest)
- i = largest
- countToIndex = elements.distance(from: range.lowerBound, to: i)
- countFromIndex = elements.distance(from: i, to: range.upperBound)
- } else {
+ switch try (areInIncreasingOrder(self[b], self[a]),
+ areInIncreasingOrder(self[c], self[b])) {
+ case (false, false):
+ // 0 swaps: 123, 112, 122, 111
break
+
+ case (true, true):
+ // 1 swap: 321
+ // swap(a, c): 312->123
+ swapAt(a, c)
+
+ case (true, false):
+ // 1 swap: 213, 212 --- 2 swaps: 312, 211
+ // swap(a, b): 213->123, 212->122, 312->132, 211->121
+ swapAt(a, b)
+
+ if try areInIncreasingOrder(self[c], self[b]) {
+ // 132 (started as 312), 121 (started as 211)
+ // swap(b, c): 132->123, 121->112
+ swapAt(b, c)
+ }
+
+ case (false, true):
+ // 1 swap: 132, 121 --- 2 swaps: 231, 221
+ // swap(b, c): 132->123, 121->112, 231->213, 221->212
+ swapAt(b, c)
+
+ if try areInIncreasingOrder(self[b], self[a]) {
+ // 213 (started as 231), 212 (started as 221)
+ // swap(a, b): 213->123, 212->122
+ swapAt(a, b)
+ }
}
}
}
-@inlinable
-internal func _heapify<C: MutableCollection & RandomAccessCollection>(
- _ elements: inout C,
- subRange range: Range<C.Index>,
- by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool
-) rethrows {
- // Here we build a heap starting from the lowest nodes and moving to the root.
- // On every step we sift down the current node to obey the max-heap property:
- // parent >= max(leftChild, rightChild)
- //
- // We skip the rightmost half of the array, because these nodes don't have
- // any children.
- let root = range.lowerBound
- var node = elements.index(
- root,
- offsetBy: elements.distance(
- from: range.lowerBound, to: range.upperBound) / 2)
+extension MutableCollection where Self: RandomAccessCollection {
+ /// Reorders the collection and returns an index `p` such that every element
+ /// in `range.lowerBound..<p` is less than every element in
+ /// `p..<range.upperBound`.
+ ///
+ /// - Precondition: The count of `range` must be >= 3 i.e.
+ /// `distance(from: range.lowerBound, to: range.upperBound) >= 3`
+ @inlinable
+ internal mutating func _partition(
+ within range: Range<Index>,
+ by areInIncreasingOrder: (Element, Element) throws -> Bool
+ ) rethrows -> Index {
+ var lo = range.lowerBound
+ var hi = index(before: range.upperBound)
- while node != root {
- elements.formIndex(before: &node)
- try _siftDown(
- &elements,
- index: node,
- subRange: range
- , by: areInIncreasingOrder)
+ // Sort the first, middle, and last elements, then use the middle value
+ // as the pivot for the partition.
+ let half = distance(from: lo, to: hi) / 2
+ let mid = index(lo, offsetBy: half)
+ try _sort3(lo, mid, hi, by: areInIncreasingOrder)
+ let pivot = self[mid]
+
+ // Loop invariants:
+ // * lo < hi
+ // * self[i] < pivot, for i in range.lowerBound..<lo
+ // * pivot <= self[i] for i in hi..<range.upperBound
+ Loop: while true {
+ FindLo: do {
+ formIndex(after: &lo)
+ while lo != hi {
+ if try !areInIncreasingOrder(self[lo], pivot) { break FindLo }
+ formIndex(after: &lo)
+ }
+ break Loop
+ }
+
+ FindHi: do {
+ formIndex(before: &hi)
+ while hi != lo {
+ if try areInIncreasingOrder(self[hi], pivot) { break FindHi }
+ formIndex(before: &hi)
+ }
+ break Loop
+ }
+
+ swapAt(lo, hi)
+ }
+
+ return lo
}
-}
-@inlinable
-internal func _heapSort<C: MutableCollection & RandomAccessCollection>(
- _ elements: inout C,
- subRange range: Range<C.Index>
- , by areInIncreasingOrder: (C.Element, C.Element) throws -> Bool
-) rethrows {
- var hi = range.upperBound
- let lo = range.lowerBound
- try _heapify(&elements, subRange: range, by: areInIncreasingOrder)
- elements.formIndex(before: &hi)
- while hi != lo {
- elements.swapAt(lo, hi)
- try _siftDown(&elements, index: lo, subRange: lo..<hi, by: areInIncreasingOrder)
- elements.formIndex(before: &hi)
+ @inlinable
+ public // @testable
+ mutating func _introSort(
+ within range: Range<Index>,
+ by areInIncreasingOrder: (Element, Element) throws -> Bool
+ ) rethrows {
+
+ let n = distance(from: range.lowerBound, to: range.upperBound)
+ guard n > 1 else { return }
+
+ // Set max recursion depth to 2*floor(log(N)), as suggested in the introsort
+ // paper: http://www.cs.rpi.edu/~musser/gp/introsort.ps
+ let depthLimit = 2 * n._binaryLogarithm()
+ try _introSortImpl(
+ within: range,
+ by: areInIncreasingOrder,
+ depthLimit: depthLimit)
+ }
+
+ @inlinable
+ internal mutating func _introSortImpl(
+ within range: Range<Index>,
+ by areInIncreasingOrder: (Element, Element) throws -> Bool,
+ depthLimit: Int
+ ) rethrows {
+
+ // Insertion sort is better at handling smaller regions.
+ if distance(from: range.lowerBound, to: range.upperBound) < 20 {
+ try _insertionSort(within: range, by: areInIncreasingOrder)
+ } else if depthLimit == 0 {
+ try _heapSort(within: range, by: areInIncreasingOrder)
+ } else {
+ // Partition and sort.
+ // We don't check the depthLimit variable for underflow because this
+ // variable is always greater than zero (see check above).
+ let partIdx = try _partition(within: range, by: areInIncreasingOrder)
+ try _introSortImpl(
+ within: range.lowerBound..<partIdx,
+ by: areInIncreasingOrder,
+ depthLimit: depthLimit &- 1)
+ try _introSortImpl(
+ within: partIdx..<range.upperBound,
+ by: areInIncreasingOrder,
+ depthLimit: depthLimit &- 1)
+ }
+ }
+
+ @inlinable
+ internal mutating func _siftDown(
+ _ idx: Index,
+ within range: Range<Index>,
+ by areInIncreasingOrder: (Element, Element) throws -> Bool
+ ) rethrows {
+ var i = idx
+ var countToIndex = distance(from: range.lowerBound, to: i)
+ var countFromIndex = distance(from: i, to: range.upperBound)
+ // Check if left child is within bounds. If not, stop iterating, because
+ // there are no children of the given node in the heap.
+ while countToIndex + 1 < countFromIndex {
+ let left = index(i, offsetBy: countToIndex + 1)
+ var largest = i
+ if try areInIncreasingOrder(self[largest], self[left]) {
+ largest = left
+ }
+ // Check if right child is also within bounds before trying to examine it.
+ if countToIndex + 2 < countFromIndex {
+ let right = index(after: left)
+ if try areInIncreasingOrder(self[largest], self[right]) {
+ largest = right
+ }
+ }
+ // If a child is bigger than the current node, swap them and continue sifting
+ // down.
+ if largest != i {
+ swapAt(idx, largest)
+ i = largest
+ countToIndex = distance(from: range.lowerBound, to: i)
+ countFromIndex = distance(from: i, to: range.upperBound)
+ } else {
+ break
+ }
+ }
+ }
+
+ @inlinable
+ internal mutating func _heapify(
+ within range: Range<Index>,
+ by areInIncreasingOrder: (Element, Element) throws -> Bool
+ ) rethrows {
+ // Here we build a heap starting from the lowest nodes and moving to the
+ // root. On every step we sift down the current node to obey the max-heap
+ // property:
+ // parent >= max(leftChild, rightChild)
+ //
+ // We skip the rightmost half of the array, because these nodes don't have
+ // any children.
+ let root = range.lowerBound
+ let half = distance(from: range.lowerBound, to: range.upperBound) / 2
+ var node = index(root, offsetBy: half)
+
+ while node != root {
+ formIndex(before: &node)
+ try _siftDown(node, within: range, by: areInIncreasingOrder)
+ }
+ }
+
+ @inlinable
+ internal mutating func _heapSort(
+ within range: Range<Index>,
+ by areInIncreasingOrder: (Element, Element) throws -> Bool
+ ) rethrows {
+ var hi = range.upperBound
+ let lo = range.lowerBound
+ try _heapify(within: range, by: areInIncreasingOrder)
+ formIndex(before: &hi)
+ while hi != lo {
+ swapAt(lo, hi)
+ try _siftDown(lo, within: lo..<hi, by: areInIncreasingOrder)
+ formIndex(before: &hi)
+ }
}
}
diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift
index f90fd8d..8c1ac62 100644
--- a/test/Constraints/diagnostics.swift
+++ b/test/Constraints/diagnostics.swift
@@ -719,14 +719,13 @@
}
postfix operator +++
-postfix func +++ <T>(_: inout T) -> T { fatalError() } // expected-note {{in call to operator '+++'}}
+postfix func +++ <T>(_: inout T) -> T { fatalError() }
// <rdar://problem/21523291> compiler error message for mutating immutable field is incorrect
func r21523291(_ bytes : UnsafeMutablePointer<UInt8>) {
- let i = 42
+ let i = 42 // expected-note {{change 'let' to 'var' to make it mutable}}
- // FIXME: rdar://41416382
- _ = bytes[i+++] // expected-error {{generic parameter 'T' could not be inferred}}
+ _ = bytes[i+++] // expected-error {{cannot pass immutable value to mutating operator: 'i' is a 'let' constant}}
}
diff --git a/test/Constraints/lvalues.swift b/test/Constraints/lvalues.swift
index e4e2fda..2ac45f7 100644
--- a/test/Constraints/lvalues.swift
+++ b/test/Constraints/lvalues.swift
@@ -76,14 +76,14 @@
f1(&non_settable_x) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
// - inout assignment
non_settable_x += x // expected-error{{left side of mutating operator isn't mutable: 'non_settable_x' is a get-only property}}
-+++non_settable_x // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
++++non_settable_x // expected-error{{cannot pass immutable value to mutating operator: 'non_settable_x' is a get-only property}}
// non-settable property is non-settable:
z.non_settable_x = x // expected-error{{cannot assign to property: 'non_settable_x' is a get-only property}}
f2(&z.non_settable_x) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
f1(&z.non_settable_x) // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
z.non_settable_x += x // expected-error{{left side of mutating operator isn't mutable: 'non_settable_x' is a get-only property}}
-+++z.non_settable_x // expected-error{{cannot pass immutable value as inout argument: 'non_settable_x' is a get-only property}}
++++z.non_settable_x // expected-error{{cannot pass immutable value to mutating operator: 'non_settable_x' is a get-only property}}
// non-settable subscript is non-settable:
z[0] = 0.0 // expected-error{{cannot assign through subscript: subscript is get-only}}
@@ -97,7 +97,7 @@
f2(&fz().settable_x) // expected-error{{cannot pass immutable value as inout argument: 'fz' returns immutable value}}
f1(&fz().settable_x) // expected-error{{cannot pass immutable value as inout argument: 'fz' returns immutable value}}
fz().settable_x += x // expected-error{{left side of mutating operator isn't mutable: 'fz' returns immutable value}}
-+++fz().settable_x // expected-error{{cannot pass immutable value as inout argument: 'fz' returns immutable value}}
++++fz().settable_x // expected-error{{cannot pass immutable value to mutating operator: 'fz' returns immutable value}}
// settable property of an rvalue reference type IS SETTABLE:
fref().property = 0.0
diff --git a/test/expr/cast/array_iteration.swift b/test/expr/cast/array_iteration.swift
index a9e51ce..61e98e5 100644
--- a/test/expr/cast/array_iteration.swift
+++ b/test/expr/cast/array_iteration.swift
@@ -16,9 +16,7 @@
doFoo()
}
-// FIXME: Diagnostic below should be "'AnyObject' is not convertible to
-// 'View'", but IUO type gets in the way of proper diagnosis.
-for view:View in rootView.subviews { // expected-error{{type 'Array<AnyObject>?' does not conform to protocol 'Sequence'}}
+for view:View in rootView.subviews { // expected-error{{'AnyObject' is not convertible to 'View'}}
doFoo()
}
diff --git a/test/expr/expressions.swift b/test/expr/expressions.swift
index b8aeeeb..4a38054 100644
--- a/test/expr/expressions.swift
+++ b/test/expr/expressions.swift
@@ -614,7 +614,7 @@
i64 += 1
i8 -= 1
- Int64(5) += 1 // expected-error{{left side of mutating operator isn't mutable: function call returns immutable value}}
+ Int64(5) += 1 // expected-error{{left side of mutating operator has immutable type 'Int64'}}
// <rdar://problem/17691565> attempt to modify a 'let' variable with ++ results in typecheck error not being able to apply ++ to Float
let a = i8 // expected-note {{change 'let' to 'var' to make it mutable}} {{3-6=var}}
diff --git a/test/multifile/Inputs/require-finalize-witness-other.swift b/test/multifile/Inputs/require-finalize-witness-other.swift
new file mode 100644
index 0000000..eec03ff
--- /dev/null
+++ b/test/multifile/Inputs/require-finalize-witness-other.swift
@@ -0,0 +1,5 @@
+import Foundation
+
+extension C {
+ @objc func foo(_: String) { }
+}
diff --git a/test/multifile/require-finalize-witness.swift b/test/multifile/require-finalize-witness.swift
new file mode 100644
index 0000000..7c8b96a
--- /dev/null
+++ b/test/multifile/require-finalize-witness.swift
@@ -0,0 +1,14 @@
+// RUN: %target-swift-frontend -module-name test -emit-ir -primary-file %s %S/Inputs/require-finalize-witness-other.swift -sdk %sdk -o - | %FileCheck %s
+
+// REQUIRES: objc_interop
+
+import Foundation
+
+@objc class C: NSObject { }
+
+protocol P {
+ func foo(_: String)
+}
+
+// CHECK: define {{.*}} @"$S4test1CCAA1PA2aDP3fooyySSFTW"
+extension C: P { }
diff --git a/validation-test/stdlib/Algorithm.swift b/validation-test/stdlib/Algorithm.swift
index ef15ea3..e3405a6 100644
--- a/validation-test/stdlib/Algorithm.swift
+++ b/validation-test/stdlib/Algorithm.swift
@@ -243,7 +243,7 @@
]) {
// decorate with offset, but sort by value
var input = Array($0.enumerated())
- _sort3(&input, 0, 1, 2) { $0.element < $1.element }
+ input._sort3(0, 1, 2) { $0.element < $1.element }
// offsets should still be ordered for equal values
expectTrue(isSorted(input) {
if $0.element == $1.element {
diff --git a/validation-test/stdlib/FixedPointDiagnostics.swift.gyb b/validation-test/stdlib/FixedPointDiagnostics.swift.gyb
index 48d776b..164a162 100644
--- a/validation-test/stdlib/FixedPointDiagnostics.swift.gyb
+++ b/validation-test/stdlib/FixedPointDiagnostics.swift.gyb
@@ -95,7 +95,8 @@
x += Stride(1) // expected-error {{'+=' is unavailable: Please use explicit type conversions or Strideable methods for mixed-type arithmetics.}}
x -= Stride(1) // expected-error {{'-=' is unavailable: Please use explicit type conversions or Strideable methods for mixed-type arithmetics.}}
- _ = (x - x) as Stride // expected-error {{ambiguous reference to member '-'}}
+ // Terrible over-specific error, but at least disabled
+ _ = (x - x) as Stride // expected-error {{'(@lvalue ${T}, @lvalue ${T}) -> ${T}' is not convertible to '(${T}, ${T}) -> ${T}'}}
//===------------------------------------------------------------------===//
// The following errors are different because they're not being
diff --git a/validation-test/stdlib/Sort.swift.gyb b/validation-test/stdlib/Sort.swift.gyb
index 3ebd3e9..6cdd31c 100644
--- a/validation-test/stdlib/Sort.swift.gyb
+++ b/validation-test/stdlib/Sort.swift.gyb
@@ -120,7 +120,7 @@
let i1 = 400
let i2 = 700
sortedAry2 = ary
- _introSort(&sortedAry2, subRange: i1..<i2, by: <)
+ sortedAry2._introSort(within: i1..<i2, by: <)
expectEqual(ary[0..<i1], sortedAry2[0..<i1])
expectSortedCollection(sortedAry2[i1..<i2], ary[i1..<i2])
expectEqual(ary[i2..<count], sortedAry2[i2..<count])