diff --git a/docs/SIL.rst b/docs/SIL.rst index 53ed7e97209fb..947f166123d33 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -5461,7 +5461,7 @@ unconditional_checked_cast_addr sil-type 'in' sil-operand 'to' sil-type 'in' sil-operand - unconditional_checked_cast_addr $A in %0 : $*@thick A to $B in $*@thick B + unconditional_checked_cast_addr $A in %0 : $*@thick A to $B in %1 : $*@thick B // $A and $B must be both addresses // %1 will be of type $*B // $A is destroyed during the conversion. There is no implicit copy. diff --git a/lib/SILGen/ManagedValue.h b/lib/SILGen/ManagedValue.h index f96525d8a223e..1187c82593437 100644 --- a/lib/SILGen/ManagedValue.h +++ b/lib/SILGen/ManagedValue.h @@ -121,6 +121,13 @@ class ManagedValue { return ManagedValue::forOwnedObjectRValue(value, cleanup); } + static ManagedValue + forExclusivelyBorrowedOwnedObjectRValue(SILValue value, + CleanupHandle cleanup) { + assert(value->getType().isObject()); + return ManagedValue::forOwnedObjectRValue(value, cleanup); + } + /// Create a managed value for a +0 borrowed non-trivial rvalue object. static ManagedValue forBorrowedObjectRValue(SILValue value) { diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 8028e6e391d89..7d1798d5fc803 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -1243,7 +1243,7 @@ class SILGenApply : public Lowering::ExprVisitor { if (superMV.getValue() != SGF.InitDelegationSelf.getValue()) { SILValue underlyingSelf = SGF.InitDelegationSelf.getValue(); SGF.InitDelegationSelf = ManagedValue::forUnmanaged(underlyingSelf); - CleanupHandle newWriteback = SGF.enterDelegateInitSelfWritebackCleanup( + CleanupHandle newWriteback = SGF.enterOwnedValueWritebackCleanup( SGF.InitDelegationLoc.getValue(), SGF.InitDelegationSelfBox, superMV.forward(SGF)); SGF.SuperInitDelegationSelf = diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 633949e32d568..58f6c3396929d 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -717,11 +717,11 @@ SILValue SILGenFunction::emitEmptyTuple(SILLocation loc) { namespace { -/// This is a simple cleanup class that is only meant to help with delegating -/// initializers. Specifically, if the delegating initializer fails to consume -/// the loaded self, we want to write back self into the slot to ensure that -/// ownership is preserved. -struct DelegateInitSelfWritebackCleanup : Cleanup { +/// This is a simple cleanup class that at the end of a lexical scope consumes +/// an owned value by writing it back to memory. The user can forward this +/// cleanup to take ownership of the value and thus prevent it form being +/// written back. +struct OwnedValueWritebackCleanup final : Cleanup { /// We store our own loc so that we can ensure that DI ignores our writeback. SILLocation loc; @@ -729,8 +729,8 @@ struct DelegateInitSelfWritebackCleanup : Cleanup { SILValue lvalueAddress; SILValue value; - DelegateInitSelfWritebackCleanup(SILLocation loc, SILValue lvalueAddress, - SILValue value) + OwnedValueWritebackCleanup(SILLocation loc, SILValue lvalueAddress, + SILValue value) : loc(loc), lvalueAddress(lvalueAddress), value(value) {} void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind) override { @@ -749,14 +749,13 @@ struct DelegateInitSelfWritebackCleanup : Cleanup { lvalueObjTy); } - auto &lowering = SGF.B.getTypeLowering(lvalueAddress->getType()); - lowering.emitStore(SGF.B, loc, valueToStore, lvalueAddress, - StoreOwnershipQualifier::Init); + SGF.B.emitStoreValueOperation(loc, valueToStore, lvalueAddress, + StoreOwnershipQualifier::Init); } void dump(SILGenFunction &) const override { #ifndef NDEBUG - llvm::errs() << "SimpleWritebackCleanup " + llvm::errs() << "OwnedValueWritebackCleanup " << "State:" << getState() << "\n" << "lvalueAddress:" << lvalueAddress << "value:" << value << "\n"; @@ -766,10 +765,9 @@ struct DelegateInitSelfWritebackCleanup : Cleanup { } // end anonymous namespace -CleanupHandle SILGenFunction::enterDelegateInitSelfWritebackCleanup( +CleanupHandle SILGenFunction::enterOwnedValueWritebackCleanup( SILLocation loc, SILValue address, SILValue newValue) { - Cleanups.pushCleanup(loc, address, - newValue); + Cleanups.pushCleanup(loc, address, newValue); return Cleanups.getTopCleanup(); } @@ -815,8 +813,8 @@ RValue SILGenFunction::emitRValueForSelfInDelegationInit(SILLocation loc, // Forward our initial value for init delegation self and create a new // cleanup that performs a writeback at the end of lexical scope if our // value is not consumed. - InitDelegationSelf = ManagedValue( - self, enterDelegateInitSelfWritebackCleanup(*InitDelegationLoc, addr, self)); + InitDelegationSelf = ManagedValue::forExclusivelyBorrowedOwnedObjectRValue( + self, enterOwnedValueWritebackCleanup(*InitDelegationLoc, addr, self)); InitDelegationSelfBox = addr; return RValue(*this, loc, refType, InitDelegationSelf); } diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 305a2a12086d2..854fa87b50c0e 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -1188,9 +1188,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction CleanupHandle enterDeallocateUninitializedArrayCleanup(SILValue array); void emitUninitializedArrayDeallocation(SILLocation loc, SILValue array); - CleanupHandle enterDelegateInitSelfWritebackCleanup(SILLocation loc, - SILValue address, - SILValue newValue); + CleanupHandle enterOwnedValueWritebackCleanup(SILLocation loc, + SILValue address, + SILValue newValue); SILValue emitConversionToSemanticRValue(SILLocation loc, SILValue value, const TypeLowering &valueTL); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 9987fee0bbb70..a088fc1fbc233 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -7683,10 +7683,30 @@ ConstraintSystem::simplifyKeyPathConstraint( return true; }; - // We have a hole, the solver can't infer the key path type. So let's - // just assume this is solved. - if (shouldAttemptFixes() && keyPathTy->isHole()) { - return SolutionKind::Solved; + // If we have a hole somewhere in the key path, the solver won't be able to + // infer the key path type. So let's just assume this is solved. + if (shouldAttemptFixes()) { + if (keyPathTy->isHole()) + return SolutionKind::Solved; + + // If the root type has been bound to a hole, we cannot infer it. + if (getFixedTypeRecursive(rootTy, /*wantRValue*/ true)->isHole()) + return SolutionKind::Solved; + + // If we have e.g a missing member somewhere, a component type variable + // will have been marked as a potential hole. + // FIXME: This relies on the fact that we only mark an overload type + // variable as a potential hole once we've added a corresponding fix. We + // can't use 'isHole' instead, as that doesn't handle cases where the + // overload type variable gets bound to another type from the context rather + // than a hole. We need to come up with a better way of handling the + // relationship between key paths and overloads. + if (llvm::any_of(componentTypeVars, [&](TypeVariableType *tv) { + return tv->getImpl().getLocator()->isForKeyPathComponent() && + tv->getImpl().canBindToHole(); + })) { + return SolutionKind::Solved; + } } // If we're fixed to a bound generic type, trying harvesting context from it. @@ -7737,34 +7757,11 @@ ConstraintSystem::simplifyKeyPathConstraint( // to determine whether the result will be a function type vs BGT KeyPath // type, so continue through components to create new constraint at the // end. - if (!overload || anyComponentsUnresolved) { + if (!overload) { if (flags.contains(TMF_GenerateConstraints)) { anyComponentsUnresolved = true; continue; } - - if (shouldAttemptFixes()) { - auto typeVar = - llvm::find_if(componentTypeVars, [&](TypeVariableType *typeVar) { - auto *locator = typeVar->getImpl().getLocator(); - auto elt = locator->findLast(); - return elt && elt->getIndex() == i; - }); - - // If one of the components haven't been resolved, let's check - // whether it has been determined to be a "hole" and if so, - // let's allow component validation to contiunue. - // - // This helps to, for example, diagnose problems with missing - // members used as part of a key path. - if (typeVar != componentTypeVars.end() && - (*typeVar)->getImpl().canBindToHole()) { - anyComponentsUnresolved = true; - capability = ReadOnly; - continue; - } - } - return SolutionKind::Unsolved; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9ca9496f2ae0f..e5eb7ec7db0e5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -120,26 +120,26 @@ set(LIT "${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py") # instead of by the path, which is good for CI. In this mode lit also updates # the mtime on the failed tests, which makes them run first on the # consecutive execution, which makes local builds fail faster. -set(SWIFT_TEST_EXTRA_ARGS "--incremental") +set(SWIFT_LIT_ARGS "--incremental" CACHE STRING "Arguments to pass to lit") if(NOT SWIFT_INCLUDE_TOOLS) - list(APPEND SWIFT_TEST_EXTRA_ARGS + list(APPEND SWIFT_LIT_ARGS "--path=${SWIFT_NATIVE_LLVM_TOOLS_PATH}" "--path=${SWIFT_NATIVE_CLANG_TOOLS_PATH}" "--path=${SWIFT_NATIVE_SWIFT_TOOLS_PATH}") if(SWIFT_BUILD_STDLIB) - list(APPEND SWIFT_TEST_EXTRA_ARGS + list(APPEND SWIFT_LIT_ARGS "--param" "test_resource_dir=${SWIFTLIB_DIR}") endif() endif() option(SWIFT_TEST_USE_LEAKS "Run Swift stdlib tests under leaks" FALSE) if (SWIFT_TEST_USE_LEAKS) - list(APPEND SWIFT_TEST_EXTRA_ARGS "--param" "leaks-all") + list(APPEND SWIFT_LIT_ARGS "--param" "leaks-all") endif() if(NOT CMAKE_CFG_INTDIR STREQUAL ".") - list(APPEND SWIFT_TEST_EXTRA_ARGS + list(APPEND SWIFT_LIT_ARGS "--param" "build_mode=${CMAKE_CFG_INTDIR}") endif() @@ -332,7 +332,7 @@ _Block_release(void) { }\n") COMMENT "Uploading stdlib") foreach(test_mode ${TEST_MODES}) - set(LIT_ARGS "${SWIFT_TEST_EXTRA_ARGS} ${LLVM_LIT_ARGS}") + set(LIT_ARGS "${SWIFT_LIT_ARGS} ${LLVM_LIT_ARGS}") separate_arguments(LIT_ARGS) if(NOT SWIFT_BUILD_STDLIB AND NOT SWIFT_PATH_TO_EXTERNAL_STDLIB_BUILD) diff --git a/test/Constraints/rdar62201037.swift b/test/Constraints/rdar62201037.swift new file mode 100644 index 0000000000000..b0b5915873e8e --- /dev/null +++ b/test/Constraints/rdar62201037.swift @@ -0,0 +1,31 @@ +// RUN: %target-swift-frontend %s -verify -emit-sil -o /dev/null + +struct R { + var str: String? +} + +func map(e: (A) -> B) -> () -> R { + fatalError() +} +func map(_ : (A) -> B) -> (A?) -> B? { + fatalError() +} + +infix operator |> +func |> (g: A, h: (A) -> B) -> B { h(g) } + +infix operator ^^^ +func ^^^ (j: ((B) -> C) -> A, k: String) {} + +extension WritableKeyPath { + static func ^^^ (l: WritableKeyPath, m: Value) -> (Root) -> Root { + fatalError() + } +} + +func foo(_ s: String, _ rt: R?) -> String? { + return rt.flatMap { _ in + rt |> map(\.str ^^^ s) + } + .flatMap(\.str) +} diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index 67e64f8f434cd..c3a7b9e9e9a07 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -895,11 +895,40 @@ struct SR_12290 { func testKeyPathHole() { _ = \.x // expected-error {{cannot infer key path type from context; consider explicitly specifying a root type}} {{8-8=<#Root#>}} + _ = \.x.y // expected-error {{cannot infer key path type from context; consider explicitly specifying a root type}} {{8-8=<#Root#>}} + let _ : AnyKeyPath = \.x // expected-error@-1 {{'AnyKeyPath' does not provide enough context for root type to be inferred; consider explicitly specifying a root type}} {{25-25=<#Root#>}} + let _ : AnyKeyPath = \.x.y + // expected-error@-1 {{'AnyKeyPath' does not provide enough context for root type to be inferred; consider explicitly specifying a root type}} {{25-25=<#Root#>}} func f(_ i: Int) {} f(\.x) // expected-error {{cannot infer key path type from context; consider explicitly specifying a root type}} {{6-6=<#Root#>}} + f(\.x.y) // expected-error {{cannot infer key path type from context; consider explicitly specifying a root type}} {{6-6=<#Root#>}} + + // FIXME(SR-12827): Instead of "generic parameter 'T' could not be inferred", + // we should offer the same diagnostic as above. + func provideValueButNotRoot(_ fn: (T) -> String) {} // expected-note 2{{in call to function 'provideValueButNotRoot'}} + provideValueButNotRoot(\.x) // expected-error {{generic parameter 'T' could not be inferred}} + provideValueButNotRoot(\.x.y) // expected-error {{generic parameter 'T' could not be inferred}} + provideValueButNotRoot(\String.foo) // expected-error {{value of type 'String' has no member 'foo'}} + + func provideKPValueButNotRoot(_ kp: KeyPath) {} // expected-note 3{{in call to function 'provideKPValueButNotRoot'}} + provideKPValueButNotRoot(\.x) // expected-error {{generic parameter 'T' could not be inferred}} + provideKPValueButNotRoot(\.x.y) // expected-error {{generic parameter 'T' could not be inferred}} + provideKPValueButNotRoot(\String.foo) + // expected-error@-1 {{value of type 'String' has no member 'foo'}} + // expected-error@-2 {{generic parameter 'T' could not be inferred}} +} + +func testMissingMember() { + let _: KeyPath = \.foo // expected-error {{value of type 'String' has no member 'foo'}} + let _: KeyPath = \.foo.bar // expected-error {{value of type 'String' has no member 'foo'}} + + let _: PartialKeyPath = \.foo // expected-error {{value of type 'String' has no member 'foo'}} + let _: PartialKeyPath = \.foo.bar // expected-error {{value of type 'String' has no member 'foo'}} + + _ = \String.x.y // expected-error {{value of type 'String' has no member 'x'}} } func testSyntaxErrors() { // expected-note{{}} diff --git a/validation-test/Sanitizers/fuzzer.swift b/validation-test/Sanitizers/fuzzer.swift index 93c8a2f799166..21c0dc463d363 100644 --- a/validation-test/Sanitizers/fuzzer.swift +++ b/validation-test/Sanitizers/fuzzer.swift @@ -17,9 +17,10 @@ import MSVCRT #endif @_cdecl("LLVMFuzzerTestOneInput") -public func test(_ start: UnsafeRawPointer, _ count: Int) -> CInt { +public func testHexDigits(_ start: UnsafeRawPointer, _ count: Int) -> CInt { let bytes = UnsafeRawBufferPointer(start: start, count: count) - if bytes.starts(with: "ABC".utf8) { + let string = String(decoding: bytes, as: Unicode.UTF8.self) + if let number = Int(string, radix: 16), (0x10...0xFF).contains(number) { print("Crash!") fflush(stdout) exit(EXIT_FAILURE)