From 616097146ec571c223a2804da278abcba30fa2ab Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Tue, 2 Aug 2016 19:39:44 -0700 Subject: [PATCH 01/27] [utils] Add support for swift-3.0-branch --- utils/update-checkout-config.json | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/utils/update-checkout-config.json b/utils/update-checkout-config.json index 0582edb1cad22..7718442f64087 100644 --- a/utils/update-checkout-config.json +++ b/utils/update-checkout-config.json @@ -155,7 +155,7 @@ } }, "swift-3.0-preview-5" : { - "aliases": ["swift-3.0-branch", "swift-3.0-preview-5-branch"], + "aliases": ["swift-3.0-preview-5-branch"], "repos": { "llvm": "swift-3.0-branch", "clang": "swift-3.0-branch", @@ -171,6 +171,24 @@ "swift-integration-tests": "swift-3.0-preview-5-branch", "swift-xcode-playground-support": "swift-3.0-preview-5-branch" } + }, + "swift-3.0-branch" : { + "aliases": ["swift-3.0-branch"], + "repos": { + "llvm": "swift-3.0-branch", + "clang": "swift-3.0-branch", + "swift": "swift-3.0-branch", + "lldb": "swift-3.0-branch", + "cmark": "swift-3.0-branch", + "llbuild": "swift-3.0-branch", + "swiftpm": "swift-3.0-branch", + "compiler-rt": "swift-3.0-branch", + "swift-corelibs-xctest": "swift-3.0-branch", + "swift-corelibs-foundation": "swift-3.0-branch", + "swift-corelibs-libdispatch": "swift-3.0-branch", + "swift-integration-tests": "swift-3.0-branch", + "swift-xcode-playground-support": "swift-3.0-branch" + } } } } From 9cf4f04746f7bcd074f3c95d8581a6da7420d94f Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Tue, 2 Aug 2016 13:34:28 -0700 Subject: [PATCH 02/27] [noescape-by-default] Better diagnostics for parameters Issue better diagnostics, along with notes and fixits, for the common case of using an implicitly non-escaping parameter of function type in a context expecting an @escaping closure. Provides even more specific diagnostics for common scenarios such as passing to another function or assignment. --- include/swift/AST/DiagnosticsSema.def | 9 ++++ lib/Sema/CSDiag.cpp | 61 +++++++++++++++++++++++++++ test/attr/attr_escaping.swift | 31 ++++++++++++++ test/attr/attr_noescape.swift | 7 +-- 4 files changed, 105 insertions(+), 3 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 63f297b3ff4f8..da88ea71cd8cf 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2310,6 +2310,15 @@ ERROR(closure_noescape_use,none, ERROR(decl_closure_noescape_use,none, "declaration closing over non-escaping parameter %0 may allow it to escape", (Identifier)) +ERROR(passing_noescape_to_escaping,none, + "passing non-escaping parameter %0 to function expecting an @escaping closure", + (Identifier)) +ERROR(assigning_noescape_to_escaping,none, + "assigning non-escaping parameter %0 to an @escaping closure", + (Identifier)) +ERROR(general_noescape_to_escaping,none, + "using non-escaping parameter %0 in a context expecting an @escaping closure", + (Identifier)) ERROR(capture_across_type_decl,none, "%0 declaration cannot close over value %1 defined in outer scope", diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 60491e8c0bfee..b0d85fbcbb459 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -3593,6 +3593,62 @@ addTypeCoerceFixit(InFlightDiagnostic &diag, ConstraintSystem *CS, return false; } +/// Try to diagnose common errors involving implicitly non-escaping parameters +/// of function type, giving more specific and simpler diagnostics, attaching +/// notes on the parameter, and offering fixits to insert @escaping. Returns +/// true if it detects and issues an error, false if it does nothing. +static bool tryDiagnoseNonEscapingParameterToEscaping(Expr *expr, Type srcType, + Type dstType, + ConstraintSystem *CS) { + assert(expr && CS); + // Need to be referencing a parameter of function type + auto declRef = dyn_cast(expr); + if (!declRef || !isa(declRef->getDecl()) || + !declRef->getType()->is()) + return false; + + // Must be from non-escaping function to escaping function + auto srcFT = srcType->getAs(); + auto destFT = dstType->getAs(); + if (!srcFT || !destFT || !srcFT->isNoEscape() || destFT->isNoEscape()) + return false; + + // Function types must be equivalent modulo @escaping + if (destFT != srcFT->withExtInfo(srcFT->getExtInfo().withNoEscape(false))) + return false; + + // Pick a specific diagnostic for the specific use + auto paramDecl = cast(declRef->getDecl()); + switch (CS->getContextualTypePurpose()) { + case CTP_CallArgument: + CS->TC.diagnose(declRef->getLoc(), diag::passing_noescape_to_escaping, + paramDecl->getName()); + break; + case CTP_AssignSource: + CS->TC.diagnose(declRef->getLoc(), diag::assigning_noescape_to_escaping, + paramDecl->getName()); + break; + + default: + CS->TC.diagnose(declRef->getLoc(), diag::general_noescape_to_escaping, + paramDecl->getName()); + break; + } + + // Give a note and fixit + InFlightDiagnostic note = CS->TC.diagnose( + paramDecl->getLoc(), srcFT->isAutoClosure() ? diag::noescape_autoclosure + : diag::noescape_parameter, + paramDecl->getName()); + + if (!srcFT->isAutoClosure()) { + note.fixItInsert(paramDecl->getTypeLoc().getSourceRange().Start, + "@escaping "); + } // TODO: add in a fixit for autoclosure + + return true; +} + bool FailureDiagnosis::diagnoseContextualConversionError() { // If the constraint system has a contextual type, then we can test to see if // this is the problem that prevents us from solving the system. @@ -3855,6 +3911,11 @@ bool FailureDiagnosis::diagnoseContextualConversionError() { } } + // Try for better/more specific diagnostics for non-escaping to @escaping + if (tryDiagnoseNonEscapingParameterToEscaping(expr, exprType, contextualType, + CS)) + return true; + // When complaining about conversion to a protocol type, complain about // conformance instead of "conversion". if (contextualType->is() || diff --git a/test/attr/attr_escaping.swift b/test/attr/attr_escaping.swift index 78739fe7ffda4..b17b3055e4ed7 100644 --- a/test/attr/attr_escaping.swift +++ b/test/attr/attr_escaping.swift @@ -7,3 +7,34 @@ func wrongParamType(a: @escaping Int) {} // expected-error {{@escaping attribute func conflictingAttrs(_ fn: @noescape @escaping () -> Int) {} // expected-error {{@escaping conflicts with @noescape}} func takesEscaping(_ fn: @escaping () -> Int) {} // ok + +func callEscapingWithNoEscape(_ fn: () -> Int) { + // expected-note@-1{{parameter 'fn' is implicitly non-escaping}} {{37-37=@escaping }} + // expected-note@-2{{parameter 'fn' is implicitly non-escaping}} {{37-37=@escaping }} + + takesEscaping(fn) // expected-error{{passing non-escaping parameter 'fn' to function expecting an @escaping closure}} + let _ = fn // expected-error{{non-escaping parameter 'fn' may only be called}} +} + + +struct StoresClosure { + var closure : () -> Int + init(_ fn: () -> Int) { + // expected-note@-1{{parameter 'fn' is implicitly non-escaping}} {{14-14=@escaping }} + + closure = fn // expected-error{{assigning non-escaping parameter 'fn' to an @escaping closure}} + } + + func arrayPack(_ fn: () -> Int) -> [()->Int] { + // expected-note@-1{{parameter 'fn' is implicitly non-escaping}} {{24-24=@escaping }} + + return [fn] // expected-error{{using non-escaping parameter 'fn' in a context expecting an @escaping closure}} + } + + func arrayPack(_ fn: @escaping () -> Int, _ fn2 : () -> Int) -> [()->Int] { + // expected-note@-1{{parameter 'fn2' is implicitly non-escaping}} {{53-53=@escaping }} + + return [fn, fn2] // expected-error{{using non-escaping parameter 'fn2' in a context expecting an @escaping closure}} + } +} + diff --git a/test/attr/attr_noescape.swift b/test/attr/attr_noescape.swift index fd9f6a942a2ff..85cf0cfa1fcd4 100644 --- a/test/attr/attr_noescape.swift +++ b/test/attr/attr_noescape.swift @@ -13,6 +13,7 @@ func takesNoEscapeClosure(_ fn : () -> Int) { // expected-note@-1{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} // expected-note@-2{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} // expected-note@-3{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} + // expected-note@-4{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }} takesNoEscapeClosure { 4 } // ok _ = fn() // ok @@ -32,7 +33,7 @@ func takesNoEscapeClosure(_ fn : () -> Int) { takesNoEscapeClosure(fn) // ok - doesEscape(fn) // expected-error {{invalid conversion from non-escaping function of type '() -> Int' to potentially escaping function type '() -> Int'}} + doesEscape(fn) // expected-error {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}} takesGenericClosure(4, fn) // ok takesGenericClosure(4) { fn() } // ok. } @@ -254,8 +255,8 @@ func curriedFlatMap2(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] { func bad(_ a : @escaping (Int)-> Int) -> Int { return 42 } func escapeNoEscapeResult(_ x: [Int]) -> (@noescape (Int) -> Int) -> Int { - return { f in - bad(f) // expected-error {{invalid conversion from non-escaping function of type '(Int) -> Int' to potentially escaping function type '(Int) -> Int'}} + return { f in // expected-note{{parameter 'f' is implicitly non-escaping}} + bad(f) // expected-error {{passing non-escaping parameter 'f' to function expecting an @escaping closure}} } } From 224613ae1573f77b5643244b9c447a12048e340f Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Tue, 2 Aug 2016 14:52:43 -0700 Subject: [PATCH 03/27] [noescape-by-default] Expand useful diagnostics to more cases Extends the more useful diagnostics for non-escaping function parameters used in @escaping contexts to apply to all functions that are equivalent modulo ExtInfo. --- lib/Sema/CSDiag.cpp | 4 ++-- test/attr/attr_autoclosure.swift | 8 ++++++-- test/attr/attr_escaping.swift | 8 ++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index b0d85fbcbb459..369ba78cd713c 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -3613,8 +3613,8 @@ static bool tryDiagnoseNonEscapingParameterToEscaping(Expr *expr, Type srcType, if (!srcFT || !destFT || !srcFT->isNoEscape() || destFT->isNoEscape()) return false; - // Function types must be equivalent modulo @escaping - if (destFT != srcFT->withExtInfo(srcFT->getExtInfo().withNoEscape(false))) + // Function types must be equivalent modulo @escaping, @convention, etc. + if (destFT != srcFT->withExtInfo(destFT->getExtInfo())) return false; // Pick a specific diagnostic for the specific use diff --git a/test/attr/attr_autoclosure.swift b/test/attr/attr_autoclosure.swift index 1323ebfb74175..5655b0f70c222 100644 --- a/test/attr/attr_autoclosure.swift +++ b/test/attr/attr_autoclosure.swift @@ -55,7 +55,9 @@ func overloadedEach(_ source: P, _ closure: @escaping () -> ()) { struct S : P2 { typealias Element = Int func each(_ closure: @autoclosure () -> ()) { - overloadedEach(self, closure) // expected-error {{invalid conversion from non-escaping function of type '@autoclosure () -> ()' to potentially escaping function type '() -> ()'}} + // expected-note@-1{{parameter 'closure' is implicitly non-escaping because it was declared @autoclosure}} + + overloadedEach(self, closure) // expected-error {{passing non-escaping parameter 'closure' to function expecting an @escaping closure}} } } @@ -87,7 +89,9 @@ class Sub : Super { func func12_sink(_ x: @escaping () -> Int) { } func func12a(_ x: @autoclosure () -> Int) { - func12_sink(x) // expected-error{{invalid conversion from non-escaping function of type '@autoclosure () -> Int' to potentially escaping function type '() -> Int'}} + // expected-note@-1{{parameter 'x' is implicitly non-escaping because it was declared @autoclosure}} + + func12_sink(x) // expected-error {{passing non-escaping parameter 'x' to function expecting an @escaping closure}} } func func12b(_ x: @autoclosure(escaping) () -> Int) { func12_sink(x) diff --git a/test/attr/attr_escaping.swift b/test/attr/attr_escaping.swift index b17b3055e4ed7..7bdc3cc06fd45 100644 --- a/test/attr/attr_escaping.swift +++ b/test/attr/attr_escaping.swift @@ -38,3 +38,11 @@ struct StoresClosure { } } +func takesEscapingBlock(_ fn: @escaping @convention(block) () -> Void) { + fn() +} +func callEscapingWithNoEscapeBlock(_ fn: () -> Void) { + // expected-note@-1{{parameter 'fn' is implicitly non-escaping}} {{42-42=@escaping }} + + takesEscapingBlock(fn) // expected-error{{passing non-escaping parameter 'fn' to function expecting an @escaping closure}} +} \ No newline at end of file From cc98458fb8d107f084fa1511bf40729450b67dd2 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Tue, 2 Aug 2016 20:59:55 -0700 Subject: [PATCH 04/27] [noescape-by-default] incorporate Doug's feedback Added sugared test case --- lib/Sema/CSDiag.cpp | 2 +- test/attr/attr_escaping.swift | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 369ba78cd713c..1c0960f026bd4 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -3614,7 +3614,7 @@ static bool tryDiagnoseNonEscapingParameterToEscaping(Expr *expr, Type srcType, return false; // Function types must be equivalent modulo @escaping, @convention, etc. - if (destFT != srcFT->withExtInfo(destFT->getExtInfo())) + if (!destFT->isEqual(srcFT->withExtInfo(destFT->getExtInfo()))) return false; // Pick a specific diagnostic for the specific use diff --git a/test/attr/attr_escaping.swift b/test/attr/attr_escaping.swift index 7bdc3cc06fd45..c9f2af6ea816a 100644 --- a/test/attr/attr_escaping.swift +++ b/test/attr/attr_escaping.swift @@ -16,6 +16,12 @@ func callEscapingWithNoEscape(_ fn: () -> Int) { let _ = fn // expected-error{{non-escaping parameter 'fn' may only be called}} } +typealias IntSugar = Int +func callSugared(_ fn: () -> IntSugar) { + // expected-note@-1{{parameter 'fn' is implicitly non-escaping}} {{24-24=@escaping }} + + takesEscaping(fn) // expected-error{{passing non-escaping parameter 'fn' to function expecting an @escaping closure}} +} struct StoresClosure { var closure : () -> Int From adfb6ec443c73db215ed1449b6902b0fb6371dd8 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 2 Aug 2016 13:26:11 -0700 Subject: [PATCH 05/27] Count tail-padding in the size of imported C types. This allows us to freely pass the address of a variable of such types to a C function without worrying about it overwriting other things if e.g. it memcpy's sizeof(T) bytes and thus clobbers the otherwise-non-existent tail padding. There are ways to get this optimization back, but they require things like materializing to a temporary instead of passing the address directly. We haven't done that work yet, so we don't get to take advantage of it. rdar://26828018 --- lib/IRGen/GenStruct.cpp | 23 ++++++++------- test/IRGen/c_layout.sil | 28 +++++++++---------- .../typeref_decoding_imported.swift | 4 +-- .../typeref_lowering_imported.swift | 12 ++++---- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/lib/IRGen/GenStruct.cpp b/lib/IRGen/GenStruct.cpp index b17f2f17b257e..83934b42e7db2 100644 --- a/lib/IRGen/GenStruct.cpp +++ b/lib/IRGen/GenStruct.cpp @@ -579,7 +579,6 @@ class ClangRecordLowering { const clang::ASTContext &ClangContext; const clang::ASTRecordLayout &ClangLayout; const Size TotalStride; - Size TotalSize; const Alignment TotalAlignment; SpareBitVector SpareBits; @@ -595,9 +594,8 @@ class ClangRecordLowering { ClangDecl(clangDecl), ClangContext(clangDecl->getASTContext()), ClangLayout(ClangContext.getASTRecordLayout(clangDecl)), TotalStride(Size(ClangLayout.getSize().getQuantity())), - TotalSize(TotalStride), TotalAlignment(Alignment(ClangLayout.getAlignment().getQuantity())) { - SpareBits.reserve(TotalSize.getValue() * 8); + SpareBits.reserve(TotalStride.getValue() * 8); } void collectRecordFields() { @@ -606,17 +604,12 @@ class ClangRecordLowering { } else { collectStructFields(); } - - // Lots of layout will get screwed up if our structure claims more - // storage than we allocated to it. - assert(NextOffset == TotalSize && NextOffset <= TotalStride); - assert(TotalSize.roundUpToAlignment(TotalAlignment) == TotalStride); } const TypeInfo *createTypeInfo(llvm::StructType *llvmType) { llvmType->setBody(LLVMFields, /*packed*/ true); return ClangRecordTypeInfo::create(FieldInfos, NextExplosionIndex, - llvmType, TotalSize, + llvmType, TotalStride, std::move(SpareBits), TotalAlignment, ClangDecl); } @@ -624,7 +617,7 @@ class ClangRecordLowering { private: /// Collect all the fields of a union. void collectUnionFields() { - addOpaqueField(Size(0), TotalSize); + addOpaqueField(Size(0), TotalStride); } static bool isImportOfClangField(VarDecl *swiftField, @@ -689,8 +682,14 @@ class ClangRecordLowering { assert(sfi == sfe && "more Swift fields than there were Clang fields?"); - // Treat this as the end of the value size. - TotalSize = NextOffset; + // We never take advantage of tail padding, because that would prevent + // us from passing the address of the object off to C, which is a pretty + // likely scenario for imported C types. + assert(NextOffset <= TotalStride); + assert(SpareBits.size() <= TotalStride.getValueInBits()); + if (NextOffset < TotalStride) { + addPaddingField(TotalStride); + } } /// Place the next struct field at its appropriate offset. diff --git a/test/IRGen/c_layout.sil b/test/IRGen/c_layout.sil index 4fc0fa4ecf853..297d861d3d995 100644 --- a/test/IRGen/c_layout.sil +++ b/test/IRGen/c_layout.sil @@ -7,8 +7,8 @@ import Swift // TODO: Provide tests for other architectures -// CHECK-x86_64: %VSC11BitfieldOne = type <{ %Vs6UInt32, %VSC6Nested, [1 x i8], [4 x i8], [4 x i8], %Sf, [1 x i8], [7 x i8], %Vs6UInt64, %Vs6UInt32 }> -// CHECK-x86_64: %VSC6Nested = type <{ %Sf, [3 x i8] }> +// CHECK-x86_64: %VSC11BitfieldOne = type <{ %Vs6UInt32, %VSC6Nested, [4 x i8], [4 x i8], %Sf, [1 x i8], [7 x i8], %Vs6UInt64, %Vs6UInt32, [4 x i8] }> +// CHECK-x86_64: %VSC6Nested = type <{ %Sf, [3 x i8], [1 x i8] }> // CHECK-x86_64: %VSC26BitfieldSeparatorReference = type [[BITFIELD_SEP_TYPE:<{ %Vs5UInt8, \[3 x i8\], %Vs5UInt8 }>]] // CHECK-x86_64: %VSC25BitfieldSeparatorSameName = type [[BITFIELD_SEP_TYPE]] @@ -42,22 +42,22 @@ bb0: // CHECK-x86_64: [[ADDR_B_YZ:%.*]] = getelementptr inbounds %VSC6Nested, %VSC6Nested* [[ADDR_B]], i32 0, i32 1 // CHECK-x86_64: [[ADDR_B_YZ_1:%.*]] = bitcast [3 x i8]* [[ADDR_B_YZ]] to i24* // CHECK-x86_64: [[B_YZ:%.*]] = load i24, i24* [[ADDR_B_YZ_1]], align 4 -// CHECK-x86_64: [[ADDR_CDE:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[RESULT]], i32 0, i32 3 +// CHECK-x86_64: [[ADDR_CDE:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[RESULT]], i32 0, i32 2 // CHECK-x86_64: [[ADDR_CDE_1:%.*]] = bitcast [4 x i8]* [[ADDR_CDE]] to i32* // CHECK-x86_64: [[CDE:%.*]] = load i32, i32* [[ADDR_CDE_1]], align 4 -// CHECK-x86_64: [[ADDR_FGH:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[RESULT]], i32 0, i32 4 +// CHECK-x86_64: [[ADDR_FGH:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[RESULT]], i32 0, i32 3 // CHECK-x86_64: [[ADDR_FGH_1:%.*]] = bitcast [4 x i8]* [[ADDR_FGH]] to i32* // CHECK-x86_64: [[FGH:%.*]] = load i32, i32* [[ADDR_FGH_1]], align 8 -// CHECK-x86_64: [[ADDR_I:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[RESULT]], i32 0, i32 5 +// CHECK-x86_64: [[ADDR_I:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[RESULT]], i32 0, i32 4 // CHECK-x86_64: [[ADDR_I_V:%.*]] = getelementptr inbounds %Sf, %Sf* [[ADDR_I]], i32 0, i32 0 // CHECK-x86_64: [[I:%.*]] = load float, float* [[ADDR_I_V]], align 4 -// CHECK-x86_64: [[ADDR_JK:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[RESULT]], i32 0, i32 6 +// CHECK-x86_64: [[ADDR_JK:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[RESULT]], i32 0, i32 5 // CHECK-x86_64: [[ADDR_JK_1:%.*]] = bitcast [1 x i8]* [[ADDR_JK]] to i8* // CHECK-x86_64: [[JK:%.*]] = load i8, i8* [[ADDR_JK_1]], align 8 -// CHECK-x86_64: [[ADDR_L:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[RESULT]], i32 0, i32 8 +// CHECK-x86_64: [[ADDR_L:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[RESULT]], i32 0, i32 7 // CHECK-x86_64: [[ADDR_L_V:%.*]] = getelementptr inbounds %Vs6UInt64, %Vs6UInt64* [[ADDR_L]], i32 0, i32 0 // CHECK-x86_64: [[L:%.*]] = load i64, i64* [[ADDR_L_V]], align 8 -// CHECK-x86_64: [[ADDR_M:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[RESULT]], i32 0, i32 9 +// CHECK-x86_64: [[ADDR_M:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[RESULT]], i32 0, i32 8 // CHECK-x86_64: [[ADDR_M_V:%.*]] = getelementptr inbounds %Vs6UInt32, %Vs6UInt32* [[ADDR_M]], i32 0, i32 0 // CHECK-x86_64: [[M:%.*]] = load i32, i32* [[ADDR_M_V]], align 8 // Put all of the values into the indirect argument and make the second call. @@ -71,22 +71,22 @@ bb0: // CHECK-x86_64: [[ADDR_B_YZ:%.*]] = getelementptr inbounds %VSC6Nested, %VSC6Nested* [[ADDR_B]], i32 0, i32 1 // CHECK-x86_64: [[ADDR_B_YZ_1:%.*]] = bitcast [3 x i8]* [[ADDR_B_YZ]] to i24* // CHECK-x86_64: store i24 [[B_YZ]], i24* [[ADDR_B_YZ_1]], align 4 -// CHECK-x86_64: [[ADDR_CDE:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[ARG]], i32 0, i32 3 +// CHECK-x86_64: [[ADDR_CDE:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[ARG]], i32 0, i32 2 // CHECK-x86_64: [[ADDR_CDE_1:%.*]] = bitcast [4 x i8]* [[ADDR_CDE]] to i32* // CHECK-x86_64: store i32 [[CDE]], i32* [[ADDR_CDE_1]], align 4 -// CHECK-x86_64: [[ADDR_FGH:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[ARG]], i32 0, i32 4 +// CHECK-x86_64: [[ADDR_FGH:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[ARG]], i32 0, i32 3 // CHECK-x86_64: [[ADDR_FGH_1:%.*]] = bitcast [4 x i8]* [[ADDR_FGH]] to i32* // CHECK-x86_64: store i32 [[FGH]], i32* [[ADDR_FGH_1]], align 8 -// CHECK-x86_64: [[ADDR_I:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[ARG]], i32 0, i32 5 +// CHECK-x86_64: [[ADDR_I:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[ARG]], i32 0, i32 4 // CHECK-x86_64: [[ADDR_I_V:%.*]] = getelementptr inbounds %Sf, %Sf* [[ADDR_I]], i32 0, i32 0 // CHECK-x86_64: store float [[I]], float* [[ADDR_I_V]], align 4 -// CHECK-x86_64: [[ADDR_JK:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[ARG]], i32 0, i32 6 +// CHECK-x86_64: [[ADDR_JK:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[ARG]], i32 0, i32 5 // CHECK-x86_64: [[ADDR_JK_1:%.*]] = bitcast [1 x i8]* [[ADDR_JK]] to i8* // CHECK-x86_64: store i8 [[JK]], i8* [[ADDR_JK_1]], align 8 -// CHECK-x86_64: [[ADDR_L:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[ARG]], i32 0, i32 8 +// CHECK-x86_64: [[ADDR_L:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[ARG]], i32 0, i32 7 // CHECK-x86_64: [[ADDR_L_V:%.*]] = getelementptr inbounds %Vs6UInt64, %Vs6UInt64* [[ADDR_L]], i32 0, i32 0 // CHECK-x86_64: store i64 [[L]], i64* [[ADDR_L_V]], align 8 -// CHECK-x86_64: [[ADDR_M:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[ARG]], i32 0, i32 9 +// CHECK-x86_64: [[ADDR_M:%.*]] = getelementptr inbounds %VSC11BitfieldOne, %VSC11BitfieldOne* [[ARG]], i32 0, i32 8 // CHECK-x86_64: [[ADDR_M_V:%.*]] = getelementptr inbounds %Vs6UInt32, %Vs6UInt32* [[ADDR_M]], i32 0, i32 0 // CHECK-x86_64: store i32 [[M]], i32* [[ADDR_M_V]], align 8 // CHECK-x86_64: call void @consumeBitfieldOne(%VSC11BitfieldOne* byval align 8 [[ARG]]) diff --git a/test/Reflection/typeref_decoding_imported.swift b/test/Reflection/typeref_decoding_imported.swift index 270fa9133d78c..a73b343533b9b 100644 --- a/test/Reflection/typeref_decoding_imported.swift +++ b/test/Reflection/typeref_decoding_imported.swift @@ -81,7 +81,7 @@ // CHECK-64: ============== // CHECK-64: - __C.MyCStruct: -// CHECK-64: Size: 17 +// CHECK-64: Size: 24 // CHECK-64: Alignment: 8 // CHECK-64: Stride: 24 // CHECK-64: NumExtraInhabitants: 0 @@ -99,7 +99,7 @@ // CHECK-64: NumExtraInhabitants: 0 // CHECK-64: - __C.MyCStructWithBitfields: -// CHECK-64: Size: 2 +// CHECK-64: Size: 4 // CHECK-64: Alignment: 4 // CHECK-64: Stride: 4 // CHECK-64: NumExtraInhabitants: 0 diff --git a/test/Reflection/typeref_lowering_imported.swift b/test/Reflection/typeref_lowering_imported.swift index f446d6aa9487f..e6e84304b10d6 100644 --- a/test/Reflection/typeref_lowering_imported.swift +++ b/test/Reflection/typeref_lowering_imported.swift @@ -7,13 +7,13 @@ V12TypeLowering9HasCTypes // CHECK: (struct TypeLowering.HasCTypes) -// CHECK-NEXT: (struct size=34 alignment=8 stride=40 num_extra_inhabitants=0 +// CHECK-NEXT: (struct size=44 alignment=8 stride=48 num_extra_inhabitants=0 // CHECK-NEXT: (field name=mcs offset=0 -// CHECK-NEXT: (builtin size=17 alignment=8 stride=24 num_extra_inhabitants=0)) -// CHECK-NEXT: (field name=mce offset=20 +// CHECK-NEXT: (builtin size=24 alignment=8 stride=24 num_extra_inhabitants=0)) +// CHECK-NEXT: (field name=mce offset=24 // CHECK-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0)) -// CHECK-NEXT: (field name=mcu offset=24 +// CHECK-NEXT: (field name=mcu offset=32 // CHECK-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0)) -// CHECK-NEXT: (field name=mcsbf offset=32 -// CHECK-NEXT: (builtin size=2 alignment=4 stride=4 num_extra_inhabitants=0))) +// CHECK-NEXT: (field name=mcsbf offset=40 +// CHECK-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0))) From daca860b215b53733a689172fb7b2482ad234529 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Tue, 2 Aug 2016 19:07:29 -0700 Subject: [PATCH 06/27] [Driver] Make sure to rebuild dependents when a dirty file fails. (#3935) Otherwise we get into a situation like this: 1. Change made to the interface of file A.swift that also causes an error in A.swift. 2. Fixing the error in A.swift does not affect A.swift's interface. 3. File B.swift that depends on A.swift is not rebuilt, since the most recent change to A.swift did not change its interface. To fix this, mark downstream files as needing to be rebuilt even when a compilation job fails with errors. Additionally, attempt to be extra conservative when there's a crash. rdar://problem/25405605 (cherry picked from commit 848b3eb6a317ecc671715d30feb1ddd34c30214c) --- lib/Driver/Compilation.cpp | 101 ++++++++++++------ lib/Driver/Driver.cpp | 54 +++++----- .../Inputs/crash-simple/crash.swift | 2 + .../Inputs/crash-simple/main.swift | 2 + .../Inputs/crash-simple/other.swift | 2 + .../Inputs/crash-simple/output.json | 17 +++ .../Inputs/fail-with-bad-deps/bad.swift | 4 + .../Inputs/fail-with-bad-deps/bad.swiftdeps | 3 + .../fail-with-bad-deps/depends-on-bad.swift | 3 + .../depends-on-bad.swiftdeps | 3 + .../fail-with-bad-deps/depends-on-main.swift | 3 + .../depends-on-main.swiftdeps | 3 + .../Inputs/fail-with-bad-deps/main.swift | 3 + .../Inputs/fail-with-bad-deps/main.swiftdeps | 3 + .../Inputs/fail-with-bad-deps/output.json | 21 ++++ .../Inputs/update-dependencies-bad.py | 23 +++- .../bindings-build-record-options.swift | 27 +++-- .../Dependencies/bindings-build-record.swift | 11 +- test/Driver/Dependencies/crash-added.swift | 30 ++++++ test/Driver/Dependencies/crash-new.swift | 44 ++++++++ test/Driver/Dependencies/crash-simple.swift | 23 ++++ test/Driver/Dependencies/fail-added.swift | 30 ++++++ .../Dependencies/fail-interface-hash.swift | 2 +- test/Driver/Dependencies/fail-new.swift | 15 ++- .../Dependencies/fail-with-bad-deps.swift | 47 ++++++++ .../Inputs/rdar25405605/helper-1.swift | 13 +++ .../Inputs/rdar25405605/helper-2.swift | 14 +++ .../Inputs/rdar25405605/helper-3.swift | 15 +++ .../Inputs/rdar25405605/output.json | 13 +++ .../Driver/Dependencies/rdar25405605.swift | 81 ++++++++++++++ 30 files changed, 533 insertions(+), 79 deletions(-) create mode 100644 test/Driver/Dependencies/Inputs/crash-simple/crash.swift create mode 100644 test/Driver/Dependencies/Inputs/crash-simple/main.swift create mode 100644 test/Driver/Dependencies/Inputs/crash-simple/other.swift create mode 100644 test/Driver/Dependencies/Inputs/crash-simple/output.json create mode 100644 test/Driver/Dependencies/Inputs/fail-with-bad-deps/bad.swift create mode 100644 test/Driver/Dependencies/Inputs/fail-with-bad-deps/bad.swiftdeps create mode 100644 test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-bad.swift create mode 100644 test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-bad.swiftdeps create mode 100644 test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-main.swift create mode 100644 test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-main.swiftdeps create mode 100644 test/Driver/Dependencies/Inputs/fail-with-bad-deps/main.swift create mode 100644 test/Driver/Dependencies/Inputs/fail-with-bad-deps/main.swiftdeps create mode 100644 test/Driver/Dependencies/Inputs/fail-with-bad-deps/output.json create mode 100644 test/Driver/Dependencies/crash-added.swift create mode 100644 test/Driver/Dependencies/crash-new.swift create mode 100644 test/Driver/Dependencies/crash-simple.swift create mode 100644 test/Driver/Dependencies/fail-added.swift create mode 100644 test/Driver/Dependencies/fail-with-bad-deps.swift create mode 100644 validation-test/Driver/Dependencies/Inputs/rdar25405605/helper-1.swift create mode 100644 validation-test/Driver/Dependencies/Inputs/rdar25405605/helper-2.swift create mode 100644 validation-test/Driver/Dependencies/Inputs/rdar25405605/helper-3.swift create mode 100644 validation-test/Driver/Dependencies/Inputs/rdar25405605/output.json create mode 100644 validation-test/Driver/Dependencies/rdar25405605.swift diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index bec468217b3bc..c790a22e71439 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -456,6 +456,70 @@ int Compilation::performJobsImpl() { llvm::errs() << Output; } + // In order to handle both old dependencies that have disappeared and new + // dependencies that have arisen, we need to reload the dependency file. + // Do this whether or not the build succeeded. + SmallVector Dependents; + if (getIncrementalBuildEnabled()) { + const CommandOutput &Output = FinishedCmd->getOutput(); + StringRef DependenciesFile = + Output.getAdditionalOutputForType(types::TY_SwiftDeps); + if (!DependenciesFile.empty() && + (ReturnCode == EXIT_SUCCESS || ReturnCode == EXIT_FAILURE)) { + bool wasCascading = DepGraph.isMarked(FinishedCmd); + + switch (DepGraph.loadFromPath(FinishedCmd, DependenciesFile)) { + case DependencyGraphImpl::LoadResult::HadError: + if (ReturnCode == EXIT_SUCCESS) { + disableIncrementalBuild(); + for (const Job *Cmd : DeferredCommands) + scheduleCommandIfNecessaryAndPossible(Cmd); + DeferredCommands.clear(); + Dependents.clear(); + } // else, let the next build handle it. + break; + case DependencyGraphImpl::LoadResult::UpToDate: + if (!wasCascading) + break; + SWIFT_FALLTHROUGH; + case DependencyGraphImpl::LoadResult::AffectsDownstream: + llvm::errs() << "DOWNSTREAM " << ReturnCode << "\n"; + DepGraph.markTransitive(Dependents, FinishedCmd); + break; + } + } else { + // If there's a crash, assume the worst. + switch (FinishedCmd->getCondition()) { + case Job::Condition::NewlyAdded: + // The job won't be treated as newly added next time. Conservatively + // mark it as affecting other jobs, because some of them may have + // completed already. + DepGraph.markTransitive(Dependents, FinishedCmd); + break; + case Job::Condition::Always: + // This applies to non-incremental tasks as well, but any incremental + // task that shows up here has already been marked. + break; + case Job::Condition::RunWithoutCascading: + // If this file changed, it might have been a non-cascading change and + // it might not. Unfortunately, the interface hash has been updated or + // compromised, so we don't actually know anymore; we have to + // conservatively assume the changes could affect other files. + DepGraph.markTransitive(Dependents, FinishedCmd); + break; + case Job::Condition::CheckDependencies: + // If the only reason we're running this is because something else + // changed, then we can trust the dependency graph as to whether it's + // a cascading or non-cascading change. That is, if whatever /caused/ + // the error isn't supposed to affect other files, and whatever + // /fixes/ the error isn't supposed to affect other files, then + // there's no need to recompile any other inputs. If either of those + // are false, we /do/ need to recompile other inputs. + break; + } + } + } + if (ReturnCode != EXIT_SUCCESS) { // The task failed, so return true without performing any further // dependency analysis. @@ -481,39 +545,10 @@ int Compilation::performJobsImpl() { // might have been blocked. markFinished(FinishedCmd); - // In order to handle both old dependencies that have disappeared and new - // dependencies that have arisen, we need to reload the dependency file. - if (getIncrementalBuildEnabled()) { - const CommandOutput &Output = FinishedCmd->getOutput(); - StringRef DependenciesFile = - Output.getAdditionalOutputForType(types::TY_SwiftDeps); - if (!DependenciesFile.empty()) { - SmallVector Dependents; - bool wasCascading = DepGraph.isMarked(FinishedCmd); - - switch (DepGraph.loadFromPath(FinishedCmd, DependenciesFile)) { - case DependencyGraphImpl::LoadResult::HadError: - disableIncrementalBuild(); - for (const Job *Cmd : DeferredCommands) - scheduleCommandIfNecessaryAndPossible(Cmd); - DeferredCommands.clear(); - Dependents.clear(); - break; - case DependencyGraphImpl::LoadResult::UpToDate: - if (!wasCascading) - break; - SWIFT_FALLTHROUGH; - case DependencyGraphImpl::LoadResult::AffectsDownstream: - DepGraph.markTransitive(Dependents, FinishedCmd); - break; - } - - for (const Job *Cmd : Dependents) { - DeferredCommands.erase(Cmd); - noteBuilding(Cmd, "because of dependencies discovered later"); - scheduleCommandIfNecessaryAndPossible(Cmd); - } - } + for (const Job *Cmd : Dependents) { + DeferredCommands.erase(Cmd); + noteBuilding(Cmd, "because of dependencies discovered later"); + scheduleCommandIfNecessaryAndPossible(Cmd); } return TaskFinishedResponse::ContinueExecution; diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 6c380b622c861..1019f9ef044ee 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1604,36 +1604,40 @@ handleCompileJobCondition(Job *J, CompileJobAction::InputInfo inputInfo, return; } - if (!alwaysRebuildDependents) { - // Default all non-newly added files to being rebuilt without cascading. - J->setCondition(Job::Condition::RunWithoutCascading); - } - + bool hasValidModTime = false; llvm::sys::fs::file_status inputStatus; - if (llvm::sys::fs::status(input, inputStatus)) - return; - - J->setInputModTime(inputStatus.getLastModificationTime()); - if (J->getInputModTime() != inputInfo.previousModTime) - return; + if (!llvm::sys::fs::status(input, inputStatus)) { + J->setInputModTime(inputStatus.getLastModificationTime()); + hasValidModTime = true; + } Job::Condition condition; - switch (inputInfo.status) { - case CompileJobAction::InputInfo::UpToDate: - if (!llvm::sys::fs::exists(J->getOutput().getPrimaryOutputFilename())) + if (!hasValidModTime || J->getInputModTime() != inputInfo.previousModTime) { + if (alwaysRebuildDependents || + inputInfo.status == CompileJobAction::InputInfo::NeedsCascadingBuild) { + condition = Job::Condition::Always; + } else { condition = Job::Condition::RunWithoutCascading; - else - condition = Job::Condition::CheckDependencies; - break; - case CompileJobAction::InputInfo::NeedsCascadingBuild: - condition = Job::Condition::Always; - break; - case CompileJobAction::InputInfo::NeedsNonCascadingBuild: - condition = Job::Condition::RunWithoutCascading; - break; - case CompileJobAction::InputInfo::NewlyAdded: - llvm_unreachable("handled above"); + } + } else { + switch (inputInfo.status) { + case CompileJobAction::InputInfo::UpToDate: + if (!llvm::sys::fs::exists(J->getOutput().getPrimaryOutputFilename())) + condition = Job::Condition::RunWithoutCascading; + else + condition = Job::Condition::CheckDependencies; + break; + case CompileJobAction::InputInfo::NeedsCascadingBuild: + condition = Job::Condition::Always; + break; + case CompileJobAction::InputInfo::NeedsNonCascadingBuild: + condition = Job::Condition::RunWithoutCascading; + break; + case CompileJobAction::InputInfo::NewlyAdded: + llvm_unreachable("handled above"); + } } + J->setCondition(condition); } diff --git a/test/Driver/Dependencies/Inputs/crash-simple/crash.swift b/test/Driver/Dependencies/Inputs/crash-simple/crash.swift new file mode 100644 index 0000000000000..7e7daa298c540 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple/crash.swift @@ -0,0 +1,2 @@ +# Dependencies after compilation: +provides-top-level: [a] diff --git a/test/Driver/Dependencies/Inputs/crash-simple/main.swift b/test/Driver/Dependencies/Inputs/crash-simple/main.swift new file mode 100644 index 0000000000000..c6dd8d475b207 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple/main.swift @@ -0,0 +1,2 @@ +# Dependencies after compilation: +depends-top-level: [a] diff --git a/test/Driver/Dependencies/Inputs/crash-simple/other.swift b/test/Driver/Dependencies/Inputs/crash-simple/other.swift new file mode 100644 index 0000000000000..33392ce138612 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple/other.swift @@ -0,0 +1,2 @@ +# Dependencies after compilation: +depends-top-level: [!private a] diff --git a/test/Driver/Dependencies/Inputs/crash-simple/output.json b/test/Driver/Dependencies/Inputs/crash-simple/output.json new file mode 100644 index 0000000000000..1f7532dc86406 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/crash-simple/output.json @@ -0,0 +1,17 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./crash.swift": { + "object": "./crash.o", + "swift-dependencies": "./crash.swiftdeps" + }, + "./other.swift": { + "object": "./other.o", + "swift-dependencies": "./other.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps/bad.swift b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/bad.swift new file mode 100644 index 0000000000000..0f05e70d14710 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/bad.swift @@ -0,0 +1,4 @@ +# Dependencies after compilation: +provides-top-level: [bad] +interface-hash: "after" +garbage: "" diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps/bad.swiftdeps b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/bad.swiftdeps new file mode 100644 index 0000000000000..923f6689ba1a5 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/bad.swiftdeps @@ -0,0 +1,3 @@ +# Dependencies before compilation: +provides-top-level: [bad] +interface-hash: "before" diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-bad.swift b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-bad.swift new file mode 100644 index 0000000000000..415ec3b051000 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-bad.swift @@ -0,0 +1,3 @@ +# Dependencies after compilation: +depends-top-level: [bad] +interface-hash: "after" diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-bad.swiftdeps b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-bad.swiftdeps new file mode 100644 index 0000000000000..97afde93b75ca --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-bad.swiftdeps @@ -0,0 +1,3 @@ +# Dependencies before compilation: +depends-top-level: [bad] +interface-hash: "before" diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-main.swift b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-main.swift new file mode 100644 index 0000000000000..2b781b861cb7e --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-main.swift @@ -0,0 +1,3 @@ +# Dependencies after compilation: +depends-top-level: [main] +interface-hash: "after" diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-main.swiftdeps b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-main.swiftdeps new file mode 100644 index 0000000000000..cd50d25b878a7 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/depends-on-main.swiftdeps @@ -0,0 +1,3 @@ +# Dependencies before compilation: +depends-top-level: [main] +interface-hash: "before" diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps/main.swift b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/main.swift new file mode 100644 index 0000000000000..5b5f8d7f3346f --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/main.swift @@ -0,0 +1,3 @@ +# Dependencies after compilation: +provides-top-level: [main] +interface-hash: "after" diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps/main.swiftdeps b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/main.swiftdeps new file mode 100644 index 0000000000000..0ec59e418937a --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/main.swiftdeps @@ -0,0 +1,3 @@ +# Dependencies before compilation: +provides-top-level: [main] +interface-hash: "before" diff --git a/test/Driver/Dependencies/Inputs/fail-with-bad-deps/output.json b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/output.json new file mode 100644 index 0000000000000..7078694c2f183 --- /dev/null +++ b/test/Driver/Dependencies/Inputs/fail-with-bad-deps/output.json @@ -0,0 +1,21 @@ +{ + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps" + }, + "./bad.swift": { + "object": "./bad.o", + "swift-dependencies": "./bad.swiftdeps" + }, + "./depends-on-main.swift": { + "object": "./depends-on-main.o", + "swift-dependencies": "./depends-on-main.swiftdeps" + }, + "./depends-on-bad.swift": { + "object": "./depends-on-bad.o", + "swift-dependencies": "./depends-on-bad.swiftdeps" + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/test/Driver/Dependencies/Inputs/update-dependencies-bad.py b/test/Driver/Dependencies/Inputs/update-dependencies-bad.py index 058428df7f65f..530ba73080955 100755 --- a/test/Driver/Dependencies/Inputs/update-dependencies-bad.py +++ b/test/Driver/Dependencies/Inputs/update-dependencies-bad.py @@ -11,23 +11,38 @@ # # ---------------------------------------------------------------------------- # -# Fails if the input file is named "bad.swift"; otherwise dispatches to -# update-dependencies.py. +# Fails if the input file is named "bad.swift" or "crash.swift"; otherwise +# dispatches to update-dependencies.py. "crash.swift" gives an exit code +# other than 1. # # ---------------------------------------------------------------------------- from __future__ import print_function import os +import shutil import sys assert sys.argv[1] == '-frontend' primaryFile = sys.argv[sys.argv.index('-primary-file') + 1] -if os.path.basename(primaryFile) == 'bad.swift': +if (os.path.basename(primaryFile) == 'bad.swift' or + os.path.basename(primaryFile) == 'crash.swift'): print("Handled", os.path.basename(primaryFile)) - sys.exit(1) + + # Replace the dependencies file with the input file. + try: + depsFile = sys.argv[sys.argv.index( + '-emit-reference-dependencies-path') + 1] + shutil.copyfile(primaryFile, depsFile) + except ValueError: + pass + + if os.path.basename(primaryFile) == 'bad.swift': + sys.exit(1) + else: + sys.exit(129) dir = os.path.dirname(os.path.abspath(__file__)) execfile(os.path.join(dir, "update-dependencies.py")) diff --git a/test/Driver/Dependencies/bindings-build-record-options.swift b/test/Driver/Dependencies/bindings-build-record-options.swift index 83a630f36ea6e..cffc3f1ab3442 100644 --- a/test/Driver/Dependencies/bindings-build-record-options.swift +++ b/test/Driver/Dependencies/bindings-build-record-options.swift @@ -1,12 +1,17 @@ // RUN: rm -rf %t && cp -r %S/Inputs/bindings-build-record/ %t // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC +// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC-INITIAL -// MUST-EXEC-NOT: warning -// MUST-EXEC: inputs: ["./main.swift"], output: {{[{].*[}]}}, condition: run-without-cascading -// MUST-EXEC: inputs: ["./other.swift"], output: {{[{].*[}]}}, condition: run-without-cascading -// MUST-EXEC: inputs: ["./yet-another.swift"], output: {{[{].*[}]}}, condition: run-without-cascading +// MUST-EXEC-INITIAL-NOT: warning +// MUST-EXEC-INITIAL: inputs: ["./main.swift"], output: {{[{].*[}]}}, condition: run-without-cascading +// MUST-EXEC-INITIAL: inputs: ["./other.swift"], output: {{[{].*[}]}}, condition: run-without-cascading +// MUST-EXEC-INITIAL: inputs: ["./yet-another.swift"], output: {{[{].*[}]}}, condition: run-without-cascading + +// MUST-EXEC-ALL-NOT: warning +// MUST-EXEC-ALL: inputs: ["./main.swift"], output: {{[{].*[}]$}} +// MUST-EXEC-ALL: inputs: ["./other.swift"], output: {{[{].*[}]$}} +// MUST-EXEC-ALL: inputs: ["./yet-another.swift"], output: {{[{].*[}]$}} // RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path %S/Inputs/update-dependencies.py ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json // RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=NO-EXEC @@ -19,28 +24,28 @@ // RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings -serialize-diagnostics ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=NO-EXEC // RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -O -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC +// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -O -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC-ALL // RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path %S/Inputs/update-dependencies.py ./main.swift ./other.swift ./yet-another.swift -incremental -O -output-file-map %t/output.json // RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -O -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=NO-EXEC // RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -O -serialize-diagnostics -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -Onone -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC +// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -Onone -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC-ALL // RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path %S/Inputs/update-dependencies.py ./main.swift ./other.swift ./yet-another.swift -incremental -Onone -output-file-map %t/output.json // RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -Onone -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC +// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC-ALL // RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path %S/Inputs/update-dependencies.py ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json // RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC +// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC-ALL // RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path %S/Inputs/update-dependencies.py ./main.swift ./other.swift ./yet-another.swift -incremental -I. -output-file-map %t/output.json // RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -I/ -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC +// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -I/ -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC-ALL // RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path %S/Inputs/update-dependencies.py ./main.swift ./other.swift ./yet-another.swift -incremental -I. -I/ -output-file-map %t/output.json // RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -I/ -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=NO-EXEC -// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -DDEBUG -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC +// RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -DDEBUG -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC-ALL // RUN: cd %t && %swiftc_driver -c -module-name main -driver-use-frontend-path %S/Inputs/update-dependencies.py ./main.swift ./other.swift ./yet-another.swift -incremental -I. -DDEBUG -output-file-map %t/output.json // RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -I. -DDEBUG -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=NO-EXEC // RUN: cd %t && %swiftc_driver -c -module-name main -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -DDEBUG -I. -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=NO-EXEC diff --git a/test/Driver/Dependencies/bindings-build-record.swift b/test/Driver/Dependencies/bindings-build-record.swift index c4d6ced29fbff..c9fa63a75b692 100644 --- a/test/Driver/Dependencies/bindings-build-record.swift +++ b/test/Driver/Dependencies/bindings-build-record.swift @@ -37,10 +37,15 @@ // RUN: %S/Inputs/touch.py 443865900 %t/* // RUN: cd %t && %swiftc_driver -driver-print-bindings ./main.swift ./other.swift -incremental -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=FILE-REMOVED -// FILE-REMOVED: inputs: ["./main.swift"], output: {{[{].*[}]}}, condition: run-without-cascading -// FILE-REMOVED: inputs: ["./other.swift"], output: {{[{].*[}]}}, condition: run-without-cascading +// FILE-REMOVED: inputs: ["./main.swift"], output: {{[{].*[}]$}} +// FILE-REMOVED: inputs: ["./other.swift"], output: {{[{].*[}]$}} // FILE-REMOVED-NOT: yet-another.swift // RUN: echo '{version: "bogus", inputs: {"./main.swift": [443865900, 0], "./other.swift": !private [443865900, 0], "./yet-another.swift": !dirty [443865900, 0]}}' > %t/main~buildrecord.swiftdeps -// RUN: cd %t && %swiftc_driver -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=MUST-EXEC +// RUN: cd %t && %swiftc_driver -driver-print-bindings ./main.swift ./other.swift ./yet-another.swift -incremental -output-file-map %t/output.json 2>&1 | FileCheck %s -check-prefix=INVALID-RECORD + +// INVALID-RECORD-NOT: warning +// INVALID-RECORD: inputs: ["./main.swift"], output: {{[{].*[}]$}} +// INVALID-RECORD: inputs: ["./other.swift"], output: {{[{].*[}]$}} +// INVALID-RECORD: inputs: ["./yet-another.swift"], output: {{[{].*[}]$}} diff --git a/test/Driver/Dependencies/crash-added.swift b/test/Driver/Dependencies/crash-added.swift new file mode 100644 index 0000000000000..1f4f9786b6585 --- /dev/null +++ b/test/Driver/Dependencies/crash-added.swift @@ -0,0 +1,30 @@ +/// crash ==> main | crash --> other + +// RUN: rm -rf %t && cp -r %S/Inputs/crash-simple/ %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-INITIAL %s + +// CHECK-INITIAL-NOT: warning +// CHECK-INITIAL: Handled main.swift +// CHECK-INITIAL: Handled other.swift + +// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./crash.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-ADDED %s +// RUN: FileCheck -check-prefix=CHECK-RECORD-ADDED %s < %t/main~buildrecord.swiftdeps + +// CHECK-ADDED-NOT: Handled +// CHECK-ADDED: Handled crash.swift +// CHECK-ADDED-NOT: Handled + +// CHECK-RECORD-ADDED-DAG: "./crash.swift": !dirty [ +// CHECK-RECORD-ADDED-DAG: "./main.swift": [ +// CHECK-RECORD-ADDED-DAG: "./other.swift": [ + + +// RUN: rm -rf %t && cp -r %S/Inputs/crash-simple/ %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-INITIAL %s + +// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./crash.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-ADDED %s +// RUN: FileCheck -check-prefix=CHECK-RECORD-ADDED %s < %t/main~buildrecord.swiftdeps diff --git a/test/Driver/Dependencies/crash-new.swift b/test/Driver/Dependencies/crash-new.swift new file mode 100644 index 0000000000000..4bb967c9edceb --- /dev/null +++ b/test/Driver/Dependencies/crash-new.swift @@ -0,0 +1,44 @@ +/// crash ==> main | crash --> other + +// RUN: rm -rf %t && cp -r %S/Inputs/crash-simple/ %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck %s +// CHECK-NOT: warning +// CHECK: Handled main.swift +// CHECK: Handled crash.swift +// CHECK-NOT: Handled other.swift + +// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-BAD-ONLY %s + +// CHECK-BAD-ONLY-NOT: warning +// CHECK-BAD-ONLY-NOT: Handled +// CHECK-BAD-ONLY: Handled crash.swift +// CHECK-BAD-ONLY-NOT: Handled + +// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-OKAY %s +// CHECK-OKAY: Handled main.swift +// CHECK-OKAY: Handled crash.swift +// CHECK-OKAY: Handled other.swift +// CHECK-OKAY-NOT: Handled + +// RUN: touch -t 201401240006 %t/crash.swift +// RUN: rm %t/crash.swiftdeps +// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck %s + +// RUN: touch -t 201401240005 %t/* +// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-OKAY-2 %s + +// CHECK-OKAY-2: Handled crash.swift +// CHECK-OKAY-2: Handled other.swift +// CHECK-OKAY-2: Handled main.swift + +// RUN: touch -t 201401240006 %t/main.swift +// RUN: rm %t/main.swiftdeps +// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck %s + +// RUN: touch -t 201401240005 %t/* +// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-OKAY %s +// RUN: touch -t 201401240006 %t/other.swift +// RUN: rm %t/other.swiftdeps +// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck %s diff --git a/test/Driver/Dependencies/crash-simple.swift b/test/Driver/Dependencies/crash-simple.swift new file mode 100644 index 0000000000000..ce0eb9e04045f --- /dev/null +++ b/test/Driver/Dependencies/crash-simple.swift @@ -0,0 +1,23 @@ +/// crash ==> main | crash --> other + +// RUN: rm -rf %t && cp -r %S/Inputs/crash-simple/ %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled crash.swift +// CHECK-FIRST: Handled other.swift + +// RUN: touch -t 201401240006 %t/crash.swift +// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./crash.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-SECOND %s +// RUN: FileCheck -check-prefix=CHECK-RECORD %s < %t/main~buildrecord.swiftdeps + +// CHECK-SECOND: Handled crash.swift +// CHECK-SECOND-NOT: Handled main.swift +// CHECK-SECOND-NOT: Handled other.swift + +// CHECK-RECORD-DAG: "./crash.swift": !dirty [ +// CHECK-RECORD-DAG: "./main.swift": !dirty [ +// CHECK-RECORD-DAG: "./other.swift": !private [ diff --git a/test/Driver/Dependencies/fail-added.swift b/test/Driver/Dependencies/fail-added.swift new file mode 100644 index 0000000000000..a333078fcfb5c --- /dev/null +++ b/test/Driver/Dependencies/fail-added.swift @@ -0,0 +1,30 @@ +/// bad ==> main | bad --> other + +// RUN: rm -rf %t && cp -r %S/Inputs/fail-simple/ %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-INITIAL %s + +// CHECK-INITIAL-NOT: warning +// CHECK-INITIAL: Handled main.swift +// CHECK-INITIAL: Handled other.swift + +// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift ./bad.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-ADDED %s +// RUN: FileCheck -check-prefix=CHECK-RECORD-ADDED %s < %t/main~buildrecord.swiftdeps + +// CHECK-ADDED-NOT: Handled +// CHECK-ADDED: Handled bad.swift +// CHECK-ADDED-NOT: Handled + +// CHECK-RECORD-ADDED-DAG: "./bad.swift": !dirty [ +// CHECK-RECORD-ADDED-DAG: "./main.swift": [ +// CHECK-RECORD-ADDED-DAG: "./other.swift": [ + + +// RUN: rm -rf %t && cp -r %S/Inputs/fail-simple/ %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-INITIAL %s + +// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./bad.swift ./main.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-ADDED %s +// RUN: FileCheck -check-prefix=CHECK-RECORD-ADDED %s < %t/main~buildrecord.swiftdeps diff --git a/test/Driver/Dependencies/fail-interface-hash.swift b/test/Driver/Dependencies/fail-interface-hash.swift index 5db2975e87c68..392cc24f996a8 100644 --- a/test/Driver/Dependencies/fail-interface-hash.swift +++ b/test/Driver/Dependencies/fail-interface-hash.swift @@ -23,7 +23,7 @@ // CHECK-SECOND: Handled bad.swift // CHECK-SECOND-NOT: Handled depends -// CHECK-RECORD-DAG: "./bad.swift": !private [ +// CHECK-RECORD-DAG: "./bad.swift": !dirty [ // CHECK-RECORD-DAG: "./main.swift": [ // CHECK-RECORD-DAG: "./depends-on-main.swift": !dirty [ // CHECK-RECORD-DAG: "./depends-on-bad.swift": [ diff --git a/test/Driver/Dependencies/fail-new.swift b/test/Driver/Dependencies/fail-new.swift index f3e3251c5e95f..226098c69a8c0 100644 --- a/test/Driver/Dependencies/fail-new.swift +++ b/test/Driver/Dependencies/fail-new.swift @@ -9,19 +9,30 @@ // CHECK: Handled bad.swift // CHECK-NOT: Handled other.swift -// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck %s +// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-BAD-ONLY %s + +// CHECK-BAD-ONLY-NOT: warning +// CHECK-BAD-ONLY-NOT: Handled +// CHECK-BAD-ONLY: Handled bad.swift +// CHECK-BAD-ONLY-NOT: Handled // RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-OKAY %s // CHECK-OKAY: Handled main.swift // CHECK-OKAY: Handled bad.swift // CHECK-OKAY: Handled other.swift +// CHECK-OKAY-NOT: Handled // RUN: touch -t 201401240006 %t/bad.swift // RUN: rm %t/bad.swiftdeps // RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck %s // RUN: touch -t 201401240005 %t/* -// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-OKAY %s +// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-OKAY-2 %s + +// CHECK-OKAY-2: Handled bad.swift +// CHECK-OKAY-2: Handled other.swift +// CHECK-OKAY-2: Handled main.swift + // RUN: touch -t 201401240006 %t/main.swift // RUN: rm %t/main.swiftdeps // RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental -driver-always-rebuild-dependents ./main.swift ./bad.swift ./other.swift -module-name main -j1 -v 2>&1 | FileCheck %s diff --git a/test/Driver/Dependencies/fail-with-bad-deps.swift b/test/Driver/Dependencies/fail-with-bad-deps.swift new file mode 100644 index 0000000000000..f7e535d89a7e9 --- /dev/null +++ b/test/Driver/Dependencies/fail-with-bad-deps.swift @@ -0,0 +1,47 @@ +/// main ==> depends-on-main | bad ==> depends-on-bad + +// RUN: rm -rf %t && cp -r %S/Inputs/fail-with-bad-deps/ %t +// RUN: touch -t 201401240005 %t/* + +// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-FIRST %s + +// CHECK-FIRST-NOT: warning +// CHECK-FIRST: Handled main.swift +// CHECK-FIRST: Handled bad.swift +// CHECK-FIRST: Handled depends-on-main.swift +// CHECK-FIRST: Handled depends-on-bad.swift + +// Reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/fail-with-bad-deps/*.swiftdeps %t + +// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-NONE %s +// CHECK-NONE-NOT: Handled + +// Reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/fail-with-bad-deps/*.swiftdeps %t + +// RUN: touch -t 201401240006 %t/bad.swift +// RUN: cd %t && %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies.py -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-BUILD-ALL %s + +// CHECK-BUILD-ALL-NOT: warning +// CHECK-BUILD-ALL: Handled bad.swift +// CHECK-BUILD-ALL-DAG: Handled main.swift +// CHECK-BUILD-ALL-DAG: Handled depends-on-main.swift +// CHECK-BUILD-ALL-DAG: Handled depends-on-bad.swift + +// Reset the .swiftdeps files. +// RUN: cp -r %S/Inputs/fail-with-bad-deps/*.swiftdeps %t + +// RUN: touch -t 201401240007 %t/bad.swift %t/main.swift +// RUN: cd %t && not %swiftc_driver -c -driver-use-frontend-path %S/Inputs/update-dependencies-bad.py -output-file-map %t/output.json -incremental ./main.swift ./bad.swift ./depends-on-main.swift ./depends-on-bad.swift -module-name main -j1 -v 2>&1 | FileCheck -check-prefix=CHECK-WITH-FAIL %s +// RUN: FileCheck -check-prefix=CHECK-RECORD %s < %t/main~buildrecord.swiftdeps + +// CHECK-WITH-FAIL: Handled main.swift +// CHECK-WITH-FAIL-NOT: Handled depends +// CHECK-WITH-FAIL: Handled bad.swift +// CHECK-WITH-FAIL-NOT: Handled depends + +// CHECK-RECORD-DAG: "./bad.swift": !private [ +// CHECK-RECORD-DAG: "./main.swift": [ +// CHECK-RECORD-DAG: "./depends-on-main.swift": !dirty [ +// CHECK-RECORD-DAG: "./depends-on-bad.swift": [ diff --git a/validation-test/Driver/Dependencies/Inputs/rdar25405605/helper-1.swift b/validation-test/Driver/Dependencies/Inputs/rdar25405605/helper-1.swift new file mode 100644 index 0000000000000..ec20ab4aa0973 --- /dev/null +++ b/validation-test/Driver/Dependencies/Inputs/rdar25405605/helper-1.swift @@ -0,0 +1,13 @@ +enum Foo { + case one + case two +} + +func doSomething(_ value: Foo) { + switch value { + case .one: + print("Hello") + case .two: + print("Goodbye") + } +} diff --git a/validation-test/Driver/Dependencies/Inputs/rdar25405605/helper-2.swift b/validation-test/Driver/Dependencies/Inputs/rdar25405605/helper-2.swift new file mode 100644 index 0000000000000..056314b1d64e1 --- /dev/null +++ b/validation-test/Driver/Dependencies/Inputs/rdar25405605/helper-2.swift @@ -0,0 +1,14 @@ +enum Foo { + case one + case two + case three +} + +func doSomething(_ value: Foo) { + switch value { + case .one: + print("Hello") + case .two: + print("Goodbye") + } +} diff --git a/validation-test/Driver/Dependencies/Inputs/rdar25405605/helper-3.swift b/validation-test/Driver/Dependencies/Inputs/rdar25405605/helper-3.swift new file mode 100644 index 0000000000000..89c1f94a47824 --- /dev/null +++ b/validation-test/Driver/Dependencies/Inputs/rdar25405605/helper-3.swift @@ -0,0 +1,15 @@ +enum Foo { + case one + case two + case three +} + +func doSomething(_ value: Foo) { + switch value { + case .one: + print("Hello") + case .two: + print("Goodbye") + case .three: break + } +} diff --git a/validation-test/Driver/Dependencies/Inputs/rdar25405605/output.json b/validation-test/Driver/Dependencies/Inputs/rdar25405605/output.json new file mode 100644 index 0000000000000..7971d0ca6d0b4 --- /dev/null +++ b/validation-test/Driver/Dependencies/Inputs/rdar25405605/output.json @@ -0,0 +1,13 @@ +{ + "./helper.swift": { + "object": "./helper.o", + "swift-dependencies": "./helper.swiftdeps", + }, + "./main.swift": { + "object": "./main.o", + "swift-dependencies": "./main.swiftdeps", + }, + "": { + "swift-dependencies": "./main~buildrecord.swiftdeps" + } +} diff --git a/validation-test/Driver/Dependencies/rdar25405605.swift b/validation-test/Driver/Dependencies/rdar25405605.swift new file mode 100644 index 0000000000000..7d29f7bfa8399 --- /dev/null +++ b/validation-test/Driver/Dependencies/rdar25405605.swift @@ -0,0 +1,81 @@ +// RUN: rm -rf %t && mkdir %t + +// RUN: cp %s %t/main.swift +// RUN: cp %S/Inputs/rdar25405605/helper-1.swift %t/helper.swift +// RUN: touch -t 201401240005 %t/*.swift + +// RUN: cd %t && %target-build-swift -c -incremental -output-file-map %S/Inputs/rdar25405605/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | FileCheck -check-prefix=CHECK-1 %s + +// CHECK-1-NOT: warning +// CHECK-1: {{^{$}} +// CHECK-1: "kind": "began" +// CHECK-1: "name": "compile" +// CHECK-1: ".\/main.swift" +// CHECK-1: {{^}$}} + +// CHECK-1: {{^{$}} +// CHECK-1: "kind": "began" +// CHECK-1: "name": "compile" +// CHECK-1: ".\/helper.swift" +// CHECK-1: {{^}$}} + +// RUN: ls %t/ | FileCheck -check-prefix=CHECK-LS %s + +// CHECK-LS-DAG: main.o +// CHECK-LS-DAG: helper.o + +// RUN: cd %t && %target-build-swift -c -incremental -output-file-map %S/Inputs/rdar25405605/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | FileCheck -check-prefix=CHECK-1-SKIPPED %s + +// CHECK-1-SKIPPED-NOT: warning +// CHECK-1-SKIPPED: {{^{$}} +// CHECK-1-SKIPPED: "kind": "skipped" +// CHECK-1-SKIPPED: "name": "compile" +// CHECK-1-SKIPPED: ".\/main.swift" +// CHECK-1-SKIPPED: {{^}$}} + +// CHECK-1-SKIPPED: {{^{$}} +// CHECK-1-SKIPPED: "kind": "skipped" +// CHECK-1-SKIPPED: "name": "compile" +// CHECK-1-SKIPPED: ".\/helper.swift" +// CHECK-1-SKIPPED: {{^}$}} + +// RUN: cp %S/Inputs/rdar25405605/helper-2.swift %t/helper.swift +// RUN: touch -t 201401240006 %t/helper.swift +// RUN: cd %t && not %target-build-swift -c -incremental -output-file-map %S/Inputs/rdar25405605/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | FileCheck -check-prefix=CHECK-2 %s + +// CHECK-2-NOT: warning +// CHECK-2: {{^{$}} +// CHECK-2: "kind": "began" +// CHECK-2: "name": "compile" +// CHECK-2: ".\/helper.swift" +// CHECK-2: {{^}$}} + +// CHECK-2: {{^{$}} +// CHECK-2: "kind": "skipped" +// CHECK-2: "name": "compile" +// CHECK-2: ".\/main.swift" +// CHECK-2: {{^}$}} + +// RUN: cp %S/Inputs/rdar25405605/helper-3.swift %t/helper.swift +// RUN: touch -t 201401240007 %t/helper.swift +// RUN: cd %t && not %target-build-swift -c -incremental -output-file-map %S/Inputs/rdar25405605/output.json -parse-as-library ./main.swift ./helper.swift -parseable-output -j1 -module-name main 2>&1 | FileCheck -check-prefix=CHECK-3 %s + +// CHECK-3-NOT: warning +// CHECK-3: {{^{$}} +// CHECK-3: "kind": "began" +// CHECK-3: "name": "compile" +// CHECK-3: ".\/helper.swift" +// CHECK-3: {{^}$}} + +// CHECK-3: {{^{$}} +// CHECK-3: "kind": "began" +// CHECK-3: "name": "compile" +// CHECK-3: ".\/main.swift" +// CHECK-3: {{^}$}} + +func foo(_ value: Foo) -> Bool { + switch value { + case .one: return true + case .two: return false + } +} From b8304d5bcd0e062ea053a030ae49360b67a7063f Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 3 Aug 2016 10:53:22 -0700 Subject: [PATCH 07/27] Fix test case for 32-bit systems. --- test/Reflection/typeref_decoding_imported.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Reflection/typeref_decoding_imported.swift b/test/Reflection/typeref_decoding_imported.swift index a73b343533b9b..2850bbce8e908 100644 --- a/test/Reflection/typeref_decoding_imported.swift +++ b/test/Reflection/typeref_decoding_imported.swift @@ -25,7 +25,7 @@ // CHECK-32: ============== // CHECK-32: - __C.MyCStruct: -// CHECK-32: Size: 9 +// CHECK-32: Size: 12 // CHECK-32: Alignment: 4 // CHECK-32: Stride: 12 // CHECK-32: NumExtraInhabitants: 0 @@ -43,7 +43,7 @@ // CHECK-32: NumExtraInhabitants: 0 // CHECK-i386: - __C.MyCStructWithBitfields: -// CHECK-i386: Size: 2 +// CHECK-i386: Size: 4 // CHECK-i386: Alignment: 4 // CHECK-i386: Stride: 4 // CHECK-i386: NumExtraInhabitants: 0 From 01eb46e99dbdcc46fc8b6eed031d180bce937df9 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Wed, 3 Aug 2016 11:15:49 -0700 Subject: [PATCH 08/27] [Driver] Remove debugging code from 848b3eb. Thanks, Dmitri. (cherry picked from commit b8cc2673c387f2e326f534dd7415dd9a894239cd) --- lib/Driver/Compilation.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index c790a22e71439..13b559beeafa3 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -483,7 +483,6 @@ int Compilation::performJobsImpl() { break; SWIFT_FALLTHROUGH; case DependencyGraphImpl::LoadResult::AffectsDownstream: - llvm::errs() << "DOWNSTREAM " << ReturnCode << "\n"; DepGraph.markTransitive(Dependents, FinishedCmd); break; } From fda6d4e981b5d7e7e99be66794e19331b131c135 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Mon, 1 Aug 2016 14:58:30 -0700 Subject: [PATCH 09/27] Add fast path for queue.async(flags: .barrier) Fixes SR-2248. --- stdlib/public/SDK/Dispatch/Queue.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/stdlib/public/SDK/Dispatch/Queue.swift b/stdlib/public/SDK/Dispatch/Queue.swift index 92bfd813a7275..4ff414110b0b0 100644 --- a/stdlib/public/SDK/Dispatch/Queue.swift +++ b/stdlib/public/SDK/Dispatch/Queue.swift @@ -197,10 +197,15 @@ public extension DispatchQueue { flags: DispatchWorkItemFlags = [], execute work: @escaping @convention(block) () -> Void) { - if group == nil && qos == .unspecified && flags.isEmpty { + if group == nil && qos == .unspecified { // Fast-path route for the most common API usage - __dispatch_async(self, work) - return + if flags.isEmpty { + __dispatch_async(self, work) + return + } else if flags == .barrier { + __dispatch_barrier_async(self, work) + return + } } var block: @convention(block) () -> Void = work From 73b25c54db38ccad8dc9a073f6604995e3cc9a5a Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Mon, 1 Aug 2016 15:53:32 -0700 Subject: [PATCH 10/27] Use shims for all calls to Dispatch APIs that take blocks This ensures that blocks that come from `DispatchWorkItem`s will function correctly. The only exception is __dispatch_barrier_sync() as the block passed to that call is not `@convention(block)` so it doesn't matter. Fixes SR-2246. --- stdlib/public/SDK/Dispatch/Dispatch.swift | 7 ++-- stdlib/public/SDK/Dispatch/Queue.swift | 20 ++++----- stdlib/public/SDK/Dispatch/Source.swift | 19 +++++---- stdlib/public/SDK/Dispatch/Time.swift | 2 + stdlib/public/SwiftShims/DispatchShims.h | 34 +++++++++++++++ stdlib/public/stubs/DispatchShims.mm | 50 +++++++++++++++++++++++ 6 files changed, 110 insertions(+), 22 deletions(-) diff --git a/stdlib/public/SDK/Dispatch/Dispatch.swift b/stdlib/public/SDK/Dispatch/Dispatch.swift index ca16e03f0eb06..be49a3ab6ed7a 100644 --- a/stdlib/public/SDK/Dispatch/Dispatch.swift +++ b/stdlib/public/SDK/Dispatch/Dispatch.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// @_exported import Dispatch +import SwiftShims /// dispatch_assert @@ -132,15 +133,15 @@ public extension DispatchGroup { public func notify(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], queue: DispatchQueue, execute work: @escaping @convention(block) () -> ()) { if #available(OSX 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty { let item = DispatchWorkItem(qos: qos, flags: flags, block: work) - __dispatch_group_notify(self, queue, item._block) + _swift_dispatch_group_notify(self, queue, item._block) } else { - __dispatch_group_notify(self, queue, work) + _swift_dispatch_group_notify(self, queue, work) } } @available(OSX 10.10, iOS 8.0, *) public func notify(queue: DispatchQueue, work: DispatchWorkItem) { - __dispatch_group_notify(self, queue, work._block) + _swift_dispatch_group_notify(self, queue, work._block) } public func wait() { diff --git a/stdlib/public/SDK/Dispatch/Queue.swift b/stdlib/public/SDK/Dispatch/Queue.swift index 4ff414110b0b0..111b2c9ed1613 100644 --- a/stdlib/public/SDK/Dispatch/Queue.swift +++ b/stdlib/public/SDK/Dispatch/Queue.swift @@ -200,10 +200,10 @@ public extension DispatchQueue { if group == nil && qos == .unspecified { // Fast-path route for the most common API usage if flags.isEmpty { - __dispatch_async(self, work) + _swift_dispatch_async(self, work) return } else if flags == .barrier { - __dispatch_barrier_async(self, work) + _swift_dispatch_barrier_async(self, work) return } } @@ -215,9 +215,9 @@ public extension DispatchQueue { } if let g = group { - __dispatch_group_async(g, self, block) + _swift_dispatch_group_async(g, self, block) } else { - __dispatch_async(self, block) + _swift_dispatch_async(self, block) } } @@ -292,9 +292,9 @@ public extension DispatchQueue { { if #available(OSX 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty { let item = DispatchWorkItem(qos: qos, flags: flags, block: work) - __dispatch_after(deadline.rawValue, self, item._block) + _swift_dispatch_after(deadline.rawValue, self, item._block) } else { - __dispatch_after(deadline.rawValue, self, work) + _swift_dispatch_after(deadline.rawValue, self, work) } } @@ -306,20 +306,20 @@ public extension DispatchQueue { { if #available(OSX 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty { let item = DispatchWorkItem(qos: qos, flags: flags, block: work) - __dispatch_after(wallDeadline.rawValue, self, item._block) + _swift_dispatch_after(wallDeadline.rawValue, self, item._block) } else { - __dispatch_after(wallDeadline.rawValue, self, work) + _swift_dispatch_after(wallDeadline.rawValue, self, work) } } @available(OSX 10.10, iOS 8.0, *) public func asyncAfter(deadline: DispatchTime, execute: DispatchWorkItem) { - __dispatch_after(deadline.rawValue, self, execute._block) + _swift_dispatch_after(deadline.rawValue, self, execute._block) } @available(OSX 10.10, iOS 8.0, *) public func asyncAfter(wallDeadline: DispatchWallTime, execute: DispatchWorkItem) { - __dispatch_after(wallDeadline.rawValue, self, execute._block) + _swift_dispatch_after(wallDeadline.rawValue, self, execute._block) } @available(OSX 10.10, iOS 8.0, *) diff --git a/stdlib/public/SDK/Dispatch/Source.swift b/stdlib/public/SDK/Dispatch/Source.swift index f5b42c1dfcc2c..1d4d0e6b9b110 100644 --- a/stdlib/public/SDK/Dispatch/Source.swift +++ b/stdlib/public/SDK/Dispatch/Source.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// // import Foundation +import SwiftShims public extension DispatchSourceProtocol { typealias DispatchSourceHandler = @convention(block) () -> Void @@ -20,15 +21,15 @@ public extension DispatchSourceProtocol { let h = handler, qos != .unspecified || !flags.isEmpty { let item = DispatchWorkItem(qos: qos, flags: flags, block: h) - __dispatch_source_set_event_handler(self as! DispatchSource, item._block) + _swift_dispatch_source_set_event_handler(self as! DispatchSource, item._block) } else { - __dispatch_source_set_event_handler(self as! DispatchSource, handler) + _swift_dispatch_source_set_event_handler(self as! DispatchSource, handler) } } @available(OSX 10.10, iOS 8.0, *) public func setEventHandler(handler: DispatchWorkItem) { - __dispatch_source_set_event_handler(self as! DispatchSource, handler._block) + _swift_dispatch_source_set_event_handler(self as! DispatchSource, handler._block) } public func setCancelHandler(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], handler: DispatchSourceHandler?) { @@ -36,15 +37,15 @@ public extension DispatchSourceProtocol { let h = handler, qos != .unspecified || !flags.isEmpty { let item = DispatchWorkItem(qos: qos, flags: flags, block: h) - __dispatch_source_set_cancel_handler(self as! DispatchSource, item._block) + _swift_dispatch_source_set_cancel_handler(self as! DispatchSource, item._block) } else { - __dispatch_source_set_cancel_handler(self as! DispatchSource, handler) + _swift_dispatch_source_set_cancel_handler(self as! DispatchSource, handler) } } @available(OSX 10.10, iOS 8.0, *) public func setCancelHandler(handler: DispatchWorkItem) { - __dispatch_source_set_cancel_handler(self as! DispatchSource, handler._block) + _swift_dispatch_source_set_cancel_handler(self as! DispatchSource, handler._block) } public func setRegistrationHandler(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], handler: DispatchSourceHandler?) { @@ -52,15 +53,15 @@ public extension DispatchSourceProtocol { let h = handler, qos != .unspecified || !flags.isEmpty { let item = DispatchWorkItem(qos: qos, flags: flags, block: h) - __dispatch_source_set_registration_handler(self as! DispatchSource, item._block) + _swift_dispatch_source_set_registration_handler(self as! DispatchSource, item._block) } else { - __dispatch_source_set_registration_handler(self as! DispatchSource, handler) + _swift_dispatch_source_set_registration_handler(self as! DispatchSource, handler) } } @available(OSX 10.10, iOS 8.0, *) public func setRegistrationHandler(handler: DispatchWorkItem) { - __dispatch_source_set_registration_handler(self as! DispatchSource, handler._block) + _swift_dispatch_source_set_registration_handler(self as! DispatchSource, handler._block) } @available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) diff --git a/stdlib/public/SDK/Dispatch/Time.swift b/stdlib/public/SDK/Dispatch/Time.swift index 08b2e1dff29ff..c6fd23c8c40e8 100644 --- a/stdlib/public/SDK/Dispatch/Time.swift +++ b/stdlib/public/SDK/Dispatch/Time.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +import SwiftShims + public struct DispatchTime : Comparable { public let rawValue: dispatch_time_t diff --git a/stdlib/public/SwiftShims/DispatchShims.h b/stdlib/public/SwiftShims/DispatchShims.h index 37c5bc2ebaf39..e5f1c5ba37026 100644 --- a/stdlib/public/SwiftShims/DispatchShims.h +++ b/stdlib/public/SwiftShims/DispatchShims.h @@ -40,10 +40,12 @@ namespace swift { extern "C" { typedef unsigned long __swift_shims_dispatch_block_flags_t; typedef unsigned int __swift_shims_qos_class_t; +typedef __swift_uint64_t __swift_shims_dispatch_time_t; typedef void (^__swift_shims_dispatch_block_t)(void); typedef id __swift_shims_dispatch_queue_t; typedef id __swift_shims_dispatch_group_t; typedef id __swift_shims_dispatch_data_t; +typedef id __swift_shims_dispatch_source_t; SWIFT_RUNTIME_STDLIB_INTERFACE SWIFT_DISPATCH_RETURNS_RETAINED @@ -90,12 +92,29 @@ void _swift_dispatch_sync( __swift_shims_dispatch_queue_t queue, __swift_shims_dispatch_block_t block); +SWIFT_RUNTIME_STDLIB_INTERFACE +void _swift_dispatch_barrier_async( + __swift_shims_dispatch_queue_t queue, + __swift_shims_dispatch_block_t block); + SWIFT_RUNTIME_STDLIB_INTERFACE void _swift_dispatch_group_async( __swift_shims_dispatch_group_t group, __swift_shims_dispatch_queue_t queue, __swift_shims_dispatch_block_t block); +SWIFT_RUNTIME_STDLIB_INTERFACE +void _swift_dispatch_group_notify( + __swift_shims_dispatch_group_t group, + __swift_shims_dispatch_queue_t queue, + __swift_shims_dispatch_block_t block); + +SWIFT_RUNTIME_STDLIB_INTERFACE +void _swift_dispatch_after( + __swift_shims_dispatch_time_t when, + __swift_shims_dispatch_queue_t queue, + __swift_shims_dispatch_block_t block); + SWIFT_RUNTIME_STDLIB_INTERFACE void _swift_dispatch_apply_current( unsigned int iterations, @@ -118,6 +137,21 @@ _swift_dispatch_data_apply( __swift_shims_dispatch_data_t data, __swift_shims_dispatch_data_applier SWIFT_DISPATCH_NOESCAPE applier); +SWIFT_RUNTIME_STDLIB_INTERFACE +void _swift_dispatch_source_set_event_handler( + __swift_shims_dispatch_source_t source, + __swift_shims_dispatch_block_t SWIFT_DISPATCH_NULLABLE block); + +SWIFT_RUNTIME_STDLIB_INTERFACE +void _swift_dispatch_source_set_cancel_handler( + __swift_shims_dispatch_source_t source, + __swift_shims_dispatch_block_t SWIFT_DISPATCH_NULLABLE block); + +SWIFT_RUNTIME_STDLIB_INTERFACE +void _swift_dispatch_source_set_registration_handler( + __swift_shims_dispatch_source_t source, + __swift_shims_dispatch_block_t SWIFT_DISPATCH_NULLABLE block); + #ifdef __cplusplus }} // extern "C", namespace swift #endif diff --git a/stdlib/public/stubs/DispatchShims.mm b/stdlib/public/stubs/DispatchShims.mm index 697bab25bb0c1..e669288830162 100644 --- a/stdlib/public/stubs/DispatchShims.mm +++ b/stdlib/public/stubs/DispatchShims.mm @@ -94,6 +94,14 @@ dispatch_async(cast(queue), cast(block)); } +void +swift::_swift_dispatch_barrier_async( + __swift_shims_dispatch_queue_t queue, + __swift_shims_dispatch_block_t block) +{ + dispatch_barrier_async(cast(queue), cast(block)); +} + void swift::_swift_dispatch_group_async( __swift_shims_dispatch_group_t group, @@ -103,6 +111,15 @@ dispatch_group_async((dispatch_group_t)group, cast(queue), cast(block)); } +void +swift::_swift_dispatch_group_notify( + __swift_shims_dispatch_group_t group, + __swift_shims_dispatch_queue_t queue, + __swift_shims_dispatch_block_t block) +{ + dispatch_group_notify((dispatch_group_t)group, cast(queue), cast(block)); +} + void swift::_swift_dispatch_sync( __swift_shims_dispatch_queue_t queue, @@ -111,6 +128,15 @@ dispatch_sync(cast(queue), cast(block)); } +void +swift::_swift_dispatch_after( + __swift_shims_dispatch_time_t when, + __swift_shims_dispatch_queue_t queue, + __swift_shims_dispatch_block_t block) +{ + dispatch_after((dispatch_time_t)when, cast(queue), cast(block)); +} + void swift::_swift_dispatch_apply_current( unsigned int iterations, @@ -140,3 +166,27 @@ void SWIFT_DISPATCH_NOESCAPE (^block)(long)) return applier(data, off, loc, size); }); } + +void +swift::_swift_dispatch_source_set_event_handler( + __swift_shims_dispatch_source_t source, + __swift_shims_dispatch_block_t block) +{ + dispatch_source_set_event_handler((dispatch_source_t)source, cast(block)); +} + +void +swift::_swift_dispatch_source_set_cancel_handler( + __swift_shims_dispatch_source_t source, + __swift_shims_dispatch_block_t block) +{ + dispatch_source_set_cancel_handler((dispatch_source_t)source, cast(block)); +} + +void +swift::_swift_dispatch_source_set_registration_handler( + __swift_shims_dispatch_source_t source, + __swift_shims_dispatch_block_t block) +{ + dispatch_source_set_registration_handler((dispatch_source_t)source, cast(block)); +} From 50068d880af6e9fede7cc167760eb333cb4a9e00 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Wed, 3 Aug 2016 11:06:34 -0700 Subject: [PATCH 11/27] This test is known to fail on a debug stdlib rdar://27226313 (cherry picked from commit b5a8805b495e1f5004c9baa7b28fe837bbeb26dd) --- test/1_stdlib/Dispatch.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/1_stdlib/Dispatch.swift b/test/1_stdlib/Dispatch.swift index 8b9b8e94fdbb5..025420d1468a1 100644 --- a/test/1_stdlib/Dispatch.swift +++ b/test/1_stdlib/Dispatch.swift @@ -3,6 +3,9 @@ // REQUIRES: objc_interop +// rdar://27226313 +// REQUIRES: optimized_stdlib + import Dispatch import Foundation import StdlibUnittest From ad4e7775d1b9c9d85bfae8aea274b1ab298fba27 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 1 Aug 2016 13:00:47 -0700 Subject: [PATCH 12/27] Eliminate race in swift_bridgeErrorToNSError. Huge thanks to John for noting that 'consume' didn't provide the guarantees we wanted, and to Michael G. for getting a TSan build up and running to identify/verify this race. It's possible that this is overlay strict, and that we only need to look at the domain to ensure that the code and userInfo are visible. However, TSan seems to prefix the form in this patch, so we'll be more conservative for now. Fixes rdar://problem/27541751. (cherry picked from commit e07e88706d873c34d5d939ac0126be65e3078f1c) --- stdlib/public/runtime/ErrorObject.mm | 24 ++++++++++++---------- validation-test/stdlib/ErrorProtocol.swift | 1 - 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/stdlib/public/runtime/ErrorObject.mm b/stdlib/public/runtime/ErrorObject.mm index 89c2dbb8d5370..2de9168ca1ab5 100644 --- a/stdlib/public/runtime/ErrorObject.mm +++ b/stdlib/public/runtime/ErrorObject.mm @@ -79,15 +79,10 @@ - (NSInteger)code { - (NSDictionary*)userInfo { auto error = (const SwiftError*)self; auto userInfo = error->userInfo.load(SWIFT_MEMORY_ORDER_CONSUME); - - if (userInfo) { - // Don't need to .retain.autorelease since it's immutable. - return (NSDictionary*)userInfo; - } else { - // -[NSError userInfo] never returns nil on OS X 10.8 or later. - NSDictionary *emptyDict = SWIFT_LAZY_CONSTANT(@{}); - return emptyDict; - } + assert(userInfo + && "Error box used as NSError before initialization"); + // Don't need to .retain.autorelease since it's immutable. + return (NSDictionary*)userInfo; } - (id)copyWithZone:(NSZone *)zone { @@ -319,8 +314,11 @@ typedef SWIFT_CC(swift) static id _swift_bridgeErrorToNSError_(SwiftError *errorObject) { auto ns = reinterpret_cast(errorObject); - // If we already have a domain set, then we've already initialized. - if (errorObject->domain.load(SWIFT_MEMORY_ORDER_CONSUME)) + // If we already have a domain and userInfo set, then we've already + // initialized. + // FIXME: This might be overly strict; can we look only at the domain? + if (errorObject->domain.load(std::memory_order_acquire) && + errorObject->userInfo.load(std::memory_order_acquire)) return ns; // Otherwise, calculate the domain and code (TODO: and user info), and @@ -334,6 +332,10 @@ static id _swift_bridgeErrorToNSError_(SwiftError *errorObject) { NSDictionary *userInfo = swift_stdlib_getErrorUserInfoNSDictionary(value, type, witness); + // Never produce an empty userInfo dictionary. + if (!userInfo) + userInfo = SWIFT_LAZY_CONSTANT(@{}); + // The error code shouldn't change, so we can store it blindly, even if // somebody beat us to it. The store can be relaxed, since we'll do a // store(release) of the domain last thing to publish the initialized diff --git a/validation-test/stdlib/ErrorProtocol.swift b/validation-test/stdlib/ErrorProtocol.swift index 91d7255eb7918..5c2b55711e84a 100644 --- a/validation-test/stdlib/ErrorProtocol.swift +++ b/validation-test/stdlib/ErrorProtocol.swift @@ -1,6 +1,5 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test -// REQUIRES: rdar27541751 // REQUIRES: objc_interop import SwiftPrivate From 122ad913737e945b3a47d207cc81f2b4b63af326 Mon Sep 17 00:00:00 2001 From: Roman Levenstein Date: Wed, 3 Aug 2016 15:01:19 -0700 Subject: [PATCH 13/27] [sil-cloner] Always call doPreProcess when visiting SILInstructions. It is important to call doPreProcess to correctly setup the available opened archetypes which were referenced from the original instruction being copied. This fixes a concrete bug in LoopRotate optimization and potential bugs related to cloning. rdar://27659420 --- include/swift/SIL/SILCloner.h | 11 +++++--- include/swift/SIL/SILVisitor.h | 8 ++++++ test/SILOptimizer/looprotate.sil | 46 ++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index b626080ba76c0..66ed0f72c42a7 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -66,6 +66,14 @@ class SILCloner : protected SILVisitor { SILBuilder &getBuilder() { return Builder; } protected: + void beforeVisit(ValueBase *V) { + if (auto I = dyn_cast(V)) { + // Update the set of available opened archetypes with the opened + // archetypes used by the current instruction. + doPreProcess(I); + } + } + #define VALUE(CLASS, PARENT) \ void visit##CLASS(CLASS *I) { \ llvm_unreachable("SILCloner visiting non-instruction?"); \ @@ -380,9 +388,6 @@ SILCloner::visitSILBasicBlock(SILBasicBlock* BB) { SILFunction &F = getBuilder().getFunction(); // Iterate over and visit all instructions other than the terminator to clone. for (auto I = BB->begin(), E = --BB->end(); I != E; ++I) { - // Update the set of available opened archetypes with the opened archetypes - // used by the current instruction. - doPreProcess(&*I); asImpl().visit(&*I); } // Iterate over successors to do the depth-first search. diff --git a/include/swift/SIL/SILVisitor.h b/include/swift/SIL/SILVisitor.h index e9366a2c89b55..d644c9a238dea 100644 --- a/include/swift/SIL/SILVisitor.h +++ b/include/swift/SIL/SILVisitor.h @@ -31,7 +31,15 @@ class SILVisitor { public: ImplClass &asImpl() { return static_cast(*this); } + // Peform any required pre-processing before visiting. + // Sub-classes can override it to provide their custom + // pre-processing steps. + void beforeVisit(ValueBase *V) { + } + ValueRetTy visit(ValueBase *V) { + asImpl().beforeVisit(V); + switch (V->getKind()) { #define VALUE(CLASS, PARENT) \ case ValueKind::CLASS: \ diff --git a/test/SILOptimizer/looprotate.sil b/test/SILOptimizer/looprotate.sil index a7268a9a8fa94..8f90f649b66b8 100644 --- a/test/SILOptimizer/looprotate.sil +++ b/test/SILOptimizer/looprotate.sil @@ -21,15 +21,22 @@ import Swift // CHECK: [[STRUCT:%.*]] = struct $Int32 ({{%.*}} : $Builtin.Int32) // CHECK: return [[STRUCT]] : $Int32 +protocol P { + func boo() -> Int64 +} + class Bar { + func boo() -> Int64 func foo() @objc func foo_objc() } sil @_TFC4main3Bar3foofS0_FT_T_ : $@convention(method) (@guaranteed Bar) -> () +sil @_TFC4main3Bar3boofS0_FT_T_ : $@convention(method) (@guaranteed Bar) -> Int64 sil @_TFC4main3Bar3foo_objcfS0_FT_T_ : $@convention(objc_method) (Bar) -> () sil_vtable Bar { + #Bar.boo!1: _TFC4main3Bar3boofS0_FT_T_ #Bar.foo!1: _TFC4main3Bar3foofS0_FT_T_ #Bar.foo_objc!1: _TFC4main3Bar3foofS0_FT_T_ } @@ -104,6 +111,45 @@ bb3 (%23 : $Builtin.Int32) : return %24 : $Int32 } +// The following function used to crash the compiler, because loop rotate +// could not properly handle the reference from witness_method to the +// archetype opened by open_existential_addr +// CHECK-LABEL: sil @looprotate_with_opened_archetype +// CHECK: return +sil @looprotate_with_opened_archetype : $@convention(thin) (Int32, @in P) -> Int32 { +bb0(%0 : $Int32, %25: $*P): + %1 = struct_extract %0 : $Int32, #Int32._value + %2 = integer_literal $Builtin.Int32, 0 + %30 = alloc_box $Bool + %30a = project_box %30 : $@box Bool + %40 = open_existential_addr %25 : $*P to $*@opened("C22498FA-CABF-11E5-B9A9-685B35C48C83") P + br bb1(%1 : $Builtin.Int32, %2 : $Builtin.Int32, %25: $*P, %30 : $@box Bool, %30a : $*Bool) + +bb1(%4 : $Builtin.Int32, %5 : $Builtin.Int32, %26: $*P, %31 : $@box Bool, %32 : $*Bool): + %111 = witness_method $@opened("C22498FA-CABF-11E5-B9A9-685B35C48C83") P, #P.boo!1, %40 : $*@opened("C22498FA-CABF-11E5-B9A9-685B35C48C83") P : $@convention(witness_method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + %122 = apply %111<@opened("C22498FA-CABF-11E5-B9A9-685B35C48C83") P>(%40) : $@convention(witness_method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> Int64 + %6 = struct $Int32 (%5 : $Builtin.Int32) + %8 = builtin "cmp_eq_Word"(%5 : $Builtin.Int32, %1 : $Builtin.Int32) : $Builtin.Int1 + cond_br %8, bb3, bb2 + +bb2: + %10 = integer_literal $Builtin.Int32, 1 + %12 = integer_literal $Builtin.Int1, -1 + %13 = builtin "sadd_with_overflow_Word"(%5 : $Builtin.Int32, %10 : $Builtin.Int32, %12 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) + %14 = tuple_extract %13 : $(Builtin.Int32, Builtin.Int1), 0 + %15 = enum $Optional, #Optional.some!enumelt.1, %6 : $Int32 + %16 = unchecked_enum_data %15 : $Optional, #Optional.some!enumelt.1 + %17 = struct_extract %16 : $Int32, #Int32._value + %19 = integer_literal $Builtin.Int1, -1 + %20 = builtin "sadd_with_overflow_Word"(%4 : $Builtin.Int32, %17 : $Builtin.Int32, %19 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1) + %21 = tuple_extract %20 : $(Builtin.Int32, Builtin.Int1), 0 + br bb1(%21 : $Builtin.Int32, %14 : $Builtin.Int32, %26: $*P, %31 : $@box Bool, %32 : $*Bool) + +bb3: + %23 = struct $Int32 (%4 : $Builtin.Int32) + return %23 : $Int32 +} + // Don't assert on this loop. bb2 is bb1 loop's new header after rotation but // also bb2 loop's header. From f39552453c4e875d424dfef7ca90e5e5900d1814 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Wed, 3 Aug 2016 16:50:22 -0700 Subject: [PATCH 14/27] [PrintAsObjC] Hack: Assume all option sets have typedefs. (#3961) ...because otherwise option sets that get imported as members using NS_SWIFT_NAME are printed with an 'enum' tag, and the definition of NS_OPTIONS only declares the typedef under C++. We should come back and figure out something more principled for this later, but for now this solves an issue with generated headers imported into C++ translation units. rdar://problem/27130343 (cherry picked from commit 02d25178669dc2dee3dd17ad024cc91d531822d3) --- lib/ClangImporter/ImportDecl.cpp | 4 ++++ lib/PrintAsObjC/PrintAsObjC.cpp | 6 +++++- test/PrintAsObjC/Inputs/enums.h | 23 +++++++++++++++++++++++ test/PrintAsObjC/enums.swift | 9 +++++++-- 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 test/PrintAsObjC/Inputs/enums.h diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 1398d706c8b85..de55096fe546a 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -2499,6 +2499,10 @@ namespace { if (!result) return nullptr; + // HACK: Make sure PrintAsObjC always omits the 'enum' tag for + // option set enums. + Impl.DeclsWithSuperfluousTypedefs.insert(decl); + enumeratorContext = result; break; } diff --git a/lib/PrintAsObjC/PrintAsObjC.cpp b/lib/PrintAsObjC/PrintAsObjC.cpp index 3665d6cc5bf47..7c11609f87abe 100644 --- a/lib/PrintAsObjC/PrintAsObjC.cpp +++ b/lib/PrintAsObjC/PrintAsObjC.cpp @@ -97,9 +97,13 @@ static StringRef getNameForObjC(const ValueDecl *VD, if (customNamesOnly) return StringRef(); - if (auto clangDecl = dyn_cast_or_null(VD->getClangDecl())) + if (auto clangDecl = dyn_cast_or_null(VD->getClangDecl())) { if (const clang::IdentifierInfo *II = clangDecl->getIdentifier()) return II->getName(); + if (auto *anonDecl = dyn_cast(clangDecl)) + if (auto *anonTypedef = anonDecl->getTypedefNameForAnonDecl()) + return anonTypedef->getIdentifier()->getName(); + } return VD->getName().str(); } diff --git a/test/PrintAsObjC/Inputs/enums.h b/test/PrintAsObjC/Inputs/enums.h new file mode 100644 index 0000000000000..492cb8addb3e1 --- /dev/null +++ b/test/PrintAsObjC/Inputs/enums.h @@ -0,0 +1,23 @@ +// This file is meant to be used with the mock SDK, not the real one. +#import + +#define SWIFT_NAME(X) __attribute__((swift_name(#X))) + +@interface Wrapper : NSObject +@end + +enum TopLevelRaw { TopLevelRawA }; +enum MemberRaw { MemberRawA } SWIFT_NAME(Wrapper.Raw); + +typedef enum { TopLevelAnonA } TopLevelAnon; +typedef enum { MemberAnonA } MemberAnon SWIFT_NAME(Wrapper.Anon); +typedef enum SWIFT_NAME(Wrapper.Anon2) { MemberAnon2A } MemberAnon2; + +typedef enum TopLevelTypedef { TopLevelTypedefA } TopLevelTypedef; +typedef enum SWIFT_NAME(Wrapper.Typedef) MemberTypedef { MemberTypedefA } MemberTypedef; + +typedef NS_ENUM(long, TopLevelEnum) { TopLevelEnumA }; +typedef NS_ENUM(long, MemberEnum) { MemberEnumA } SWIFT_NAME(Wrapper.Enum); + +typedef NS_OPTIONS(long, TopLevelOptions) { TopLevelOptionsA = 1 }; +typedef NS_OPTIONS(long, MemberOptions) { MemberOptionsA = 1} SWIFT_NAME(Wrapper.Options); diff --git a/test/PrintAsObjC/enums.swift b/test/PrintAsObjC/enums.swift index b574dd16a9f78..cd532c6eef730 100644 --- a/test/PrintAsObjC/enums.swift +++ b/test/PrintAsObjC/enums.swift @@ -1,7 +1,7 @@ // RUN: rm -rf %t // RUN: mkdir %t -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-source-import -emit-module -emit-module-doc -o %t %s -disable-objc-attr-requires-foundation-module -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -parse-as-library %t/enums.swiftmodule -parse -emit-objc-header-path %t/enums.h -import-objc-header %S/../Inputs/empty.h -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-source-import -emit-module -emit-module-doc -o %t %s -import-objc-header %S/Inputs/enums.h -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -parse-as-library %t/enums.swiftmodule -parse -emit-objc-header-path %t/enums.h -import-objc-header %S/Inputs/enums.h -disable-objc-attr-requires-foundation-module // RUN: FileCheck %s < %t/enums.h // RUN: FileCheck -check-prefix=NEGATIVE %s < %t/enums.h // RUN: %check-in-clang %t/enums.h @@ -21,6 +21,8 @@ import Foundation // CHECK-NEXT: - (enum NegativeValues)takeAndReturnEnum:(enum FooComments)foo; // CHECK-NEXT: - (void)acceptPlainEnum:(enum NSMalformedEnumMissingTypedef)_; // CHECK-NEXT: - (enum ObjcEnumNamed)takeAndReturnRenamedEnum:(enum ObjcEnumNamed)foo; +// CHECK-NEXT: - (void)acceptTopLevelImportedWithA:(enum TopLevelRaw)a b:(TopLevelEnum)b c:(TopLevelOptions)c d:(TopLevelTypedef)d e:(TopLevelAnon)e; +// CHECK-NEXT: - (void)acceptMemberImportedWithA:(enum MemberRaw)a b:(enum MemberEnum)b c:(MemberOptions)c d:(enum MemberTypedef)d e:(MemberAnon)e ee:(MemberAnon2)ee; // CHECK: @end @objc class AnEnumMethod { @objc func takeAndReturnEnum(_ foo: FooComments) -> NegativeValues { @@ -30,6 +32,9 @@ import Foundation @objc func takeAndReturnRenamedEnum(_ foo: EnumNamed) -> EnumNamed { return .A } + + @objc func acceptTopLevelImported(a: TopLevelRaw, b: TopLevelEnum, c: TopLevelOptions, d: TopLevelTypedef, e: TopLevelAnon) {} + @objc func acceptMemberImported(a: Wrapper.Raw, b: Wrapper.Enum, c: Wrapper.Options, d: Wrapper.Typedef, e: Wrapper.Anon, ee: Wrapper.Anon2) {} } // CHECK-LABEL: typedef SWIFT_ENUM_NAMED(NSInteger, ObjcEnumNamed, "EnumNamed") { From 9ad7cbdf6aefdb08a061e92f82d61c525c7e26ed Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Wed, 3 Aug 2016 11:41:55 -0700 Subject: [PATCH 15/27] SpriteKit overlay: add a better variant of the SKWarpGeometryGrid() initializer overlay --- stdlib/public/SDK/SpriteKit/SpriteKit.swift | 56 +++++++++++++++++++-- validation-test/stdlib/SpriteKit.swift | 50 +++++++++--------- 2 files changed, 79 insertions(+), 27 deletions(-) diff --git a/stdlib/public/SDK/SpriteKit/SpriteKit.swift b/stdlib/public/SDK/SpriteKit/SpriteKit.swift index 1666fc6598563..6da1e3fb69061 100644 --- a/stdlib/public/SDK/SpriteKit/SpriteKit.swift +++ b/stdlib/public/SDK/SpriteKit/SpriteKit.swift @@ -1,3 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + @_exported import SpriteKit import simd @@ -22,13 +34,51 @@ public typealias SKColor = UIColor @available(tvOS, introduced: 10.0) @available(watchOS, introduced: 3.0) extension SKWarpGeometryGrid { - /* init with the specified dimensions, source and dest positions. */ - public convenience init(columns: Int, rows: Int, sourcePositions: [simd.float2]? = nil, destinationPositions: [simd.float2]? = nil) { - self.init(__columns: columns, rows: rows, sourcePositions: sourcePositions, destPositions:destinationPositions) + /// Create a grid of the specified dimensions, source and destination positions. + /// + /// Grid dimensions (columns and rows) refer to the number of faces in each dimension. The + /// number of vertices required for a given dimension is equal to (cols + 1) * (rows + 1). + /// + /// SourcePositions are normalized (0.0 - 1.0) coordinates to determine the source content. + /// + /// DestinationPositions are normalized (0.0 - 1.0) positional coordinates with respect to + /// the node's native size. Values outside the (0.0-1.0) range are perfectly valid and + /// correspond to positions outside of the native undistorted bounds. + /// + /// Source and destination positions are provided in row-major order staring from the top-left. + /// For example the indices for a 2x2 grid would be as follows: + /// + /// [0]---[1]---[2] + /// | | | + /// [3]---[4]---[5] + /// | | | + /// [6]---[7]---[8] + /// + /// - Parameter columns: the number of columns to initialize the SKWarpGeometryGrid with + /// - Parameter rows: the number of rows to initialize the SKWarpGeometryGrid with + /// - Paremeter sourcePositions: the source positions for the SKWarpGeometryGrid to warp from + /// - Parameter destinationPositions: the destination positions for SKWarpGeometryGrid to warp to + public convenience init(columns: Int, rows: Int, sourcePositions: [simd.float2] = [float2](), destinationPositions: [simd.float2] = [float2]()) { + let requiredElementsCount = (columns + 1) * (rows + 1) + switch (destinationPositions.count, sourcePositions.count) { + case (0, 0): + self.init(__columns: columns, rows: rows, sourcePositions: nil, destPositions: nil) + case (let dests, 0): + _precondition(dests == requiredElementsCount, "Mismatch found between rows/columns and positions.") + self.init(__columns: columns, rows: rows, sourcePositions: nil, destPositions: destinationPositions) + case (0, let sources): + _precondition(sources == requiredElementsCount, "Mismatch found between rows/columns and positions.") + self.init(__columns: columns, rows: rows, sourcePositions: sourcePositions, destPositions: nil) + case (let dests, let sources): + _precondition(dests == requiredElementsCount && sources == requiredElementsCount, "Mismatch found between rows/columns and positions.") + self.init(__columns: columns, rows: rows, sourcePositions: sourcePositions, destPositions: destinationPositions) + } } + public func replacingBySourcePositions(positions source: [simd.float2]) -> SKWarpGeometryGrid { return self.__replacingSourcePositions(source) } + public func replacingByDestinationPositions(positions destination: [simd.float2]) -> SKWarpGeometryGrid { return self.__replacingDestPositions(destination) } diff --git a/validation-test/stdlib/SpriteKit.swift b/validation-test/stdlib/SpriteKit.swift index 8f46a577268f6..6e4685fc8ec24 100644 --- a/validation-test/stdlib/SpriteKit.swift +++ b/validation-test/stdlib/SpriteKit.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift | FileCheck %s +// RUN: %target-run-simple-swift // REQUIRES: executable_test // REQUIRES: objc_interop @@ -8,39 +8,41 @@ import StdlibUnittest import Foundation import SpriteKit +var SpriteKitTests = TestSuite("SpriteKit") + // Check that the subscript is there. @available(OSX,introduced: 10.10) @available(iOS,introduced: 8.0) @available(tvOS,introduced: 8.0) @available(watchOS,introduced: 2.0) func testSubscript(_ node: SKNode) { - var _: [SKNode] = node["me"] + var result = node["me"] + expectType(Array.self, &result) } -// SKColor is NSColor on OS X and UIColor on iOS. - -var r = CGFloat(0) -var g = CGFloat(0) -var b = CGFloat(0) -var a = CGFloat(0) -var color = SKColor.red -color.getRed(&r, green:&g, blue:&b, alpha:&a) -print("color \(r) \(g) \(b) \(a)") -// CHECK: color 1.0 0.0 0.0 1.0 - +SpriteKitTests.test("SKColor/TypeEquivalence") { + // SKColor is NSColor on OS X and UIColor on iOS. #if os(OSX) -func f(_ c: NSColor) { - print("colortastic") -} + expectEqualType(NSColor.self, SKColor.self) #elseif os(iOS) || os(tvOS) || os(watchOS) -func f(_ c: UIColor) { - print("colortastic") -} + expectEqualType(UIColor.self, SKColor.self) +#else + _UnknownOSError() #endif -f(color) -// CHECK: colortastic +} -var SpriteKitTests = TestSuite("SpriteKit") +SpriteKitTests.test("getRed(_:green:blue:alpha:)") { + var r: CGFloat = 0.0 + var g: CGFloat = 0.0 + var b: CGFloat = 0.0 + var a: CGFloat = 0.0 + let color = SKColor.red + color.getRed(&r, green: &g, blue: &b, alpha: &a) + expectEqual(1.0, r) + expectEqual(0.0, g) + expectEqual(0.0, b) + expectEqual(1.0, a) +} if #available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { SpriteKitTests.test("SKNode.setValue(_:forAttribute:)") { @@ -62,11 +64,11 @@ if #available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { warpGrid = warpGrid.replacingByDestinationPositions(positions: [float2(1.0), float2(2.0), float2(3.0), float2(4.0)]) expectEqual(warpGrid.destPosition(at: 0).x, 1.0) - warpGrid = SKWarpGeometryGrid(columns: 1, rows: 1, sourcePositions: nil, destinationPositions: [float2(1.0), float2(2.0), float2(3.0), float2(4.0)]) + warpGrid = SKWarpGeometryGrid(columns: 1, rows: 1, destinationPositions: [float2(1.0), float2(2.0), float2(3.0), float2(4.0)]) expectEqual(warpGrid.destPosition(at: 0).x, 1.0) expectEqual(warpGrid.sourcePosition(at: 0).x, 0.0) - warpGrid = SKWarpGeometryGrid(columns: 1, rows: 1, sourcePositions: [float2(1.0), float2(2.0), float2(3.0), float2(4.0)], destinationPositions: nil) + warpGrid = SKWarpGeometryGrid(columns: 1, rows: 1, sourcePositions: [float2(1.0), float2(2.0), float2(3.0), float2(4.0)]) expectEqual(warpGrid.destPosition(at: 0).x, 0.0) expectEqual(warpGrid.sourcePosition(at: 0).x, 1.0) From 7878fedd491cf1e8ed12a3c63e5511e57c984504 Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Wed, 3 Aug 2016 13:30:45 -0700 Subject: [PATCH 16/27] SpriteKit overlay: fix a typo --- stdlib/public/SDK/SpriteKit/SpriteKit.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/SDK/SpriteKit/SpriteKit.swift b/stdlib/public/SDK/SpriteKit/SpriteKit.swift index 6da1e3fb69061..b29e33cb21a1d 100644 --- a/stdlib/public/SDK/SpriteKit/SpriteKit.swift +++ b/stdlib/public/SDK/SpriteKit/SpriteKit.swift @@ -56,7 +56,7 @@ extension SKWarpGeometryGrid { /// /// - Parameter columns: the number of columns to initialize the SKWarpGeometryGrid with /// - Parameter rows: the number of rows to initialize the SKWarpGeometryGrid with - /// - Paremeter sourcePositions: the source positions for the SKWarpGeometryGrid to warp from + /// - Parameter sourcePositions: the source positions for the SKWarpGeometryGrid to warp from /// - Parameter destinationPositions: the destination positions for SKWarpGeometryGrid to warp to public convenience init(columns: Int, rows: Int, sourcePositions: [simd.float2] = [float2](), destinationPositions: [simd.float2] = [float2]()) { let requiredElementsCount = (columns + 1) * (rows + 1) From 141c0f90e46d8f42c270acd8e23b41480d5295bf Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 2 Aug 2016 16:05:36 -0700 Subject: [PATCH 17/27] [SILGen] Use the 'Class' existential representation for NSError subclasses. In addition to using the 'Class' existential representation for NSError, use it for subclasses of NSError, which can also be toll-free bridged. Narrowly addresses rdar://problem/27658748. (cherry picked from commit 3f960e306ec93d06d7e1e169273902a78505476a) --- lib/SIL/SILType.cpp | 4 ++-- test/1_stdlib/ErrorBridged.swift | 8 ++++++++ test/SILGen/objc_error.swift | 13 +++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/SIL/SILType.cpp b/lib/SIL/SILType.cpp index 33f529cf9b408..932749ad42f01 100644 --- a/lib/SIL/SILType.cpp +++ b/lib/SIL/SILType.cpp @@ -465,8 +465,8 @@ static bool isBridgedErrorClass(SILModule &M, t = archetypeType->getSuperclass(); // NSError (TODO: and CFError) can be bridged. - auto errorType = M.Types.getNSErrorType(); - if (t && errorType && t->isEqual(errorType)) { + auto nsErrorType = M.Types.getNSErrorType(); + if (t && nsErrorType && nsErrorType->isExactSuperclassOf(t, nullptr)) { return true; } diff --git a/test/1_stdlib/ErrorBridged.swift b/test/1_stdlib/ErrorBridged.swift index 65b82fc974889..618613ccf9c4f 100644 --- a/test/1_stdlib/ErrorBridged.swift +++ b/test/1_stdlib/ErrorBridged.swift @@ -582,4 +582,12 @@ ErrorBridgingTests.test("Customizing localization/recovery laziness") { } } +class MyNSError : NSError { } + +ErrorBridgingTests.test("NSError subclass identity") { + let myNSError: Error = MyNSError(domain: "MyNSError", code: 0, userInfo: [:]) + let nsError = myNSError as NSError + expectTrue(type(of: nsError) == MyNSError.self) +} + runAllTests() diff --git a/test/SILGen/objc_error.swift b/test/SILGen/objc_error.swift index 4da389b7b61d3..d46d071f04ec4 100644 --- a/test/SILGen/objc_error.swift +++ b/test/SILGen/objc_error.swift @@ -95,3 +95,16 @@ func testProduceOptionalError() -> Error? { // CHECK: function_ref @swift_convertNSErrorToError return produceOptionalError(); } + +class MyNSError : NSError { + override init() { + super.init(domain: "MyNSError", code: 0, userInfo: [:]) + } +} + +// CHECK-LABEL: sil hidden @_TF10objc_error14eraseMyNSError +// CHECK-NOT: return +// CHECK: init_existential_ref +func eraseMyNSError() -> Error { + return MyNSError() +} From fe2f9ce810f7d4c7812f433617717cad126d6d6c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 3 Aug 2016 09:12:51 -0700 Subject: [PATCH 18/27] [NSError bridging] Use embedded NSError when erasing types to Error existentials. Imported Cocoa error types are represented by structs wrapping an NSError. The conversion from these structs to Error would end up boxing the structs in _SwiftNativeNSError, losing identity and leading to a wrapping loop. Instead, extract the embedded NSError if there is one. In the Swift runtime, do this as part of the dynamic cast to NSError, using a (new, defaulted) requirement in the Error type so we can avoid an extra runtime lookup of the protocol. In SILGEn, do this by looking for the _BridgedStoredNSError protocol conformance when erasing to an Error type. Fixes SR-1562 / rdar://problem/26370984. (cherry picked from commit d2195318d2a4a58b6c611c93bdfd4e883c33c066) --- lib/SILGen/SILGen.cpp | 77 ++++++++++++++++++++++ lib/SILGen/SILGen.h | 19 ++++++ lib/SILGen/SILGenConvert.cpp | 51 ++++++++++++++ stdlib/public/SDK/Foundation/NSError.swift | 5 ++ stdlib/public/core/ErrorType.swift | 21 +++++- stdlib/public/runtime/Casting.cpp | 18 +++++ test/1_stdlib/ErrorBridged.swift | 37 +++++++++++ test/SILGen/objc_error.swift | 10 +++ 8 files changed, 236 insertions(+), 2 deletions(-) diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 4846cfb3479e2..0996993e5fc93 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -283,6 +283,83 @@ SILGenModule::getConformanceToObjectiveCBridgeable(SILLocation loc, Type type) { return nullptr; } +ProtocolDecl *SILGenModule::getBridgedStoredNSError(SILLocation loc) { + if (BridgedStoredNSError) + return *BridgedStoredNSError; + + // Find the _BridgedStoredNSError protocol. + auto &ctx = getASTContext(); + auto proto = ctx.getProtocol(KnownProtocolKind::BridgedStoredNSError); + BridgedStoredNSError = proto; + return proto; +} + +VarDecl *SILGenModule::getNSErrorRequirement(SILLocation loc) { + if (NSErrorRequirement) + return *NSErrorRequirement; + + // Find the _BridgedStoredNSError protocol. + auto proto = getBridgedStoredNSError(loc); + if (!proto) { + NSErrorRequirement = nullptr; + return nullptr; + } + + // Look for _nsError. + auto &ctx = getASTContext(); + VarDecl *found = nullptr; + for (auto member : proto->lookupDirect(ctx.Id_nsError, true)) { + if (auto var = dyn_cast(member)) { + found = var; + break; + } + } + + NSErrorRequirement = found; + return found; +} + +ProtocolConformance * +SILGenModule::getConformanceToBridgedStoredNSError(SILLocation loc, Type type) { + auto proto = getBridgedStoredNSError(loc); + if (!proto) return nullptr; + + // Find the conformance to _BridgedStoredNSError. + auto result = SwiftModule->lookupConformance(type, proto, nullptr); + if (result) return result->getConcrete(); + return nullptr; +} + +ProtocolConformance *SILGenModule::getNSErrorConformanceToError() { + if (NSErrorConformanceToError) + return *NSErrorConformanceToError; + + auto &ctx = getASTContext(); + auto nsError = ctx.getNSErrorDecl(); + if (!nsError) { + NSErrorConformanceToError = nullptr; + return nullptr; + } + + auto error = ctx.getErrorDecl(); + if (!error) { + NSErrorConformanceToError = nullptr; + return nullptr; + } + + auto conformance = + SwiftModule->lookupConformance(nsError->getDeclaredInterfaceType(), + cast(error), + nullptr); + + if (conformance && conformance->isConcrete()) + NSErrorConformanceToError = conformance->getConcrete(); + else + NSErrorConformanceToError = nullptr; + return *NSErrorConformanceToError; +} + + SILFunction *SILGenModule::emitTopLevelFunction(SILLocation Loc) { ASTContext &C = M.getASTContext(); auto extInfo = SILFunctionType::ExtInfo() diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index d4b294e1afe7f..88809359c100e 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -129,6 +129,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { Optional UnconditionallyBridgeFromObjectiveCRequirement; Optional BridgedObjectiveCType; + Optional BridgedStoredNSError; + Optional NSErrorRequirement; + + Optional NSErrorConformanceToError; + public: SILGenModule(SILModule &M, Module *SM, bool makeModuleFragile); ~SILGenModule(); @@ -377,6 +382,20 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { ProtocolConformance *getConformanceToObjectiveCBridgeable(SILLocation loc, Type type); + /// Retrieve the _BridgedStoredNSError protocol definition. + ProtocolDecl *getBridgedStoredNSError(SILLocation loc); + + /// Retrieve the _BridgedStoredNSError._nsError requirement. + VarDecl *getNSErrorRequirement(SILLocation loc); + + /// Find the conformance of the given Swift type to the + /// _BridgedStoredNSError protocol. + ProtocolConformance *getConformanceToBridgedStoredNSError(SILLocation loc, + Type type); + + /// Retrieve the conformance of NSError to the Error protocol. + ProtocolConformance *getNSErrorConformanceToError(); + /// Report a diagnostic. template InFlightDiagnostic diagnose(SourceLoc loc, Diag diag, diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp index aefd04b7aea71..103e5236805f4 100644 --- a/lib/SILGen/SILGenConvert.cpp +++ b/lib/SILGen/SILGenConvert.cpp @@ -455,6 +455,57 @@ ManagedValue SILGenFunction::emitExistentialErasure( for (auto conformance : conformances) SGM.useConformance(conformance); + // If we're erasing to the 'Error' type, and the concrete type conforms to the + // _BridgedStoredNSError protocol, call the _nsError witness getter to extract + // the NSError directly. + auto &ctx = getASTContext(); + if (existentialTL.getSemanticType().getSwiftRValueType()->getAnyNominal() == + ctx.getErrorDecl()) { + auto conformance = + SGM.getConformanceToBridgedStoredNSError(loc, concreteFormalType); + auto nsError = ctx.getNSErrorDecl(); + if (conformance && nsError && SGM.getNSErrorConformanceToError()) { + CanType nsErrorType = + nsError->getDeclaredInterfaceType()->getCanonicalType(); + if (auto witness = + conformance->getWitness(SGM.getNSErrorRequirement(loc), nullptr)) { + // Create a reference to the getter witness. + SILDeclRef getter = + getGetterDeclRef(cast(witness.getDecl()), + /*isDirectAccessorUse=*/true); + + // Compute the substitutions. + ArrayRef substitutions = + concreteFormalType->gatherAllSubstitutions( + SGM.SwiftModule, nullptr); + + // Emit the erasure, through the getter to _nsError. + ProtocolConformanceRef nsErrorConformances[1] = { + ProtocolConformanceRef(SGM.getNSErrorConformanceToError()) + }; + + return emitExistentialErasure( + loc, nsErrorType, + getTypeLowering(nsErrorType), + existentialTL, + ctx.AllocateCopy(nsErrorConformances), + C, + [&](SGFContext innerC) -> ManagedValue { + // Call the getter. + return emitGetAccessor(loc, getter, substitutions, + ArgumentSource(loc, + RValue(*this, loc, + concreteFormalType, + F(SGFContext()))), + /*isSuper=*/false, + /*isDirectAccessorUse=*/true, + RValue(), innerC) + .getAsSingleValue(*this, loc); + }); + } + } + } + switch (existentialTL.getLoweredType().getObjectType() .getPreferredExistentialRepresentation(SGM.M, concreteFormalType)) { case ExistentialRepresentation::None: diff --git a/stdlib/public/SDK/Foundation/NSError.swift b/stdlib/public/SDK/Foundation/NSError.swift index 9125e49ecbfec..3ff76b382ccdd 100644 --- a/stdlib/public/SDK/Foundation/NSError.swift +++ b/stdlib/public/SDK/Foundation/NSError.swift @@ -496,6 +496,11 @@ extension _ErrorCodeProtocol where Self._ErrorType: _BridgedStoredNSError { } extension _BridgedStoredNSError { + /// Retrieve the embedded NSError from a bridged, stored NSError. + public func _getEmbeddedNSError() -> AnyObject? { + return _nsError + } + public static func == (lhs: Self, rhs: Self) -> Bool { return lhs._nsError.isEqual(rhs._nsError) } diff --git a/stdlib/public/core/ErrorType.swift b/stdlib/public/core/ErrorType.swift index d3a8ef43df31a..433385af1b9fe 100644 --- a/stdlib/public/core/ErrorType.swift +++ b/stdlib/public/core/ErrorType.swift @@ -114,11 +114,22 @@ public protocol Error { var _domain: String { get } var _code: Int { get } var _userInfo: Any? { get } + +#if _runtime(_ObjC) + func _getEmbeddedNSError() -> AnyObject? +#endif } #if _runtime(_ObjC) -// Helper functions for the C++ runtime to have easy access to domain, -// code, and userInfo as Objective-C values. +extension Error { + /// Default implementation: there is no embedded NSError. + public func _getEmbeddedNSError() -> AnyObject? { return nil } +} +#endif + +#if _runtime(_ObjC) +// Helper functions for the C++ runtime to have easy access to embedded error, +// domain, code, and userInfo as Objective-C values. @_silgen_name("swift_stdlib_getErrorDomainNSString") public func _stdlib_getErrorDomainNSString(_ x: UnsafePointer) -> AnyObject { @@ -138,6 +149,12 @@ public func _stdlib_getErrorUserInfoNSDictionary(_ x: UnsafePointer(_ x: UnsafePointer) +-> AnyObject? { + return x.pointee._getEmbeddedNSError() +} + @_silgen_name("swift_stdlib_getErrorDefaultUserInfo") public func _stdlib_getErrorDefaultUserInfo(_ error: Error) -> AnyObject? diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index 96f600eb9c178..d034599b27e28 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -2006,14 +2006,32 @@ static bool _dynamicCastToFunction(OpaqueValue *dest, } #if SWIFT_OBJC_INTEROP +// @_silgen_name("swift_stdlib_getErrorEmbeddedNSError") +// public func _stdlib_getErrorEmbeddedNSError(_ x: UnsafePointer) +// -> AnyObject? +SWIFT_CC(swift) +extern "C" id swift_stdlib_getErrorEmbeddedNSError(const OpaqueValue *error, + const Metadata *T, + const WitnessTable *Error); + static id dynamicCastValueToNSError(OpaqueValue *src, const Metadata *srcType, const WitnessTable *srcErrorWitness, DynamicCastFlags flags) { + // Check whether there is an embedded error. + if (auto embedded = swift_stdlib_getErrorEmbeddedNSError(src, srcType, + srcErrorWitness)) { + if (flags & DynamicCastFlags::TakeOnSuccess) + srcType->vw_destroy(src); + + return embedded; + } + BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src, /*isTake*/ flags & DynamicCastFlags::TakeOnSuccess); return swift_bridgeErrorToNSError((SwiftError*)errorBox.first); } + #endif namespace { diff --git a/test/1_stdlib/ErrorBridged.swift b/test/1_stdlib/ErrorBridged.swift index 618613ccf9c4f..42ae1cbc43566 100644 --- a/test/1_stdlib/ErrorBridged.swift +++ b/test/1_stdlib/ErrorBridged.swift @@ -590,4 +590,41 @@ ErrorBridgingTests.test("NSError subclass identity") { expectTrue(type(of: nsError) == MyNSError.self) } +ErrorBridgingTests.test("Wrapped NSError identity") { + let nsError = NSError(domain: NSCocoaErrorDomain, + code: NSFileNoSuchFileError, + userInfo: [ + AnyHashable(NSFilePathErrorKey) : "/dev/null", + AnyHashable(NSStringEncodingErrorKey): /*ASCII=*/1, + ]) + + let error: Error = nsError + let nsError2: NSError = error as NSError + expectTrue(nsError === nsError2) + + // Extracting the NSError via the runtime. + let cocoaErrorAny: Any = error as! CocoaError + let nsError3: NSError = cocoaErrorAny as! NSError + expectTrue(nsError === nsError3) + + if let cocoaErrorAny2: Any = error as? CocoaError { + let nsError4: NSError = cocoaErrorAny2 as! NSError + expectTrue(nsError === nsError4) + } else { + expectUnreachable() + } + + // Extracting the NSError via direct call. + let cocoaError = error as! CocoaError + let nsError5: NSError = cocoaError as NSError + expectTrue(nsError === nsError5) + + if let cocoaError2 = error as? CocoaError { + let nsError6: NSError = cocoaError as NSError + expectTrue(nsError === nsError6) + } else { + expectUnreachable() + } +} + runAllTests() diff --git a/test/SILGen/objc_error.swift b/test/SILGen/objc_error.swift index d46d071f04ec4..d522c240afd09 100644 --- a/test/SILGen/objc_error.swift +++ b/test/SILGen/objc_error.swift @@ -108,3 +108,13 @@ class MyNSError : NSError { func eraseMyNSError() -> Error { return MyNSError() } + +// CHECK-LABEL: sil hidden @_TF10objc_error25eraseFictionalServerErrorFT_Ps5Error_ +func eraseFictionalServerError() -> Error { + // CHECK-NOT: return + // CHECK: [[NSERROR_GETTER:%[0-9]+]] = function_ref @_TFVSC20FictionalServerErrorg8_nsErrorCSo7NSError + // CHECK: [[NSERROR:%[0-9]+]] = apply [[NSERROR_GETTER]] + // CHECK: [[ERROR:%[0-9]+]] = init_existential_ref [[NSERROR]] + // CHECK: return [[ERROR]] + return FictionalServerError(.meltedDown) +} From a0128f9b02aa25a784a891af44dbbb2f21af9df4 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 3 Aug 2016 11:45:31 -0700 Subject: [PATCH 19/27] [NSError bridging] Extract embedded NSError from an Error consistently. When emitting an existential erasure to Error from an archetype, use the _getEmbeddedNSError() witness. If it produces an NSError, erase that; otherwise, go through the normal erasure path. Of course, make NSError and CFError implement _getEmbeddedNSError() so this kicks in for the obvious cases as well as the more obscure ones. Fixes the rest of SR-1562 / rdar://problem/26370984. (cherry picked from commit 75e85dc5bd5f15da98f7abc27afa74868170db74) --- include/swift/AST/ASTContext.h | 2 +- include/swift/AST/KnownDecls.def | 2 + lib/AST/ASTContext.cpp | 2 +- lib/SILGen/SILGenConvert.cpp | 121 +++++++++++++++++++-- lib/SILGen/SILGenFunction.h | 3 +- stdlib/public/SDK/Foundation/NSError.swift | 10 ++ stdlib/public/core/ErrorType.swift | 7 ++ test/1_stdlib/ErrorBridged.swift | 13 +++ test/SILGen/objc_error.swift | 27 +++++ 9 files changed, 172 insertions(+), 15 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index a69c915f3e998..0756059c1e7a0 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -364,7 +364,7 @@ class ASTContext { PrecedenceGroupDecl *right) const; /// Retrieve the declaration of Swift.Error. - NominalTypeDecl *getErrorDecl() const; + ProtocolDecl *getErrorDecl() const; CanType getExceptionType() const; /// Retrieve the declaration of Swift.Bool. diff --git a/include/swift/AST/KnownDecls.def b/include/swift/AST/KnownDecls.def index f4d371d0427ff..651f388e7c6d5 100644 --- a/include/swift/AST/KnownDecls.def +++ b/include/swift/AST/KnownDecls.def @@ -71,4 +71,6 @@ FUNC_DECL(BridgeAnythingToObjectiveC, FUNC_DECL(DidEnterMain, "_stdlib_didEnterMain") FUNC_DECL(DiagnoseUnexpectedNilOptional, "_diagnoseUnexpectedNilOptional") +FUNC_DECL(GetErrorEmbeddedNSErrorValue, "_stdlib_getErrorEmbeddedNSErrorValue") + #undef FUNC_DECL diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 803db403accd0..625f601849c3b 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -612,7 +612,7 @@ CanType ASTContext::getExceptionType() const { } } -NominalTypeDecl *ASTContext::getErrorDecl() const { +ProtocolDecl *ASTContext::getErrorDecl() const { return getProtocol(KnownProtocolKind::Error); } diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp index 103e5236805f4..1a48e917f5223 100644 --- a/lib/SILGen/SILGenConvert.cpp +++ b/lib/SILGen/SILGenConvert.cpp @@ -450,23 +450,33 @@ ManagedValue SILGenFunction::emitExistentialErasure( const TypeLowering &existentialTL, ArrayRef conformances, SGFContext C, - llvm::function_ref F) { + llvm::function_ref F, + bool allowEmbeddedNSError) { // Mark the needed conformances as used. for (auto conformance : conformances) SGM.useConformance(conformance); - // If we're erasing to the 'Error' type, and the concrete type conforms to the - // _BridgedStoredNSError protocol, call the _nsError witness getter to extract - // the NSError directly. + // If we're erasing to the 'Error' type, we might be able to get an NSError + // representation more efficiently. auto &ctx = getASTContext(); - if (existentialTL.getSemanticType().getSwiftRValueType()->getAnyNominal() == + auto nsError = ctx.getNSErrorDecl(); + if (allowEmbeddedNSError && nsError && + existentialTL.getSemanticType().getSwiftRValueType()->getAnyNominal() == ctx.getErrorDecl()) { + // Check whether the concrete type conforms to the _BridgedStoredNSError + // protocol. In that case, call the _nsError witness getter to extract the + // NSError directly. auto conformance = SGM.getConformanceToBridgedStoredNSError(loc, concreteFormalType); - auto nsError = ctx.getNSErrorDecl(); + + CanType nsErrorType = + nsError->getDeclaredInterfaceType()->getCanonicalType(); + + ProtocolConformanceRef nsErrorConformances[1] = { + ProtocolConformanceRef(SGM.getNSErrorConformanceToError()) + }; + if (conformance && nsError && SGM.getNSErrorConformanceToError()) { - CanType nsErrorType = - nsError->getDeclaredInterfaceType()->getCanonicalType(); if (auto witness = conformance->getWitness(SGM.getNSErrorRequirement(loc), nullptr)) { // Create a reference to the getter witness. @@ -480,10 +490,6 @@ ManagedValue SILGenFunction::emitExistentialErasure( SGM.SwiftModule, nullptr); // Emit the erasure, through the getter to _nsError. - ProtocolConformanceRef nsErrorConformances[1] = { - ProtocolConformanceRef(SGM.getNSErrorConformanceToError()) - }; - return emitExistentialErasure( loc, nsErrorType, getTypeLowering(nsErrorType), @@ -504,6 +510,97 @@ ManagedValue SILGenFunction::emitExistentialErasure( }); } } + + // Check whether the concrete type is an archetype. If so, call the + // _getEmbeddedNSError() witness to try to dig out the embedded NSError. + if (auto archetypeType = concreteFormalType->getAs()) { + if (std::find(archetypeType->getConformsTo().begin(), + archetypeType->getConformsTo().end(), + ctx.getErrorDecl()) + != archetypeType->getConformsTo().end()) { + auto contBB = createBasicBlock(); + auto isNotPresentBB = createBasicBlock(); + auto isPresentBB = createBasicBlock(); + + SILValue existentialResult = + contBB->createBBArg(existentialTL.getLoweredType()); + + ProtocolConformanceRef trivialErrorConformances[1] = { + ProtocolConformanceRef(ctx.getErrorDecl()) + }; + + Substitution substitutions[1] = { + Substitution(concreteFormalType, + ctx.AllocateCopy(trivialErrorConformances)) + }; + + // Call swift_stdlib_getErrorEmbeddedNSError to attempt to extract an + // NSError from the value. + ManagedValue concreteValue = F(SGFContext()); + ManagedValue potentialNSError = + emitApplyOfLibraryIntrinsic(loc, + SGM.getGetErrorEmbeddedNSErrorValue(loc), + ctx.AllocateCopy(substitutions), + { concreteValue }, + SGFContext()) + .getAsSingleValue(*this, loc); + + // Check whether we got an NSError back. + SILValue hasNSError = + emitDoesOptionalHaveValue(loc, potentialNSError.getValue()); + + B.createCondBranch(loc, hasNSError, isPresentBB, isNotPresentBB); + + // If we did get an NSError, emit the existential erasure from that + // NSError. + B.emitBlock(isPresentBB); + SILValue branchArg; + { + // Don't allow cleanups to escape the conditional block. + FullExpr presentScope(Cleanups, CleanupLocation::get(loc)); + + // Emit the existential erasure from the NSError. + branchArg = emitExistentialErasure( + loc, nsErrorType, + getTypeLowering(nsErrorType), + existentialTL, + ctx.AllocateCopy(nsErrorConformances), + C, + [&](SGFContext innerC) -> ManagedValue { + // Pull the NSError object out of the optional result. + auto &inputTL = getTypeLowering(potentialNSError.getType()); + auto nsErrorValue = + emitUncheckedGetOptionalValueFrom(loc, potentialNSError, + inputTL); + + + // Perform an unchecked cast down to NSError, because it was typed + // as 'AnyObject' for layering reasons. + return ManagedValue(B.createUncheckedRefCast( + loc, + nsErrorValue.getValue(), + getLoweredType(nsErrorType)), + nsErrorValue.getCleanup()); + + }).forward(*this); + } + B.createBranch(loc, contBB, branchArg); + + // If we did not get an NSError, just directly emit the existential + // (recursively). + B.emitBlock(isNotPresentBB); + branchArg = emitExistentialErasure(loc, concreteFormalType, concreteTL, + existentialTL, conformances, + SGFContext(), F, + /*allowEmbeddedNSError=*/false) + .forward(*this); + B.createBranch(loc, contBB, branchArg); + + // Continue. + B.emitBlock(contBB); + return emitManagedRValueWithCleanup(existentialResult, existentialTL); + } + } } switch (existentialTL.getLoweredType().getObjectType() diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 5a3f69f019f42..6d6cadd7869b6 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -944,7 +944,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction const TypeLowering &existentialTL, ArrayRef conformances, SGFContext C, - llvm::function_ref F); + llvm::function_ref F, + bool allowEmbeddedNSError = true); //===--------------------------------------------------------------------===// // Recursive entry points diff --git a/stdlib/public/SDK/Foundation/NSError.swift b/stdlib/public/SDK/Foundation/NSError.swift index 3ff76b382ccdd..fa164664be82f 100644 --- a/stdlib/public/SDK/Foundation/NSError.swift +++ b/stdlib/public/SDK/Foundation/NSError.swift @@ -241,6 +241,11 @@ extension NSError : Error { public var _domain: String { return domain } public var _code: Int { return code } public var _userInfo: Any? { return userInfo } + + /// The "embedded" NSError is itself. + public func _getEmbeddedNSError() -> AnyObject? { + return self + } } extension CFError : Error { @@ -255,6 +260,11 @@ extension CFError : Error { public var _userInfo: Any? { return CFErrorCopyUserInfo(self) as Any } + + /// The "embedded" NSError is itself. + public func _getEmbeddedNSError() -> AnyObject? { + return self + } } // An error value to use when an Objective-C API indicates error diff --git a/stdlib/public/core/ErrorType.swift b/stdlib/public/core/ErrorType.swift index 433385af1b9fe..3ddecab5c942a 100644 --- a/stdlib/public/core/ErrorType.swift +++ b/stdlib/public/core/ErrorType.swift @@ -155,6 +155,13 @@ public func _stdlib_getErrorEmbeddedNSError(_ x: UnsafePointer) return x.pointee._getEmbeddedNSError() } +/// FIXME: Quite unfortunate to have both of these. +@_silgen_name("swift_stdlib_getErrorEmbeddedNSErrorValue") +public func _stdlib_getErrorEmbeddedNSErrorValue(_ x: T) +-> AnyObject? { + return x._getEmbeddedNSError() +} + @_silgen_name("swift_stdlib_getErrorDefaultUserInfo") public func _stdlib_getErrorDefaultUserInfo(_ error: Error) -> AnyObject? diff --git a/test/1_stdlib/ErrorBridged.swift b/test/1_stdlib/ErrorBridged.swift index 42ae1cbc43566..807669213abac 100644 --- a/test/1_stdlib/ErrorBridged.swift +++ b/test/1_stdlib/ErrorBridged.swift @@ -627,4 +627,17 @@ ErrorBridgingTests.test("Wrapped NSError identity") { } } +extension Error { + func asNSError() -> NSError { + return self as NSError + } +} + +// SR-1562 +ErrorBridgingTests.test("Error archetype identity") { + let myError = NSError(domain: "myErrorDomain", code: 0, + userInfo: [ AnyHashable("one") : 1 ]) + expectTrue(myError === myError.asNSError()) +} + runAllTests() diff --git a/test/SILGen/objc_error.swift b/test/SILGen/objc_error.swift index d522c240afd09..8feac4c015997 100644 --- a/test/SILGen/objc_error.swift +++ b/test/SILGen/objc_error.swift @@ -118,3 +118,30 @@ func eraseFictionalServerError() -> Error { // CHECK: return [[ERROR]] return FictionalServerError(.meltedDown) } + +// SR-1562 +extension Error { + // CHECK-LABEL: sil hidden @_TFE10objc_errorPs5Error16convertToNSErrorfT_CSo7NSError + // CHECK: bb0([[SELF:%[0-9]+]] : $*Self) + func convertToNSError() -> NSError { + // CHECK: [[GET_EMBEDDED_FN:%[0-9]+]] = function_ref @swift_stdlib_getErrorEmbeddedNSErrorValue + // CHECK: [[EMBEDDED_RESULT_OPT:%[0-9]+]] = apply [[GET_EMBEDDED_FN]] + // CHECK: [[HAS_EMBEDDED_RESULT:%[0-9]+]] = select_enum [[EMBEDDED_RESULT_OPT]] : $Optional + // CHECK: cond_br [[HAS_EMBEDDED_RESULT]], [[SUCCESS:bb[0-9]+]], [[FAILURE:bb[0-9]+]] + + // CHECK: [[SUCCESS]]: + // CHECK: [[EMBEDDED_RESULT:%[0-9]+]] = unchecked_enum_data [[EMBEDDED_RESULT_OPT]] : $Optional, #Optional.some!enumelt.1 + // CHECK: [[EMBEDDED_NSERROR:%[0-9]+]] = unchecked_ref_cast [[EMBEDDED_RESULT]] : $AnyObject to $NSError + // CHECK: [[ERROR:%[0-9]+]] = init_existential_ref [[EMBEDDED_NSERROR]] : $NSError : $NSError, $Error + // CHECK: br [[CONTINUATION:bb[0-9]+]]([[ERROR]] : $Error) + + // CHECK: [[FAILURE]]: + // CHECK: [[ERROR_BOX:%[0-9]+]] = alloc_existential_box $Error, $Self + // CHECK: [[ERROR_PROJECTED:%[0-9]+]] = project_existential_box $Self in [[ERROR_BOX]] : $Error + // CHECK: copy_addr [[SELF]] to [initialization] [[ERROR_PROJECTED]] : $*Self + // CHECK: br [[CONTINUATION]]([[ERROR_BOX]] : $Error) + + // CHECK: [[CONTINUATION]]([[ERROR_ARG:%[0-9]+]] : $Error): + return self as NSError + } +} From 60d0f47800f7aab66d27eca816f90f100cd4a439 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 3 Aug 2016 15:37:52 -0700 Subject: [PATCH 20/27] [NSError bridging] Rename runtime entry points for _getErrorEmbeddedNSError. This clarifies the 'Indirect' case. Thanks, Dmitri! (cherry picked from commit 4b8b7bb87867ae256d5e34ba148a2dd81054adfd) --- include/swift/AST/KnownDecls.def | 2 +- lib/SILGen/SILGenConvert.cpp | 2 +- stdlib/public/core/ErrorType.swift | 10 +++++----- stdlib/public/runtime/Casting.cpp | 18 ++++++++++-------- test/SILGen/objc_error.swift | 2 +- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/include/swift/AST/KnownDecls.def b/include/swift/AST/KnownDecls.def index 651f388e7c6d5..d6e643be5e0ce 100644 --- a/include/swift/AST/KnownDecls.def +++ b/include/swift/AST/KnownDecls.def @@ -71,6 +71,6 @@ FUNC_DECL(BridgeAnythingToObjectiveC, FUNC_DECL(DidEnterMain, "_stdlib_didEnterMain") FUNC_DECL(DiagnoseUnexpectedNilOptional, "_diagnoseUnexpectedNilOptional") -FUNC_DECL(GetErrorEmbeddedNSErrorValue, "_stdlib_getErrorEmbeddedNSErrorValue") +FUNC_DECL(GetErrorEmbeddedNSError, "_stdlib_getErrorEmbeddedNSError") #undef FUNC_DECL diff --git a/lib/SILGen/SILGenConvert.cpp b/lib/SILGen/SILGenConvert.cpp index 1a48e917f5223..3ab4dd3d46791 100644 --- a/lib/SILGen/SILGenConvert.cpp +++ b/lib/SILGen/SILGenConvert.cpp @@ -539,7 +539,7 @@ ManagedValue SILGenFunction::emitExistentialErasure( ManagedValue concreteValue = F(SGFContext()); ManagedValue potentialNSError = emitApplyOfLibraryIntrinsic(loc, - SGM.getGetErrorEmbeddedNSErrorValue(loc), + SGM.getGetErrorEmbeddedNSError(loc), ctx.AllocateCopy(substitutions), { concreteValue }, SGFContext()) diff --git a/stdlib/public/core/ErrorType.swift b/stdlib/public/core/ErrorType.swift index 3ddecab5c942a..cb7b3453c8f1b 100644 --- a/stdlib/public/core/ErrorType.swift +++ b/stdlib/public/core/ErrorType.swift @@ -149,15 +149,15 @@ public func _stdlib_getErrorUserInfoNSDictionary(_ x: UnsafePointer(_ x: UnsafePointer) --> AnyObject? { +@_silgen_name("swift_stdlib_getErrorEmbeddedNSErrorIndirect") +public func _stdlib_getErrorEmbeddedNSErrorIndirect( + _ x: UnsafePointer) -> AnyObject? { return x.pointee._getEmbeddedNSError() } /// FIXME: Quite unfortunate to have both of these. -@_silgen_name("swift_stdlib_getErrorEmbeddedNSErrorValue") -public func _stdlib_getErrorEmbeddedNSErrorValue(_ x: T) +@_silgen_name("swift_stdlib_getErrorEmbeddedNSError") +public func _stdlib_getErrorEmbeddedNSError(_ x: T) -> AnyObject? { return x._getEmbeddedNSError() } diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index d034599b27e28..05afd17cd2360 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -2006,21 +2006,23 @@ static bool _dynamicCastToFunction(OpaqueValue *dest, } #if SWIFT_OBJC_INTEROP -// @_silgen_name("swift_stdlib_getErrorEmbeddedNSError") -// public func _stdlib_getErrorEmbeddedNSError(_ x: UnsafePointer) -// -> AnyObject? +// @_silgen_name("swift_stdlib_getErrorEmbeddedNSErrorIndirect") +// public func _stdlib_getErrorEmbeddedNSErrorIndirect( +/// _ x: UnsafePointer) -> AnyObject? SWIFT_CC(swift) -extern "C" id swift_stdlib_getErrorEmbeddedNSError(const OpaqueValue *error, - const Metadata *T, - const WitnessTable *Error); +extern "C" id swift_stdlib_getErrorEmbeddedNSErrorIndirect( + const OpaqueValue *error, + const Metadata *T, + const WitnessTable *Error); static id dynamicCastValueToNSError(OpaqueValue *src, const Metadata *srcType, const WitnessTable *srcErrorWitness, DynamicCastFlags flags) { // Check whether there is an embedded error. - if (auto embedded = swift_stdlib_getErrorEmbeddedNSError(src, srcType, - srcErrorWitness)) { + if (auto embedded = + swift_stdlib_getErrorEmbeddedNSErrorIndirect(src, srcType, + srcErrorWitness)) { if (flags & DynamicCastFlags::TakeOnSuccess) srcType->vw_destroy(src); diff --git a/test/SILGen/objc_error.swift b/test/SILGen/objc_error.swift index 8feac4c015997..3629204c073e1 100644 --- a/test/SILGen/objc_error.swift +++ b/test/SILGen/objc_error.swift @@ -124,7 +124,7 @@ extension Error { // CHECK-LABEL: sil hidden @_TFE10objc_errorPs5Error16convertToNSErrorfT_CSo7NSError // CHECK: bb0([[SELF:%[0-9]+]] : $*Self) func convertToNSError() -> NSError { - // CHECK: [[GET_EMBEDDED_FN:%[0-9]+]] = function_ref @swift_stdlib_getErrorEmbeddedNSErrorValue + // CHECK: [[GET_EMBEDDED_FN:%[0-9]+]] = function_ref @swift_stdlib_getErrorEmbeddedNSError // CHECK: [[EMBEDDED_RESULT_OPT:%[0-9]+]] = apply [[GET_EMBEDDED_FN]] // CHECK: [[HAS_EMBEDDED_RESULT:%[0-9]+]] = select_enum [[EMBEDDED_RESULT_OPT]] : $Optional // CHECK: cond_br [[HAS_EMBEDDED_RESULT]], [[SUCCESS:bb[0-9]+]], [[FAILURE:bb[0-9]+]] From 171a18c96c0eee984632c3ad8be20e2d08577cdc Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 3 Aug 2016 23:13:37 -0700 Subject: [PATCH 21/27] [Runtime] Extract an embedded NSError when dynamic casting to an Error existential. SILGen already attempts to extract an embedded NSError when type-erasing to an Error existential; make the runtime do the same thing dynamically. Huge thanks to Joe Groff who noticed that I missed this path. (cherry picked from commit e83fb64917bdc948eded079bc67c634c70a2ecd5) --- stdlib/public/runtime/Casting.cpp | 36 +++++++++++++++++++++---------- test/1_stdlib/ErrorBridged.swift | 13 +++++++++++ 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index 05afd17cd2360..01f1372e07a3f 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -872,6 +872,16 @@ swift_dynamicCastMetatypeToObjectUnconditional(const Metadata *metatype) { } } } + +// @_silgen_name("swift_stdlib_getErrorEmbeddedNSErrorIndirect") +// public func _stdlib_getErrorEmbeddedNSErrorIndirect( +/// _ x: UnsafePointer) -> AnyObject? +SWIFT_CC(swift) +extern "C" id swift_stdlib_getErrorEmbeddedNSErrorIndirect( + const OpaqueValue *error, + const Metadata *T, + const WitnessTable *Error); + #endif /// Perform a dynamic cast to an existential type. @@ -1040,7 +1050,20 @@ static bool _dynamicCastToExistential(OpaqueValue *dest, targetType->Protocols, &errorWitness)) return _fail(src, srcType, targetType, flags, srcDynamicType); - + +#if SWIFT_OBJC_INTEROP + // Check whether there is an embedded NSError. If so, use that for our Error + // representation. + if (auto embedded = + swift_stdlib_getErrorEmbeddedNSErrorIndirect(srcDynamicValue, + srcDynamicType, + errorWitness)) { + *destBoxAddr = reinterpret_cast(embedded); + maybeDeallocateSourceAfterSuccess(); + return true; + } +#endif + BoxPair destBox = swift_allocError(srcDynamicType, errorWitness, srcDynamicValue, /*isTake*/ canTake && (flags & DynamicCastFlags::TakeOnSuccess)); @@ -2006,20 +2029,11 @@ static bool _dynamicCastToFunction(OpaqueValue *dest, } #if SWIFT_OBJC_INTEROP -// @_silgen_name("swift_stdlib_getErrorEmbeddedNSErrorIndirect") -// public func _stdlib_getErrorEmbeddedNSErrorIndirect( -/// _ x: UnsafePointer) -> AnyObject? -SWIFT_CC(swift) -extern "C" id swift_stdlib_getErrorEmbeddedNSErrorIndirect( - const OpaqueValue *error, - const Metadata *T, - const WitnessTable *Error); - static id dynamicCastValueToNSError(OpaqueValue *src, const Metadata *srcType, const WitnessTable *srcErrorWitness, DynamicCastFlags flags) { - // Check whether there is an embedded error. + // Check whether there is an embedded NSError. if (auto embedded = swift_stdlib_getErrorEmbeddedNSErrorIndirect(src, srcType, srcErrorWitness)) { diff --git a/test/1_stdlib/ErrorBridged.swift b/test/1_stdlib/ErrorBridged.swift index 807669213abac..140f545b49e01 100644 --- a/test/1_stdlib/ErrorBridged.swift +++ b/test/1_stdlib/ErrorBridged.swift @@ -633,11 +633,24 @@ extension Error { } } +func unconditionalCast(_ x: Any, to: T.Type) -> T { + return x as! T +} + +func conditionalCast(_ x: Any, to: T.Type) -> T? { + return x as? T +} + // SR-1562 ErrorBridgingTests.test("Error archetype identity") { let myError = NSError(domain: "myErrorDomain", code: 0, userInfo: [ AnyHashable("one") : 1 ]) expectTrue(myError === myError.asNSError()) + + expectTrue(unconditionalCast(myError, to: Error.self) as NSError + === myError) + expectTrue(conditionalCast(myError, to: Error.self)! as NSError + === myError) } runAllTests() From a1e2d4edb63dfc51ddd205ef4ba85573fad73c22 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 3 Aug 2016 08:41:22 -0700 Subject: [PATCH 22/27] stdlib/Foundation: optimize the NSDictionary and NSSet copy-constructors rdar://problem/27678985 --- stdlib/public/SDK/Foundation/Foundation.swift | 65 +++++++++++++++++-- stdlib/public/SDK/Foundation/Thunks.mm | 10 +++ 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/stdlib/public/SDK/Foundation/Foundation.swift b/stdlib/public/SDK/Foundation/Foundation.swift index e59c8c8539f04..458ae79e9101a 100644 --- a/stdlib/public/SDK/Foundation/Foundation.swift +++ b/stdlib/public/SDK/Foundation/Foundation.swift @@ -1242,8 +1242,28 @@ extension NSSet { /// receiver. @nonobjc public convenience init(set anSet: NSSet) { - // FIXME: This is a bit weird. Maybe there's a better way? - self.init(set: anSet as Set as Set) + // FIXME(performance)(compiler limitation): we actually want to do just + // `self = anSet.copy()`, but Swift does not have factory + // initializers right now. + let numElems = anSet.count + let stride = MemoryLayout>.stride + let alignment = MemoryLayout>.alignment + let bufferSize = stride * numElems + assert(stride == MemoryLayout.stride) + assert(alignment == MemoryLayout.alignment) + + let rawBuffer = UnsafeMutableRawPointer.allocate( + bytes: bufferSize, alignedTo: alignment) + defer { + rawBuffer.deallocate(bytes: bufferSize, alignedTo: alignment) + _fixLifetime(anSet) + } + let valueBuffer = rawBuffer.bindMemory( + to: Optional.self, capacity: numElems) + + CFSetGetValues(anSet, valueBuffer) + let valueBufferForInit = rawBuffer.assumingMemoryBound(to: AnyObject.self) + self.init(objects: valueBufferForInit, count: numElems) } } @@ -1256,12 +1276,47 @@ extension NSDictionary { /// found in `otherDictionary`. @objc(_swiftInitWithDictionary_NSDictionary:) public convenience init(dictionary otherDictionary: NSDictionary) { - // FIXME: This is a bit weird. Maybe there's a better way? - self.init(dictionary: otherDictionary as [NSObject: AnyObject] - as [AnyHashable: Any]) + // FIXME(performance)(compiler limitation): we actually want to do just + // `self = otherDictionary.copy()`, but Swift does not have factory + // initializers right now. + let numElems = otherDictionary.count + let stride = MemoryLayout.stride + let alignment = MemoryLayout.alignment + let singleSize = stride * numElems + let totalSize = singleSize * 2 + _sanityCheck(stride == MemoryLayout.stride) + _sanityCheck(alignment == MemoryLayout.alignment) + + // Allocate a buffer containing both the keys and values. + let buffer = UnsafeMutableRawPointer.allocate( + bytes: totalSize, alignedTo: alignment) + defer { + buffer.deallocate(bytes: totalSize, alignedTo: alignment) + _fixLifetime(otherDictionary) + } + + let valueBuffer = buffer.bindMemory(to: AnyObject.self, capacity: numElems) + let buffer2 = buffer + singleSize + let keyBuffer = buffer2.bindMemory(to: AnyObject.self, capacity: numElems) + + _stdlib_NSDictionary_getObjects( + nsDictionary: otherDictionary, + objects: valueBuffer, + andKeys: keyBuffer) + + let keyBufferCopying = buffer2.assumingMemoryBound(to: NSCopying.self) + self.init(objects: valueBuffer, forKeys: keyBufferCopying, count: numElems) } } +@_silgen_name("__NSDictionaryGetObjects") +func _stdlib_NSDictionary_getObjects( + nsDictionary: NSDictionary, + objects: UnsafeMutablePointer?, + andKeys keys: UnsafeMutablePointer? +) + + //===----------------------------------------------------------------------===// // NSUndoManager //===----------------------------------------------------------------------===// diff --git a/stdlib/public/SDK/Foundation/Thunks.mm b/stdlib/public/SDK/Foundation/Thunks.mm index e9ee7ab267c40..9b3b2f80ac1aa 100644 --- a/stdlib/public/SDK/Foundation/Thunks.mm +++ b/stdlib/public/SDK/Foundation/Thunks.mm @@ -212,3 +212,13 @@ void *_Nullable contextInfo) { objc_msgSend(delegate, selector, success, contextInfo); } + +// -- NSDictionary +SWIFT_CC(swift) +extern "C" void +__NSDictionaryGetObjects(NSDictionary *_Nonnull nsDictionary, + id *objects, id *keys) { + [nsDictionary getObjects:objects andKeys:keys]; + [nsDictionary release]; +} + From 7d2ee6700366098916afd1b026c448fe33911650 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 3 Aug 2016 23:54:50 -0700 Subject: [PATCH 23/27] rdar://problem/27541751: Temporarily disable test. We're still seeing some problems on the CI from re-enabling this test, despite TSan being happy with the result. Disable this test again while we track the issue down. --- validation-test/stdlib/ErrorProtocol.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/validation-test/stdlib/ErrorProtocol.swift b/validation-test/stdlib/ErrorProtocol.swift index 5c2b55711e84a..91d7255eb7918 100644 --- a/validation-test/stdlib/ErrorProtocol.swift +++ b/validation-test/stdlib/ErrorProtocol.swift @@ -1,5 +1,6 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +// REQUIRES: rdar27541751 // REQUIRES: objc_interop import SwiftPrivate From 553c804f8ac9459df037b8739052d972295a04dd Mon Sep 17 00:00:00 2001 From: Mishal Shah Date: Wed, 3 Aug 2016 14:34:50 -0700 Subject: [PATCH 24/27] [Preset] SR-2232 Update preset to use build-swift-static-sdk-overlay --- utils/build-presets.ini | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/utils/build-presets.ini b/utils/build-presets.ini index eee22a5b9bd61..0f95798e1f1b1 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -36,6 +36,7 @@ build-ninja # Build static standard library because it is used # to build external projects. build-swift-static-stdlib +build-swift-static-sdk-overlay build-swift-stdlib-unittest-extra compiler-vendor=apple @@ -188,6 +189,7 @@ lto dash-dash build-swift-static-stdlib +build-swift-static-sdk-overlay # Don't run host tests for iOS, tvOS and watchOS platforms to make the build # faster. @@ -620,6 +622,7 @@ install-xctest install-prefix=/usr swift-install-components=autolink-driver;compiler;clang-builtin-headers;stdlib;swift-remote-mirror;sdk-overlay;license build-swift-static-stdlib +build-swift-static-sdk-overlay build-swift-stdlib-unittest-extra # Executes the lit tests for the installable package that is created @@ -683,6 +686,8 @@ install-xctest install-prefix=/usr swift-install-components=autolink-driver;compiler;clang-builtin-headers;stdlib;swift-remote-mirror;sdk-overlay;dev build-swift-static-stdlib +build-swift-static-sdk-overlay + skip-test-lldb # Executes the lit tests for the installable package that is created @@ -766,6 +771,7 @@ lldb-build-type=Release verbose-build build-ninja build-swift-static-stdlib +build-swift-static-sdk-overlay build-swift-stdlib-unittest-extra compiler-vendor=apple playgroundlogger-build-type=Release From 89a414dd8b397b4fcdea651688a6d9c1bc5f2e0c Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Thu, 4 Aug 2016 00:18:14 -0700 Subject: [PATCH 25/27] Remove redundant tests for SpriteKit These exact tests already exist in validation-test/stdlib/SpriteKit.swift. --- test/1_stdlib/SpriteKit.swift | 71 -------------------------- validation-test/stdlib/SpriteKit.swift | 17 +++++- 2 files changed, 15 insertions(+), 73 deletions(-) delete mode 100644 test/1_stdlib/SpriteKit.swift diff --git a/test/1_stdlib/SpriteKit.swift b/test/1_stdlib/SpriteKit.swift deleted file mode 100644 index e41ae035b8a12..0000000000000 --- a/test/1_stdlib/SpriteKit.swift +++ /dev/null @@ -1,71 +0,0 @@ -// RUN: %target-run-simple-swift | FileCheck %s -// REQUIRES: executable_test - -// REQUIRES: objc_interop - -// watchOS does not have SpriteKit. -// UNSUPPORTED: OS=watchos - -import StdlibUnittest - -import Foundation -import SpriteKit - -// Check that the subscript is there. -@available(OSX,introduced: 10.10) -@available(iOS,introduced: 8.0) -@available(tvOS,introduced: 8.0) -@available(watchOS,introduced: 2.0) -func testSubscript(_ node: SKNode) { - var _: [SKNode] = node["me"] -} - -// SKColor is NSColor on OS X and UIColor on iOS. - -var r = CGFloat(0) -var g = CGFloat(0) -var b = CGFloat(0) -var a = CGFloat(0) -var color = SKColor.red -color.getRed(&r, green:&g, blue:&b, alpha:&a) -print("color \(r) \(g) \(b) \(a)") -// CHECK: color 1.0 0.0 0.0 1.0 - -#if os(OSX) -func f(_ c: NSColor) { - print("colortastic") -} -#endif -#if os(iOS) || os(tvOS) -func f(_ c: UIColor) { - print("colortastic") -} -#endif -f(color) -// CHECK: colortastic - -var SpriteKitTests = TestSuite("SpriteKit") - -if #available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { - SpriteKitTests.test("SKNode.setValue(_:forAttribute:)") { - let node = SKNode() - let attrVal = SKAttributeValue(float: 2.0) - node.setValue(attrVal, forAttribute: "test") - expectEqual(node.attributeValues["test"], attrVal) - } - - SpriteKitTests.test("SKWarpGeometryGrid") { - var warpGrid = SKWarpGeometryGrid(columns: 3, rows: 4) - expectEqual(warpGrid.numberOfColumns, 3) - - expectEqual(warpGrid.sourcePosition(at: 0).x, 0.0) - warpGrid = warpGrid.replacingBySourcePositions(positions: [float2(30.0)]) - expectEqual(warpGrid.sourcePosition(at: 0).x, 30.0) - - expectEqual(warpGrid.destPosition(at: 0).x, 0.0) - warpGrid = warpGrid.replacingByDestinationPositions(positions: [float2(30.0)]) - expectEqual(warpGrid.destPosition(at: 0).x, 30.0) - } -} - -runAllTests() diff --git a/validation-test/stdlib/SpriteKit.swift b/validation-test/stdlib/SpriteKit.swift index 6e4685fc8ec24..fe1378963fa00 100644 --- a/validation-test/stdlib/SpriteKit.swift +++ b/validation-test/stdlib/SpriteKit.swift @@ -52,9 +52,9 @@ if #available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { expectEqual(node.attributeValues["test"], attrVal) } - SpriteKitTests.test("SKWarpGeometryGrid") { + SpriteKitTests.test("SKWarpGeometryGrid/1") { var warpGrid = SKWarpGeometryGrid(columns: 1, rows: 1) - expectEqual(warpGrid.numberOfColumns, 1) + expectEqual(warpGrid.numberOfColumns, 1) expectEqual(warpGrid.sourcePosition(at: 0).x, 0.0) warpGrid = warpGrid.replacingBySourcePositions(positions: [float2(1.0), float2(2.0), float2(3.0), float2(4.0)]) @@ -76,6 +76,19 @@ if #available(OSX 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) { expectEqual(warpGrid.destPosition(at: 0).x, 1.0) expectEqual(warpGrid.sourcePosition(at: 0).x, 2.0) } + + SpriteKitTests.test("SKWarpGeometryGrid/2") { + var warpGrid = SKWarpGeometryGrid(columns: 3, rows: 4) + expectEqual(warpGrid.numberOfColumns, 3) + + expectEqual(warpGrid.sourcePosition(at: 0).x, 0.0) + warpGrid = warpGrid.replacingBySourcePositions(positions: [float2(30.0)]) + expectEqual(warpGrid.sourcePosition(at: 0).x, 30.0) + + expectEqual(warpGrid.destPosition(at: 0).x, 0.0) + warpGrid = warpGrid.replacingByDestinationPositions(positions: [float2(30.0)]) + expectEqual(warpGrid.destPosition(at: 0).x, 30.0) + } } runAllTests() From 610dc149033e3d696970a1c321f4d9f096d573bc Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Wed, 3 Aug 2016 18:57:39 -0700 Subject: [PATCH 26/27] stdlib: add tests for Stride types on integers --- validation-test/stdlib/FixedPoint.swift.gyb | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/validation-test/stdlib/FixedPoint.swift.gyb b/validation-test/stdlib/FixedPoint.swift.gyb index 731480479d9f2..c6c2801887959 100644 --- a/validation-test/stdlib/FixedPoint.swift.gyb +++ b/validation-test/stdlib/FixedPoint.swift.gyb @@ -9,6 +9,7 @@ var FixedPoint = TestSuite("FixedPoint") %{ import gyb +from SwiftIntTypes import all_integer_types test_bit_patterns = [ 0x0000000000000000, @@ -61,6 +62,32 @@ def get_fixed_point_hash(bit_pattern, src_bits, word_bits): }% +//===----------------------------------------------------------------------===// +// 'Int.Stride' type +//===----------------------------------------------------------------------===// + +FixedPoint.test("Integers.Stride") { +#if arch(i386) || arch(arm) + +% for self_ty in all_integer_types(32): +% Self = self_ty.stdlib_name + expectEqualType(Int.self, ${Self}.Stride.self) +% end + +#elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) + +% for self_ty in all_integer_types(64): +% Self = self_ty.stdlib_name + expectEqualType(Int.self, ${Self}.Stride.self) +% end + +#else + +_UnimplementedError() + +#endif +} + //===----------------------------------------------------------------------===// // 'Int(truncatingBitPattern:)' initializer //===----------------------------------------------------------------------===// From 593915a21aaf7e32edf74ca2fb0aa8a93b51aae1 Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Wed, 3 Aug 2016 22:38:28 -0700 Subject: [PATCH 27/27] StdlibCollectionUnittest: reduce boilerplate --- .../LoggingWrappers.swift.gyb | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb b/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb index 94d11e5e8e1cf..d5548338abb25 100644 --- a/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb +++ b/stdlib/private/StdlibCollectionUnittest/LoggingWrappers.swift.gyb @@ -12,6 +12,7 @@ %{ from gyb_stdlib_support import TRAVERSALS, collectionForTraversal +from gyb_stdlib_unittest_support import TRACE, stackTrace, trace }% import StdlibUnittest @@ -666,12 +667,7 @@ public struct ${Self}< //===----------------------------------------------------------------------===// public func expectCustomizable( - _: T, _ counters: TypeIndexed, - //===--- TRACE boilerplate ----------------------------------------------===// - _ message: @autoclosure () -> String = "", - showFrame: Bool = true, - stackTrace: SourceLocStack = SourceLocStack(), - file: String = #file, line: UInt = #line + _: T, _ counters: TypeIndexed, ${TRACE} ) where T : LoggingType, T.Base : Wrapper, T.Base : LoggingType, @@ -683,12 +679,7 @@ public func expectCustomizable( } public func expectNotCustomizable( - _: T, _ counters: TypeIndexed, - //===--- TRACE boilerplate ----------------------------------------------===// - _ message: @autoclosure () -> String = "", - showFrame: Bool = true, - stackTrace: SourceLocStack = SourceLocStack(), - file: String = #file, line: UInt = #line + _: T, _ counters: TypeIndexed, ${TRACE} ) where T : LoggingType, T.Base : Wrapper, T.Base : LoggingType,