diff --git a/stdlib/public/core/Assert.swift b/stdlib/public/core/Assert.swift index d88d5b0580002..5da93bbc40759 100644 --- a/stdlib/public/core/Assert.swift +++ b/stdlib/public/core/Assert.swift @@ -59,10 +59,12 @@ public func assert( /// /// * In playgrounds and `-Onone` builds (the default for Xcode's Debug /// configuration): If `condition` evaluates to `false`, stop program -/// execution in a debuggable state after printing `message`. +/// execution in a debuggable state after printing `message` +/// along with the file and line information.. /// /// * In `-O` builds (the default for Xcode's Release configuration): If -/// `condition` evaluates to `false`, stop program execution. +/// `condition` evaluates to `false`, stop program execution state after +/// printing `message`. /// /// * In `-Ounchecked` builds, `condition` is not evaluated, but the optimizer /// may assume that it *always* evaluates to `true`. Failure to satisfy that @@ -82,7 +84,7 @@ public func assert( @_transparent public func precondition( _ condition: @autoclosure () -> Bool, - _ message: @autoclosure () -> String = String(), + _ message: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line ) { // Only check in debug and release mode. In release mode just trap. @@ -91,10 +93,55 @@ public func precondition( _assertionFailure("Precondition failed", message(), file: file, line: line, flags: _fatalErrorFlags()) } + } else if _isReleaseAssertConfiguration() { + if !_fastPath(condition()) { + _assertionFailure("Precondition failed", message(), + flags: _fatalErrorFlags()) + } + } +} + +/// Checks a necessary condition for making forward progress. +/// +/// Use this function to detect conditions that must prevent the program from +/// proceeding, even in shipping code, but without a failure message. +/// +/// * In playgrounds and `-Onone` builds (the default for Xcode's Debug +/// configuration): If `condition` evaluates to `false`, stop program +/// execution in a debuggable state after printing "Precondition failed", +/// along with the file and line information. +/// +/// * In `-O` builds (the default for Xcode's Release configuration): If +/// `condition` evaluates to `false`, stop program execution. +/// +/// * In `-Ounchecked` builds, `condition` is not evaluated, but the optimizer +/// may assume that it *always* evaluates to `true`. Failure to satisfy that +/// assumption is a serious programming error. +/// +/// - Parameters: +/// - condition: The condition to test. `condition` is not evaluated in +/// `-Ounchecked` builds. +/// - file: The file name to print with `message` if the precondition fails. +/// The default is the file where `precondition(_:_:file:line:)` is +/// called. +/// - line: The line number to print along with `message` if the assertion +/// fails. The default is the line number where +/// `precondition(_:_:file:line:)` is called. +@_transparent +public func precondition( + _ condition: @autoclosure () -> Bool, + file: StaticString = #file, line: UInt = #line +) { + // Only check in debug and release mode. In release mode just trap. + if _isDebugAssertConfiguration() { + if !_fastPath(condition()) { + _assertionFailure("Precondition failed", String(), file: file, line: line, + flags: _fatalErrorFlags()) + } } else if _isReleaseAssertConfiguration() { let error = !condition() Builtin.condfail_message(error._value, - StaticString("precondition failure").unsafeRawPointer) + StaticString("Precondition failed").unsafeRawPointer) } } @@ -146,10 +193,10 @@ public func assertionFailure( /// /// * In playgrounds and `-Onone` builds (the default for Xcode's Debug /// configuration), stops program execution in a debuggable state after -/// printing `message`. +/// printing `message` along with the file and line information. /// /// * In `-O` builds (the default for Xcode's Release configuration), stops -/// program execution. +/// program execution after printing `message`. /// /// * In `-Ounchecked` builds, the optimizer may assume that this function is /// never called. Failure to satisfy that assumption is a serious @@ -164,16 +211,53 @@ public func assertionFailure( /// line number where `preconditionFailure(_:file:line:)` is called. @_transparent public func preconditionFailure( - _ message: @autoclosure () -> String = String(), + _ message: @autoclosure () -> String, file: StaticString = #file, line: UInt = #line ) -> Never { // Only check in debug and release mode. In release mode just trap. if _isDebugAssertConfiguration() { _assertionFailure("Fatal error", message(), file: file, line: line, flags: _fatalErrorFlags()) + } else if _isReleaseAssertConfiguration() { + _assertionFailure("Precondition failed", message(), + flags: _fatalErrorFlags()) + } + _conditionallyUnreachable() +} + +/// Indicates that a precondition was violated, without a message. +/// +/// Use this function to stop the program when control flow can only reach the +/// call if your API was improperly used. This function's effects vary +/// depending on the build flag used: +/// +/// * In playgrounds and `-Onone` builds (the default for Xcode's Debug +/// configuration), stops program execution in a debuggable state after +/// printing "Fatal error". +/// +/// * In `-O` builds (the default for Xcode's Release configuration), stops +/// program execution. +/// +/// * In `-Ounchecked` builds, the optimizer may assume that this function is +/// never called. Failure to satisfy that assumption is a serious +/// programming error. +/// +/// - Parameters: +/// - file: The file name to print with `message`. The default is the file +/// where `preconditionFailure(file:line:)` is called. +/// - line: The line number to print. The default is the +/// line number where `preconditionFailure(file:line:)` is called. +@_transparent +public func preconditionFailure( + file: StaticString = #file, line: UInt = #line +) -> Never { + // Only check in debug and release mode. In release mode just trap. + if _isDebugAssertConfiguration() { + _assertionFailure("Fatal error", String(), file: file, line: line, + flags: _fatalErrorFlags()) } else if _isReleaseAssertConfiguration() { Builtin.condfail_message(true._value, - StaticString("precondition failure").unsafeRawPointer) + StaticString("Precondition failed").unsafeRawPointer) } _conditionallyUnreachable() } diff --git a/test/Frontend/OptimizationOptions-without-stdlib-checks.swift b/test/Frontend/OptimizationOptions-without-stdlib-checks.swift index c51ab8a7834c4..2451819566e9f 100644 --- a/test/Frontend/OptimizationOptions-without-stdlib-checks.swift +++ b/test/Frontend/OptimizationOptions-without-stdlib-checks.swift @@ -100,10 +100,10 @@ func test_partial_safety_check(x: Int, y: Int) -> Int { // In release mode keep succinct library precondition checks (trap). // RELEASE-LABEL: sil hidden @$s19OptimizationOptions22testprecondition_check1x1yS2i_SitF : $@convention(thin) (Int, Int) -> Int { -// RELEASE-NOT: "Fatal error" -// RELEASE: %[[V2:.+]] = builtin "xor_Int1"(%{{.+}}, %{{.+}}) -// RELEASE: cond_fail %[[V2]] -// RELEASE: return +// RELEASE-DAG: "Precondition failed" +// RELEASE-DAG: %[[FATAL_ERROR:.+]] = function_ref @[[FATAL_ERROR_FUNC:.*assertionFailure.*]] +// RELEASE: apply %[[FATAL_ERROR]]({{.*}}) +// RELEASE: unreachable // In unchecked mode remove library precondition checks. // UNCHECKED-LABEL: sil hidden @$s19OptimizationOptions22testprecondition_check1x1yS2i_SitF : $@convention(thin) (Int, Int) -> Int { diff --git a/test/SILOptimizer/specialize_checked_cast_branch.swift b/test/SILOptimizer/specialize_checked_cast_branch.swift index ca3162c2d0c8d..6d4d0fa5dc001 100644 --- a/test/SILOptimizer/specialize_checked_cast_branch.swift +++ b/test/SILOptimizer/specialize_checked_cast_branch.swift @@ -22,7 +22,7 @@ public func ArchetypeToArchetypeCast(t1 : T1, t2 : T2) -> T2 { if let x = t1 as? T2 { return x } - preconditionFailure("??? Profit?") + preconditionFailure() } // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch011ArchetypeToE4Cast2t12t2q_x_q_tr0_lFAA1CC_AA1DCTg5 : $@convention(thin) (@guaranteed C, @guaranteed D) -> @owned D @@ -34,7 +34,7 @@ public func ArchetypeToArchetypeCast(t1 : T1, t2 : T2) -> T2 { // CHECK: return [[T0]] // // CHECK: bb2 -// CHECK: cond_fail {{%.*}}, "precondition failure" +// CHECK: cond_fail {{%.*}}, "Precondition failed" // CHECK: unreachable // CHECK: } // end sil function '$s30specialize_checked_cast_branch011ArchetypeToE4Cast2t12t2q_x_q_tr0_lFAA1CC_AA1DCTg5' _ = ArchetypeToArchetypeCast(t1: c, t2: d) @@ -58,7 +58,7 @@ _ = ArchetypeToArchetypeCast(t1: b, t2: f) // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch011ArchetypeToE4Cast2t12t2q_x_q_tr0_lFAA8NotUInt8V_AA1CCTg5 : $@convention(thin) (NotUInt8, @guaranteed C) -> @owned C { // CHECK: bb0 // CHECK-NOT: bb1 -// CHECK: cond_fail {{%.*}}, "precondition failure" +// CHECK: cond_fail {{%.*}}, "Precondition failed" // CHECK: unreachable // CHECK: } // end sil function '$s30specialize_checked_cast_branch011ArchetypeToE4Cast2t12t2q_x_q_tr0_lFAA8NotUInt8V_AA1CCTg5' _ = ArchetypeToArchetypeCast(t1: b, t2: c) @@ -67,7 +67,7 @@ _ = ArchetypeToArchetypeCast(t1: b, t2: c) // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch011ArchetypeToE4Cast2t12t2q_x_q_tr0_lFAA1CC_AA8NotUInt8VTg5 : $@convention(thin) (@guaranteed C, NotUInt8) -> NotUInt8 { // CHECK: bb0 // CHECK-NOT: bb1 -// CHECK: cond_fail {{%.*}}, "precondition failure" +// CHECK: cond_fail {{%.*}}, "Precondition failed" // CHECK: unreachable // CHECK: } // end sil function '$s30specialize_checked_cast_branch011ArchetypeToE4Cast2t12t2q_x_q_tr0_lFAA1CC_AA8NotUInt8VTg5' _ = ArchetypeToArchetypeCast(t1: c, t2: b) @@ -84,7 +84,7 @@ _ = ArchetypeToArchetypeCast(t1: d, t2: c) // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch011ArchetypeToE4Cast2t12t2q_x_q_tr0_lFAA1CC_AA1ECTg5 : $@convention(thin) (@guaranteed C, @guaranteed E) -> @owned E { // CHECK: bb0 // CHECK-NOT: bb1 -// CHECK: cond_fail {{%.*}}, "precondition failure" +// CHECK: cond_fail {{%.*}}, "Precondition failed" // CHECK: unreachable // CHECK: } // end sil function '$s30specialize_checked_cast_branch011ArchetypeToE4Cast2t12t2q_x_q_tr0_lFAA1CC_AA1ECTg5' _ = ArchetypeToArchetypeCast(t1: c, t2: e) @@ -97,28 +97,28 @@ func ArchetypeToConcreteCastUInt8(t : T) -> NotUInt8 { if let x = t as? NotUInt8 { return x } - preconditionFailure("??? Profit?") + preconditionFailure() } func ArchetypeToConcreteCastC(t : T) -> C { if let x = t as? C { return x } - preconditionFailure("??? Profit?") + preconditionFailure() } func ArchetypeToConcreteCastD(t : T) -> D { if let x = t as? D { return x } - preconditionFailure("??? Profit?") + preconditionFailure() } func ArchetypeToConcreteCastE(t : T) -> E { if let x = t as? E { return x } - preconditionFailure("??? Profit?") + preconditionFailure() } // uint8 -> uint8 @@ -135,7 +135,7 @@ _ = ArchetypeToConcreteCastUInt8(t: c) // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch28ArchetypeToConcreteCastUInt81tAA03NotI0Vx_tlFAA0J6UInt64V_Tg5 : $@convention(thin) (NotUInt64) -> NotUInt8 { // CHECK: bb0 // CHECK-NOT: checked_cast_br -// CHECK: cond_fail {{%.*}}, "precondition failure" +// CHECK: cond_fail {{%.*}}, "Precondition failed" // CHECK: unreachable _ = ArchetypeToConcreteCastUInt8(t: f) @@ -167,7 +167,7 @@ _ = ArchetypeToConcreteCastC(t: d) // E -> C // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch24ArchetypeToConcreteCastC1tAA1CCx_tlFAA1EC_Tg5 : $@convention(thin) (@guaranteed E) -> @owned C { // CHECK: bb0 -// CHECK: cond_fail {{%.*}}, "precondition failure" +// CHECK: cond_fail {{%.*}}, "Precondition failed" // CHECK: unreachable _ = ArchetypeToConcreteCastC(t: e) @@ -182,7 +182,7 @@ _ = ArchetypeToConcreteCastC(t: e) // CHECK: return [[T0]] : $D // // CHECK: [[FAIL_BB]]: -// CHECK: cond_fail {{%.*}}, "precondition failure" +// CHECK: cond_fail {{%.*}}, "Precondition failed" // CHECK: unreachable // CHECK: } // end sil function '$s30specialize_checked_cast_branch24ArchetypeToConcreteCastD1tAA1DCx_tlFAA1CC_Tg5' _ = ArchetypeToConcreteCastD(t: c) @@ -190,7 +190,7 @@ _ = ArchetypeToConcreteCastD(t: c) // C -> E // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch24ArchetypeToConcreteCastE1tAA1ECx_tlFAA1CC_Tg5 : $@convention(thin) (@guaranteed C) -> @owned E { // CHECK: bb0 -// CHECK: cond_fail {{%.*}}, "precondition failure" +// CHECK: cond_fail {{%.*}}, "Precondition failed" // CHECK: unreachable _ = ArchetypeToConcreteCastE(t: c) @@ -202,19 +202,19 @@ func ConcreteToArchetypeCastUInt8(t: NotUInt8, t2: T) -> T { if let x = t as? T { return x } - preconditionFailure("??? Profit?") + preconditionFailure() } func ConcreteToArchetypeCastC(t: C, t2: T) -> T { if let x = t as? T { return x } - preconditionFailure("??? Profit?") + preconditionFailure() } func ConcreteToArchetypeCastD(t: D, t2: T) -> T { if let x = t as? T { return x } - preconditionFailure("??? Profit?") + preconditionFailure() } // x -> x where x is not a class. @@ -273,14 +273,14 @@ func SuperToArchetypeCastC(c : C, t : T) -> T { if let x = c as? T { return x } - preconditionFailure("??? Profit?") + preconditionFailure() } func SuperToArchetypeCastD(d : D, t : T) -> T { if let x = d as? T { return x } - preconditionFailure("??? Profit?") + preconditionFailure() } // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch21SuperToArchetypeCastC1c1txAA1CC_xtlFAF_Tg5 : $@convention(thin) (@guaranteed C, @guaranteed C) -> @owned C @@ -318,7 +318,7 @@ func ExistentialToArchetypeCast(o : AnyObject, t : T) -> T { if let x = o as? T { return x } - preconditionFailure("??? Profit?") + preconditionFailure() } // CHECK-LABEL: sil shared @$s30specialize_checked_cast_branch26ExistentialToArchetypeCast1o1txyXl_xtlFAA1CC_Tg5 : $@convention(thin) (@guaranteed AnyObject, @guaranteed C) -> @owned C