Merge pull request #21515 from nathawes/r46694149-cursor-info-crash-in-extractInlinableText
[sourcekitd][AST] Fix CursorInfo crash on method with unresolved default value
diff --git a/benchmark/scripts/Benchmark_Driver b/benchmark/scripts/Benchmark_Driver
index 6df7916..230e180 100755
--- a/benchmark/scripts/Benchmark_Driver
+++ b/benchmark/scripts/Benchmark_Driver
@@ -330,11 +330,15 @@
super(BenchmarkDoctor, self).__init__()
self.driver = driver or BenchmarkDriver(args)
self.results = {}
- self.console_handler = logging.StreamHandler(sys.stdout)
- self.console_handler.setLevel(logging.DEBUG if args.verbose else
- logging.INFO)
- self.console_handler.setFormatter(
- LoggingReportFormatter(use_color=sys.stdout.isatty()))
+
+ if hasattr(args, 'markdown') and args.markdown:
+ self.console_handler = MarkdownReportHandler(sys.stdout)
+ else:
+ self.console_handler = logging.StreamHandler(sys.stdout)
+ self.console_handler.setFormatter(
+ LoggingReportFormatter(use_color=sys.stdout.isatty()))
+ self.console_handler.setLevel(logging.DEBUG if args.verbose else
+ logging.INFO)
self.log.addHandler(self.console_handler)
self.log.debug('Checking tests: %s', ', '.join(self.driver.tests))
self.requirements = [
@@ -350,6 +354,7 @@
"""Close log handlers on exit."""
for handler in list(self.log.handlers):
handler.close()
+ self.log.removeHandler(self.console_handler)
benchmark_naming_convention_re = re.compile(r'[A-Z][a-zA-Z0-9\-.!?]+')
camel_humps_re = re.compile(r'[a-z][A-Z]')
@@ -703,9 +708,13 @@
'check',
help='',
parents=[shared_benchmarks_parser])
- check_parser.add_argument(
+ check_group = check_parser.add_mutually_exclusive_group()
+ check_group.add_argument(
'-v', '--verbose', action='store_true',
- help='show more details during benchmark analysis',)
+ help='show more details during benchmark analysis')
+ check_group.add_argument(
+ '-md', '--markdown', action='store_true',
+ help='format report as Markdown table')
check_parser.set_defaults(func=BenchmarkDoctor.run_check)
compare_parser = subparsers.add_parser(
diff --git a/benchmark/scripts/test_Benchmark_Driver.py b/benchmark/scripts/test_Benchmark_Driver.py
index f0194ce..5ecf76f 100644
--- a/benchmark/scripts/test_Benchmark_Driver.py
+++ b/benchmark/scripts/test_Benchmark_Driver.py
@@ -120,6 +120,20 @@
self.assertTrue(parse_args(['check', '-v']).verbose)
self.assertTrue(parse_args(['check', '--verbose']).verbose)
+ def test_check_supports_mardown_output(self):
+ self.assertFalse(parse_args(['check']).markdown)
+ self.assertTrue(parse_args(['check', '-md']).markdown)
+ self.assertTrue(parse_args(['check', '--markdown']).markdown)
+
+ def test_check_flags_are_mutually_exclusive(self):
+ with captured_output() as (out, err):
+ self.assertRaises(SystemExit,
+ parse_args, ['check', '-md', '-v'])
+ self.assert_contains(
+ ['error:', 'argument -v/--verbose: ' +
+ 'not allowed with argument -md/--markdown'],
+ err.getvalue())
+
class ArgsStub(object):
def __init__(self):
@@ -497,7 +511,7 @@
def setUp(self):
super(TestBenchmarkDoctor, self).setUp()
- self.args = Stub(verbose=False)
+ self.args = Stub(verbose=False, markdown=False)
self._doctor_log_handler.reset()
self.logs = self._doctor_log_handler.messages
@@ -516,8 +530,9 @@
def test_supports_verbose_output(self):
driver = BenchmarkDriverMock(tests=['B1', 'B2'])
driver.verbose = True
+ self.args.verbose = True
with captured_output() as (out, _):
- BenchmarkDoctor(Stub(verbose=True), driver)
+ BenchmarkDoctor(self.args, driver)
self.assert_contains(['Checking tests: B1, B2'], out.getvalue())
def test_uses_report_formatter(self):
@@ -528,6 +543,14 @@
self.assertTrue(isinstance(console_handler.formatter,
LoggingReportFormatter))
+ def test_uses_optional_markdown_report_formatter(self):
+ self.args.markdown = True
+ with captured_output() as (_, _):
+ doc = BenchmarkDoctor(self.args, BenchmarkDriverMock(tests=['B1']))
+ self.assertTrue(doc)
+ console_handler = logging.getLogger('BenchmarkDoctor').handlers[1]
+ self.assertTrue(isinstance(console_handler, MarkdownReportHandler))
+
def test_measure_10_independent_1s_benchmark_series(self):
"""Measurement strategy takes 5 i2 and 5 i1 series.
diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def
index 1467616..1651bfb 100644
--- a/include/swift/AST/DiagnosticsSema.def
+++ b/include/swift/AST/DiagnosticsSema.def
@@ -3886,6 +3886,10 @@
"replaced function %0 could not be found", (DeclName))
ERROR(dynamic_replacement_accessor_not_found, none,
"replaced accessor for %0 could not be found", (DeclName))
+ERROR(dynamic_replacement_accessor_ambiguous, none,
+ "replaced accessor for %0 occurs in multiple places", (DeclName))
+NOTE(dynamic_replacement_accessor_ambiguous_candidate, none,
+ "candidate accessor found in module %0", (DeclName))
ERROR(dynamic_replacement_function_of_type_not_found, none,
"replaced function %0 of type %1 could not be found", (DeclName, Type))
NOTE(dynamic_replacement_found_function_of_type, none,
diff --git a/lib/Basic/UUID.cpp b/lib/Basic/UUID.cpp
index f17aab4..ad90e55 100644
--- a/lib/Basic/UUID.cpp
+++ b/lib/Basic/UUID.cpp
@@ -23,6 +23,7 @@
#define NOMINMAX
#include <objbase.h>
#include <string>
+#include <algorithm>
#else
#include <uuid/uuid.h>
#endif
@@ -94,6 +95,7 @@
char* signedStr = reinterpret_cast<char*>(str);
memcpy(out.data(), signedStr, StringBufferSize);
+ std::transform(std::begin(out), std::end(out), std::begin(out), toupper);
#else
uuid_unparse_upper(Value, out.data());
#endif
diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp
index 3e71875..590bf2d 100644
--- a/lib/Sema/CSDiag.cpp
+++ b/lib/Sema/CSDiag.cpp
@@ -1876,7 +1876,7 @@
(isa<OverloadedDeclRefExpr>(subExpr->getValueProvidingExpr()))) {
return subExpr;
}
-
+
// Save any existing type data of the subexpr tree, and reset it to null in
// prep for re-type-checking the tree. If things fail, we can revert the
// types back to their original state.
@@ -1920,9 +1920,12 @@
// holding on to an expression containing open existential types but
// no OpenExistentialExpr, which breaks invariants enforced by the
// ASTChecker.
- if (isa<OpenExistentialExpr>(subExpr))
- eraseOpenedExistentials(CS, subExpr);
-
+ // Another reason why we need to do this is because diagnostics might pick
+ // constraint anchor for re-typechecking which would only have opaque value
+ // expression and not enclosing open existential, which is going to trip up
+ // sanitizer.
+ eraseOpenedExistentials(CS, subExpr);
+
// If recursive type checking failed, then an error was emitted. Return
// null to indicate this to the caller.
if (!resultTy)
diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp
index 5577763..2cdb2c0 100644
--- a/lib/Sema/TypeCheckAttr.cpp
+++ b/lib/Sema/TypeCheckAttr.cpp
@@ -2036,8 +2036,9 @@
replacement->getModuleScopeContext(), nullptr,
attr->getLocation());
if (lookup.isSuccess()) {
- for (auto entry : lookup.Results)
+ for (auto entry : lookup.Results) {
results.push_back(entry.getValueDecl());
+ }
}
return;
}
@@ -2051,6 +2052,28 @@
{typeCtx}, replacedDeclName, NL_QualifiedDefault, results);
}
+/// Remove any argument labels from the interface type of the given value that
+/// are extraneous from the type system's point of view, producing the
+/// type to compare against for the purposes of dynamic replacement.
+static Type getDynamicComparisonType(ValueDecl *value) {
+ unsigned numArgumentLabels = 0;
+
+ if (isa<AbstractFunctionDecl>(value)) {
+ ++numArgumentLabels;
+
+ if (value->getDeclContext()->isTypeContext())
+ ++numArgumentLabels;
+ } else if (isa<SubscriptDecl>(value)) {
+ ++numArgumentLabels;
+ }
+
+ auto interfaceType = value->getInterfaceType();
+ if (!interfaceType)
+ return ErrorType::get(value->getASTContext());
+
+ return interfaceType->removeArgumentLabels(numArgumentLabels);
+}
+
static FuncDecl *findReplacedAccessor(DeclName replacedVarName,
AccessorDecl *replacement,
DynamicReplacementAttr *attr,
@@ -2060,13 +2083,47 @@
SmallVector<ValueDecl *, 4> results;
lookupReplacedDecl(replacedVarName, attr, replacement, results);
+ // Filter out any accessors that won't work.
+ if (!results.empty()) {
+ auto replacementStorage = replacement->getStorage();
+ TC.validateDecl(replacementStorage);
+ Type replacementStorageType = getDynamicComparisonType(replacementStorage);
+ results.erase(std::remove_if(results.begin(), results.end(),
+ [&](ValueDecl *result) {
+ // Check for static/instance mismatch.
+ if (result->isStatic() != replacementStorage->isStatic())
+ return true;
+
+ // Check for type mismatch.
+ TC.validateDecl(result);
+ auto resultType = getDynamicComparisonType(result);
+ if (!resultType->isEqual(replacementStorageType)) {
+ return true;
+ }
+
+ return false;
+ }),
+ results.end());
+ }
+
if (results.empty()) {
TC.diagnose(attr->getLocation(),
diag::dynamic_replacement_accessor_not_found, replacedVarName);
attr->setInvalid();
return nullptr;
}
- assert(results.size() == 1 && "Should only have on var or fun");
+
+ if (results.size() > 1) {
+ TC.diagnose(attr->getLocation(),
+ diag::dynamic_replacement_accessor_ambiguous, replacedVarName);
+ for (auto result : results) {
+ TC.diagnose(result,
+ diag::dynamic_replacement_accessor_ambiguous_candidate,
+ result->getModuleContext()->getFullName());
+ }
+ attr->setInvalid();
+ return nullptr;
+ }
assert(!isa<FuncDecl>(results[0]));
TC.validateDecl(results[0]);
@@ -2117,6 +2174,10 @@
lookupReplacedDecl(replacedFunctionName, attr, replacement, results);
for (auto *result : results) {
+ // Check for static/instance mismatch.
+ if (result->isStatic() != replacement->isStatic())
+ continue;
+
TC.validateDecl(result);
if (result->getInterfaceType()->getCanonicalType()->matches(
replacement->getInterfaceType()->getCanonicalType(),
diff --git a/stdlib/public/Darwin/Foundation/NSDictionary.swift b/stdlib/public/Darwin/Foundation/NSDictionary.swift
index 4d2ec75..2b5b58b 100644
--- a/stdlib/public/Darwin/Foundation/NSDictionary.swift
+++ b/stdlib/public/Darwin/Foundation/NSDictionary.swift
@@ -61,6 +61,140 @@
to: NSDictionary.self)
}
+ /***
+ Precondition: `buffer` points to a region of memory bound to `AnyObject`,
+ with a capacity large enough to fit at least `index`+1 elements of type `T`
+
+ _bridgeInitialize rebinds the `index`th `T` of `buffer` to `T`,
+ and initializes it to `value`
+
+ Note: *not* the `index`th element of `buffer`, since T and AnyObject may be
+ different sizes. e.g. if T is String (2 words) then given a buffer like so:
+
+ [object:AnyObject, object:AnyObject, uninitialized, uninitialized]
+
+ `_bridgeInitialize(1, of: buffer, to: buffer[1] as! T)` will leave it as:
+
+ [object:AnyObject, object:AnyObject, string:String]
+
+ and `_bridgeInitialize(0, of: buffer, to: buffer[0] as! T)` will then leave:
+
+ [string:String, string:String]
+
+ Doing this in reverse order as shown above is required if T and AnyObject are
+ different sizes. Here's what we get if instead of 1, 0 we did 0, 1:
+
+ [object:AnyObject, object:AnyObject, uninitialized, uninitialized]
+ [string:String, uninitialized, uninitialized]
+ <segfault trying to treat the second word of 'string' as an AnyObject>
+
+ Note: if you have retained any of the objects in `buffer`, you must release
+ them separately, _bridgeInitialize will overwrite them without releasing them
+ */
+ @inline(__always)
+ private static func _bridgeInitialize<T>(index:Int,
+ of buffer: UnsafePointer<AnyObject>, to value: T) {
+ let typedBase = UnsafeMutableRawPointer(mutating:
+ buffer).assumingMemoryBound(to: T.self)
+ let rawTarget = UnsafeMutableRawPointer(mutating: typedBase + index)
+ rawTarget.initializeMemory(as: T.self, repeating: value, count: 1)
+ }
+
+ @inline(__always)
+ private static func _verbatimForceBridge<T>(
+ _ buffer: UnsafeMutablePointer<AnyObject>,
+ count: Int,
+ to: T.Type
+ ) {
+ //doesn't have to iterate in reverse because sizeof(T) == sizeof(AnyObject)
+ for i in 0..<count {
+ _bridgeInitialize(index: i, of: buffer, to: buffer[i] as! T)
+ }
+ }
+
+ @inline(__always)
+ private static func _verbatimBridge<T>(
+ _ buffer: UnsafeMutablePointer<AnyObject>,
+ count: Int,
+ to type: T.Type
+ ) -> Int {
+ var numUninitialized = count
+ while numUninitialized > 0 {
+ guard let bridged = buffer[numUninitialized - 1] as? T else {
+ return numUninitialized
+ }
+ numUninitialized -= 1
+ _bridgeInitialize(index: numUninitialized, of: buffer, to: bridged)
+ }
+ return numUninitialized
+ }
+
+ @inline(__always)
+ private static func _nonVerbatimForceBridge<T>(
+ _ buffer: UnsafeMutablePointer<AnyObject>,
+ count: Int,
+ to: T.Type
+ ) {
+ for i in (0..<count).reversed() {
+ let bridged = Swift._forceBridgeFromObjectiveC(buffer[i], T.self)
+ _bridgeInitialize(index: i, of: buffer, to: bridged)
+ }
+ }
+
+ @inline(__always)
+ private static func _nonVerbatimBridge<T>(
+ _ buffer: UnsafeMutablePointer<AnyObject>,
+ count: Int,
+ to: T.Type
+ ) -> Int {
+ var numUninitialized = count
+ while numUninitialized > 0 {
+ guard let bridged = Swift._conditionallyBridgeFromObjectiveC(
+ buffer[numUninitialized - 1], T.self)
+ else {
+ return numUninitialized
+ }
+ numUninitialized -= 1
+ _bridgeInitialize(index: numUninitialized, of: buffer, to: bridged)
+ }
+ return numUninitialized
+ }
+
+ @inline(__always)
+ private static func _forceBridge<T>(
+ _ buffer: UnsafeMutablePointer<AnyObject>,
+ count: Int,
+ to: T.Type
+ ) {
+ if _isBridgedVerbatimToObjectiveC(T.self) {
+ _verbatimForceBridge(buffer, count: count, to: T.self)
+ } else {
+ _nonVerbatimForceBridge(buffer, count: count, to: T.self)
+ }
+ }
+
+ @inline(__always)
+ private static func _conditionallyBridge<T>(
+ _ buffer: UnsafeMutablePointer<AnyObject>,
+ count: Int,
+ to: T.Type
+ ) -> Bool {
+ let numUninitialized:Int
+ if _isBridgedVerbatimToObjectiveC(T.self) {
+ numUninitialized = _verbatimBridge(buffer, count: count, to: T.self)
+ } else {
+ numUninitialized = _nonVerbatimBridge(buffer, count: count, to: T.self)
+ }
+ if numUninitialized == 0 {
+ return true
+ }
+ let numInitialized = count - numUninitialized
+ (UnsafeMutableRawPointer(mutating: buffer).assumingMemoryBound(to:
+ T.self) + numUninitialized).deinitialize(count: numInitialized)
+ return false
+ }
+
+ @_specialize(where Key == String, Value == Any)
public static func _forceBridgeFromObjectiveC(
_ d: NSDictionary,
result: inout Dictionary?
@@ -73,53 +207,120 @@
if _isBridgedVerbatimToObjectiveC(Key.self) &&
_isBridgedVerbatimToObjectiveC(Value.self) {
+ //Lazily type-checked on access
result = [Key : Value](_cocoaDictionary: d)
return
}
- if Key.self == String.self {
+ let keyStride = MemoryLayout<Key>.stride
+ let valueStride = MemoryLayout<Value>.stride
+ let objectStride = MemoryLayout<AnyObject>.stride
+
+ //If Key or Value are smaller than AnyObject, a Dictionary with N elements
+ //doesn't have large enough backing stores to hold the objects to be bridged
+ //For now we just handle that case the slow way.
+ if keyStride < objectStride || valueStride < objectStride {
+ var builder = _DictionaryBuilder<Key, Value>(count: d.count)
+ d.enumerateKeysAndObjects({ (anyKey: Any, anyValue: Any, _) in
+ let anyObjectKey = anyKey as AnyObject
+ let anyObjectValue = anyValue as AnyObject
+ builder.add(
+ key: Swift._forceBridgeFromObjectiveC(anyObjectKey, Key.self),
+ value: Swift._forceBridgeFromObjectiveC(anyObjectValue, Value.self))
+ })
+ result = builder.take()
+ } else {
+ defer { _fixLifetime(d) }
+
+ let numElems = d.count
+
// String and NSString have different concepts of equality, so
// string-keyed NSDictionaries may generate key collisions when bridged
// over to Swift. See rdar://problem/35995647
- var dict = Dictionary(minimumCapacity: d.count)
- d.enumerateKeysAndObjects({ (anyKey: Any, anyValue: Any, _) in
- let key = Swift._forceBridgeFromObjectiveC(
- anyKey as AnyObject, Key.self)
- let value = Swift._forceBridgeFromObjectiveC(
- anyValue as AnyObject, Value.self)
- // FIXME: Log a warning if `dict` already had a value for `key`
- dict[key] = value
- })
- result = dict
- return
+ let handleDuplicates = (Key.self == String.self)
+
+ result = Dictionary(_unsafeUninitializedCapacity: numElems,
+ allowingDuplicates: handleDuplicates) { (keys, vals, outCount) in
+
+ let objectKeys = UnsafeMutableRawPointer(mutating:
+ keys.baseAddress!).assumingMemoryBound(to: AnyObject.self)
+ let objectVals = UnsafeMutableRawPointer(mutating:
+ vals.baseAddress!).assumingMemoryBound(to: AnyObject.self)
+
+ //This initializes the first N AnyObjects of the Dictionary buffers.
+ //Any unused buffer space is left uninitialized
+ //This is fixed up in-place as we bridge elements, by _bridgeInitialize
+ __NSDictionaryGetObjects(d, objectVals, objectKeys, numElems)
+
+ _forceBridge(objectKeys, count: numElems, to: Key.self)
+ _forceBridge(objectVals, count: numElems, to: Value.self)
+
+ outCount = numElems
+ }
}
-
- // `Dictionary<Key, Value>` where either `Key` or `Value` is a value type
- // may not be backed by an NSDictionary.
- var builder = _DictionaryBuilder<Key, Value>(count: d.count)
- d.enumerateKeysAndObjects({ (anyKey: Any, anyValue: Any, _) in
- let anyObjectKey = anyKey as AnyObject
- let anyObjectValue = anyValue as AnyObject
- builder.add(
- key: Swift._forceBridgeFromObjectiveC(anyObjectKey, Key.self),
- value: Swift._forceBridgeFromObjectiveC(anyObjectValue, Value.self))
- })
- result = builder.take()
}
-
+
+ @_specialize(where Key == String, Value == Any)
public static func _conditionallyBridgeFromObjectiveC(
_ x: NSDictionary,
result: inout Dictionary?
) -> Bool {
- let anyDict = x as [NSObject : AnyObject]
- if _isBridgedVerbatimToObjectiveC(Key.self) &&
- _isBridgedVerbatimToObjectiveC(Value.self) {
- result = Swift._dictionaryDownCastConditional(anyDict)
+
+ if let native = [Key : Value]._bridgeFromObjectiveCAdoptingNativeStorageOf(
+ x as AnyObject) {
+ result = native
+ return true
+ }
+
+ let keyStride = MemoryLayout<Key>.stride
+ let valueStride = MemoryLayout<Value>.stride
+ let objectStride = MemoryLayout<AnyObject>.stride
+
+ //If Key or Value are smaller than AnyObject, a Dictionary with N elements
+ //doesn't have large enough backing stores to hold the objects to be bridged
+ //For now we just handle that case the slow way.
+ if keyStride < objectStride || valueStride < objectStride {
+ result = x as [NSObject : AnyObject] as? Dictionary
return result != nil
}
- result = anyDict as? Dictionary
- return result != nil
+ defer { _fixLifetime(x) }
+
+ let numElems = x.count
+ var success = true
+
+ // String and NSString have different concepts of equality, so
+ // string-keyed NSDictionaries may generate key collisions when bridged
+ // over to Swift. See rdar://problem/35995647
+ let handleDuplicates = (Key.self == String.self)
+
+ let tmpResult = Dictionary(_unsafeUninitializedCapacity: numElems,
+ allowingDuplicates: handleDuplicates) { (keys, vals, outCount) in
+
+ let objectKeys = UnsafeMutableRawPointer(mutating:
+ keys.baseAddress!).assumingMemoryBound(to: AnyObject.self)
+ let objectVals = UnsafeMutableRawPointer(mutating:
+ vals.baseAddress!).assumingMemoryBound(to: AnyObject.self)
+
+ //This initializes the first N AnyObjects of the Dictionary buffers.
+ //Any unused buffer space is left uninitialized
+ //This is fixed up in-place as we bridge elements, by _bridgeInitialize
+ __NSDictionaryGetObjects(x, objectVals, objectKeys, numElems)
+
+ success = _conditionallyBridge(objectKeys, count: numElems, to: Key.self)
+ if success {
+ success = _conditionallyBridge(objectVals,
+ count: numElems, to: Value.self)
+ if !success {
+ (UnsafeMutableRawPointer(mutating: objectKeys).assumingMemoryBound(to:
+ Key.self)).deinitialize(count: numElems)
+ }
+ }
+ outCount = success ? numElems : 0
+ }
+
+ result = success ? tmpResult : nil
+ return success
}
@_effects(readonly)
diff --git a/stdlib/public/Windows/WinSDK.swift b/stdlib/public/Windows/WinSDK.swift
index b38159b..a71cf3a 100644
--- a/stdlib/public/Windows/WinSDK.swift
+++ b/stdlib/public/Windows/WinSDK.swift
@@ -30,3 +30,6 @@
// minwindef.h
public let TRUE: BOOL = 1
+// handleapi.h
+public let INVALID_HANDLE_VALUE: HANDLE = HANDLE(bitPattern: -1)!
+
diff --git a/stdlib/public/core/AtomicInt.swift.gyb b/stdlib/public/core/AtomicInt.swift.gyb
index 6a20166..17d3484 100644
--- a/stdlib/public/core/AtomicInt.swift.gyb
+++ b/stdlib/public/core/AtomicInt.swift.gyb
@@ -72,8 +72,10 @@
}
-@usableFromInline // used by SwiftPrivate._stdlib_AtomicInt
-internal func _swift_stdlib_atomicLoadInt(
+// FIXME: ideally it should not be here, at the very least not public, but
+// @usableFromInline internal to be used by SwiftPrivate._stdlib_AtomicInt
+public // Existing uses outside stdlib
+func _swift_stdlib_atomicLoadInt(
object target: UnsafeMutablePointer<Int>) -> Int {
#if arch(i386) || arch(arm)
let value = Builtin.atomicload_seqcst_Int32(target._rawValue)
@@ -97,8 +99,10 @@
% for operation in ['Add', 'And', 'Or', 'Xor']:
// Warning: no overflow checking.
-@usableFromInline // used by SwiftPrivate._stdlib_AtomicInt
-internal func _swift_stdlib_atomicFetch${operation}Int(
+// FIXME: ideally it should not be here, at the very least not public, but
+// @usableFromInline internal to be used by SwiftPrivate._stdlib_AtomicInt
+public // Existing uses outside stdlib
+func _swift_stdlib_atomicFetch${operation}Int(
object target: UnsafeMutablePointer<Int>,
operand: Int) -> Int {
let rawTarget = UnsafeMutableRawPointer(target)
diff --git a/stdlib/public/core/DictionaryBuilder.swift b/stdlib/public/core/DictionaryBuilder.swift
index 68ec578..0178c92 100644
--- a/stdlib/public/core/DictionaryBuilder.swift
+++ b/stdlib/public/core/DictionaryBuilder.swift
@@ -42,3 +42,134 @@
return Dictionary(_native: _target)
}
}
+
+extension Dictionary {
+ /// Creates a new dictionary with the specified capacity, then calls the given
+ /// closure to initialize its contents.
+ ///
+ /// Foundation uses this initializer to bridge the contents of an NSDictionary
+ /// instance without allocating a pair of intermediary buffers. Pass the
+ /// required capacity and a closure that can intialize the dictionary's
+ /// elements. The closure must return `c`, the number of initialized elements
+ /// in both buffers, such that the elements in the range `0..<c` are
+ /// initialized and the elements in the range `c..<capacity` are
+ /// uninitialized.
+ ///
+ /// The resulting dictionary has a `count` less than or equal to `c`. The
+ /// actual count is less iff some of the initialized keys were duplicates.
+ /// (This cannot happen if `allowingDuplicates` is false.)
+ ///
+ /// The buffers passed to the closure are only valid for the duration of the
+ /// call. After the closure returns, this initializer moves all initialized
+ /// elements into their correct buckets.
+ ///
+ /// - Parameters:
+ /// - capacity: The capacity of the new dictionary.
+ /// - allowingDuplicates: If false, then the caller guarantees that all keys
+ /// are unique. This promise isn't verified -- if it turns out to be
+ /// false, then the resulting dictionary won't be valid.
+ /// - body: A closure that can initialize the dictionary's elements. This
+ /// closure must return the count of the initialized elements, starting at
+ /// the beginning of the buffer.
+ @inlinable
+ public // SPI(Foundation)
+ init(
+ _unsafeUninitializedCapacity capacity: Int,
+ allowingDuplicates: Bool,
+ initializingWith initializer: (
+ _ keys: UnsafeMutableBufferPointer<Key>,
+ _ values: UnsafeMutableBufferPointer<Value>,
+ _ initializedCount: inout Int
+ ) -> Void
+ ) {
+ self.init(_native: _NativeDictionary(
+ _unsafeUninitializedCapacity: capacity,
+ allowingDuplicates: allowingDuplicates,
+ initializingWith: initializer))
+ }
+}
+
+extension _NativeDictionary {
+ @inlinable
+ internal init(
+ _unsafeUninitializedCapacity capacity: Int,
+ allowingDuplicates: Bool,
+ initializingWith initializer: (
+ _ keys: UnsafeMutableBufferPointer<Key>,
+ _ values: UnsafeMutableBufferPointer<Value>,
+ _ initializedCount: inout Int
+ ) -> Void
+ ) {
+ self.init(capacity: capacity)
+ var initializedCount = 0
+ initializer(
+ UnsafeMutableBufferPointer(start: _keys, count: capacity),
+ UnsafeMutableBufferPointer(start: _values, count: capacity),
+ &initializedCount)
+ _precondition(count >= 0 && count <= capacity)
+ _storage._count = initializedCount
+
+ // Hash initialized elements and move each of them into their correct
+ // buckets.
+ //
+ // - We have some number of unprocessed elements at the start of the
+ // key/value buffers -- buckets up to and including `bucket`. Everything
+ // in this region is either unprocessed or in use. There are no
+ // uninitialized entries in it.
+ //
+ // - Everything after `bucket` is either uninitialized or in use. This
+ // region works exactly like regular dictionary storage.
+ //
+ // - "in use" is tracked by the bitmap in `hashTable`, the same way it would
+ // be for a working Dictionary.
+ //
+ // Each iteration of the loop below processes an unprocessed element, and/or
+ // reduces the size of the unprocessed region, while ensuring the above
+ // invariants.
+ var bucket = _HashTable.Bucket(offset: initializedCount - 1)
+ while bucket.offset >= 0 {
+ if hashTable._isOccupied(bucket) {
+ // We've moved an element here in a previous iteration.
+ bucket.offset -= 1
+ continue
+ }
+ // Find the target bucket for this entry and mark it as in use.
+ let target: Bucket
+ if _isDebugAssertConfiguration() || allowingDuplicates {
+ let (b, found) = find(_keys[bucket.offset])
+ if found {
+ _internalInvariant(b != bucket)
+ _precondition(allowingDuplicates, "Duplicate keys found")
+ // Discard duplicate entry.
+ uncheckedDestroy(at: bucket)
+ _storage._count -= 1
+ bucket.offset -= 1
+ continue
+ }
+ hashTable.insert(b)
+ target = b
+ } else {
+ let hashValue = self.hashValue(for: _keys[bucket.offset])
+ target = hashTable.insertNew(hashValue: hashValue)
+ }
+
+ if target > bucket {
+ // The target is outside the unprocessed region. We can simply move the
+ // entry, leaving behind an uninitialized bucket.
+ moveEntry(from: bucket, to: target)
+ // Restore invariants by lowering the region boundary.
+ bucket.offset -= 1
+ } else if target == bucket {
+ // Already in place.
+ bucket.offset -= 1
+ } else {
+ // The target bucket is also in the unprocessed region. Swap the current
+ // item into place, then try again with the swapped-in value, so that we
+ // don't lose it.
+ swapEntry(target, with: bucket)
+ }
+ }
+ // When there are no more unprocessed entries, we're left with a valid
+ // Dictionary.
+ }
+}
diff --git a/stdlib/public/core/HashTable.swift b/stdlib/public/core/HashTable.swift
index 198dc6b..3ad9f42 100644
--- a/stdlib/public/core/HashTable.swift
+++ b/stdlib/public/core/HashTable.swift
@@ -127,7 +127,6 @@
@inlinable
@inline(__always)
internal init(offset: Int) {
- _internalInvariant(offset >= 0)
self.offset = offset
}
diff --git a/stdlib/public/core/NativeDictionary.swift b/stdlib/public/core/NativeDictionary.swift
index 39df16d..f6abdaf 100644
--- a/stdlib/public/core/NativeDictionary.swift
+++ b/stdlib/public/core/NativeDictionary.swift
@@ -137,7 +137,7 @@
@inline(__always)
internal func uncheckedDestroy(at bucket: Bucket) {
defer { _fixLifetime(self) }
- _internalInvariant(hashTable.isOccupied(bucket))
+ _internalInvariant(hashTable.isValid(bucket))
(_keys + bucket.offset).deinitialize(count: 1)
(_values + bucket.offset).deinitialize(count: 1)
}
@@ -620,11 +620,22 @@
@inlinable
@inline(__always)
internal func moveEntry(from source: Bucket, to target: Bucket) {
+ _internalInvariant(hashTable.isValid(source))
+ _internalInvariant(hashTable.isValid(target))
(_keys + target.offset)
.moveInitialize(from: _keys + source.offset, count: 1)
(_values + target.offset)
.moveInitialize(from: _values + source.offset, count: 1)
}
+
+ @inlinable
+ @inline(__always)
+ internal func swapEntry(_ left: Bucket, with right: Bucket) {
+ _internalInvariant(hashTable.isValid(left))
+ _internalInvariant(hashTable.isValid(right))
+ swap(&_keys[left.offset], &_keys[right.offset])
+ swap(&_values[left.offset], &_values[right.offset])
+ }
}
extension _NativeDictionary { // Deletion
diff --git a/test/Constraints/rdar46544601.swift b/test/Constraints/rdar46544601.swift
new file mode 100644
index 0000000..7002cde
--- /dev/null
+++ b/test/Constraints/rdar46544601.swift
@@ -0,0 +1,34 @@
+// RUN: %target-typecheck-verify-swift
+
+struct D {}
+
+class Future<T> {
+ func then<U>(_ fn: @escaping (T) -> Future<U>) -> Future<U> { fatalError() }
+ func thenThrowing<U>(_ fn: @escaping (T) throws -> U) -> Future<U> { fatalError() }
+ func whenFailure(_ fn: @escaping (Error) -> Void) {}
+
+ func and<U>(result: U) -> Future<(T,U)> { fatalError() }
+}
+
+protocol P {
+ func foo(arr: [D], data: ArraySlice<UInt8>) -> Future<D>
+ // expected-note@-1 {{found this candidate}}
+ func bar(root: D, from: P) -> Future<D>
+}
+
+extension P {
+ func foo(arr: [D] = [], data: [UInt8]) -> Future<D> { fatalError() }
+ // expected-note@-1 {{found this candidate}}
+}
+
+func crash(_ p: P, payload: [UInt8]) throws {
+ p.foo(data: payload).then { _ in
+ return Future<(D, [D])>()
+ }.then { (id, arr) in
+ p.foo(arr: arr, data: []).and(result: (id, arr))
+ // expected-error@-1 {{mbiguous reference to member 'foo(arr:data:)'}}
+ }.then { args0 in
+ let (parentID, args1) = args0
+ p.bar(root: parentID, from: p).and(args1)
+ }.whenFailure { _ in }
+}
diff --git a/test/SIL/Parser/apply_with_conformance.sil b/test/SIL/Parser/apply_with_conformance.sil
index 2aeafe8..85c600d 100644
--- a/test/SIL/Parser/apply_with_conformance.sil
+++ b/test/SIL/Parser/apply_with_conformance.sil
@@ -18,7 +18,7 @@
// test.S.foo (test.S)<A : test.P>(A) -> ()
sil @_TFV4test1S3foofS0_US_1P__FQ_T_ : $@convention(method) <T where T : P> (@in T, S) -> ()
-// CHECK-LABEL: define{{( protected)?}} swiftcc void @_TF4test3barFTVS_1SVS_1X_T_()
+// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @_TF4test3barFTVS_1SVS_1X_T_()
// CHECK: call
// test.bar (test.S, test.X) -> ()
sil [ossa] @_TF4test3barFTVS_1SVS_1X_T_ : $@convention(thin) (S, X) -> () {
diff --git a/test/attr/Inputs/dynamicReplacementA.swift b/test/attr/Inputs/dynamicReplacementA.swift
new file mode 100644
index 0000000..3572996
--- /dev/null
+++ b/test/attr/Inputs/dynamicReplacementA.swift
@@ -0,0 +1,2 @@
+public struct TheReplaceables {
+}
diff --git a/test/attr/Inputs/dynamicReplacementB.swift b/test/attr/Inputs/dynamicReplacementB.swift
new file mode 100644
index 0000000..7b2918b
--- /dev/null
+++ b/test/attr/Inputs/dynamicReplacementB.swift
@@ -0,0 +1,9 @@
+import A
+
+public extension TheReplaceables {
+ dynamic var property1: Int { return 0 }
+ dynamic var property2: Int { return 0 }
+
+ dynamic subscript (i: Int) -> Int { return 0 }
+ dynamic subscript (i: Int) -> String { return "" }
+}
diff --git a/test/attr/Inputs/dynamicReplacementC.swift b/test/attr/Inputs/dynamicReplacementC.swift
new file mode 100644
index 0000000..25b4a8f
--- /dev/null
+++ b/test/attr/Inputs/dynamicReplacementC.swift
@@ -0,0 +1,9 @@
+import A
+
+public extension TheReplaceables {
+ dynamic var property1: Int { return 0 }
+ dynamic var property2: String { return "" }
+
+ dynamic subscript (i: Int) -> Int { return 0 }
+ dynamic subscript (s: String) -> String { return "" }
+}
diff --git a/test/attr/dynamicReplacement.swift b/test/attr/dynamicReplacement.swift
new file mode 100644
index 0000000..48aaf28
--- /dev/null
+++ b/test/attr/dynamicReplacement.swift
@@ -0,0 +1,40 @@
+// RUN: %empty-directory(%t)
+// RUN: %target-swift-frontend -emit-module -swift-version 5 -enable-implicit-dynamic %S/Inputs/dynamicReplacementA.swift -o %t -module-name A
+// RUN: %target-swift-frontend -emit-module -swift-version 5 -enable-implicit-dynamic -c %S/Inputs/dynamicReplacementB.swift -o %t -I %t -module-name B
+// RUN: %target-swift-frontend -emit-module -swift-version 5 -enable-implicit-dynamic -c %S/Inputs/dynamicReplacementC.swift -o %t -I %t -module-name C
+// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-implicit-dynamic -I %t
+import A
+import B
+import C
+
+// rdar://problem/46737657: static properties
+struct StaticProperties {
+ dynamic var property: Int { return 1 }
+ dynamic static var property: Int { return 11 }
+}
+
+extension StaticProperties {
+ @_dynamicReplacement(for: property)
+ var replacement_property: Int { return 2 }
+}
+
+// Replacements involving different types.
+extension TheReplaceables {
+ @_dynamicReplacement(for: property1) // expected-error{{replaced accessor for 'property1' occurs in multiple places}}
+ var replace_property1: Int { return 0 }
+
+ @_dynamicReplacement(for: property2)
+ var replace_property2_int: Int { return 1 }
+
+ @_dynamicReplacement(for: property2)
+ var replace_property2_string: String { return "replaced" }
+
+ @_dynamicReplacement(for: subscript(_:)) // expected-error{{replaced accessor for 'subscript(_:)' occurs in multiple places}}
+ subscript (int_int i: Int) -> Int { return 0 }
+
+ @_dynamicReplacement(for: subscript(_:))
+ subscript (int_string i: Int) -> String { return "" }
+
+ @_dynamicReplacement(for: subscript(_:))
+ subscript (string_string i: String) -> String { return "" }
+}
diff --git a/validation-test/stdlib/Dictionary.swift b/validation-test/stdlib/Dictionary.swift
index 7659d65..2555100 100644
--- a/validation-test/stdlib/Dictionary.swift
+++ b/validation-test/stdlib/Dictionary.swift
@@ -2383,6 +2383,9 @@
}
DictionaryTestSuite.test("BridgedFromObjC.Nonverbatim.ImmutableDictionaryIsCopied") {
+ //some bridged NSDictionary operations on non-standard NSDictionary subclasses
+ //autorelease keys and values. Make sure the leak checker isn't confused
+ autoreleasepool {
let nsd: NSDictionary = CustomImmutableNSDictionary(_privateInit: ())
CustomImmutableNSDictionary.timesCopyWithZoneWasCalled = 0
@@ -2406,6 +2409,7 @@
_fixLifetime(nsd)
_fixLifetime(d)
_fixLifetime(bridgedBack)
+ }
}
@@ -3243,9 +3247,9 @@
}
DictionaryTestSuite.test("BridgedFromObjC.Nonverbatim.Generate_ParallelArray") {
-autoreleasepoolIfUnoptimizedReturnAutoreleased {
- // Add an autorelease pool because ParallelArrayDictionary autoreleases
- // values in objectForKey.
+ //some bridged NSDictionary operations on non-standard NSDictionary subclasses
+ //autorelease keys and values. Make sure the leak checker isn't confused
+autoreleasepool {
let d = getParallelArrayBridgedNonverbatimDictionary()
let identity1 = d._rawIdentifier()
@@ -5691,6 +5695,62 @@
d.remove(at: i)
}
+DictionaryTestSuite.test("BulkLoadingInitializer.Unique") {
+ for c in [0, 1, 2, 3, 5, 10, 25, 150] {
+ let d1 = Dictionary<TestKeyTy, TestEquatableValueTy>(
+ _unsafeUninitializedCapacity: c,
+ allowingDuplicates: false
+ ) { keys, values, count in
+ let k = keys.baseAddress!
+ let v = values.baseAddress!
+ for i in 0 ..< c {
+ (k + i).initialize(to: TestKeyTy(i))
+ (v + i).initialize(to: TestEquatableValueTy(i))
+ count += 1
+ }
+ }
+
+ let d2 = Dictionary(
+ uniqueKeysWithValues: (0..<c).map {
+ (TestKeyTy($0), TestEquatableValueTy($0))
+ })
+
+ for i in 0 ..< c {
+ expectEqual(TestEquatableValueTy(i), d1[TestKeyTy(i)])
+ }
+ expectEqual(d2, d1)
+ }
+}
+
+DictionaryTestSuite.test("BulkLoadingInitializer.Nonunique") {
+ for c in [0, 1, 2, 3, 5, 10, 25, 150] {
+ let d1 = Dictionary<TestKeyTy, TestEquatableValueTy>(
+ _unsafeUninitializedCapacity: c,
+ allowingDuplicates: true
+ ) { keys, values, count in
+ let k = keys.baseAddress!
+ let v = values.baseAddress!
+ for i in 0 ..< c {
+ (k + i).initialize(to: TestKeyTy(i / 2))
+ (v + i).initialize(to: TestEquatableValueTy(i / 2))
+ count += 1
+ }
+ }
+
+ let d2 = Dictionary(
+ (0 ..< c).map {
+ (TestKeyTy($0 / 2), TestEquatableValueTy($0 / 2))
+ },
+ uniquingKeysWith: { a, b in a })
+
+ expectEqual(d1.count, d2.count)
+ for i in 0 ..< c / 2 {
+ expectEqual(TestEquatableValueTy(i), d1[TestKeyTy(i)])
+ }
+ expectEqual(d2, d1)
+ }
+}
+
DictionaryTestSuite.setUp {
#if _runtime(_ObjC)
// Exercise ARC's autoreleased return value optimization in Foundation.