Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 92 additions & 8 deletions stdlib/public/core/Assert.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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..
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// along with the file and line information..
/// 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
Expand All @@ -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.
Expand All @@ -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)
}
}

Expand Down Expand Up @@ -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
Expand All @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest not adding this additional overload. We don't really want people omitting messages from their preconditions to save on code size; that is an unnecessary level of flexibility.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't add it, but converted the default-argument version to a separate function. Removing the overload would be a source compatibility break.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I'm saying you could just leave the default-argument version.

}
_conditionallyUnreachable()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
38 changes: 19 additions & 19 deletions test/SILOptimizer/specialize_checked_cast_branch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public func ArchetypeToArchetypeCast<T1, T2>(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
Expand All @@ -34,7 +34,7 @@ public func ArchetypeToArchetypeCast<T1, T2>(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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -97,28 +97,28 @@ func ArchetypeToConcreteCastUInt8<T>(t : T) -> NotUInt8 {
if let x = t as? NotUInt8 {
return x
}
preconditionFailure("??? Profit?")
preconditionFailure()
}

func ArchetypeToConcreteCastC<T>(t : T) -> C {
if let x = t as? C {
return x
}
preconditionFailure("??? Profit?")
preconditionFailure()
}

func ArchetypeToConcreteCastD<T>(t : T) -> D {
if let x = t as? D {
return x
}
preconditionFailure("??? Profit?")
preconditionFailure()
}

func ArchetypeToConcreteCastE<T>(t : T) -> E {
if let x = t as? E {
return x
}
preconditionFailure("??? Profit?")
preconditionFailure()
}

// uint8 -> uint8
Expand All @@ -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)

Expand Down Expand Up @@ -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)

Expand All @@ -182,15 +182,15 @@ _ = 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)

// 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)

Expand All @@ -202,19 +202,19 @@ func ConcreteToArchetypeCastUInt8<T>(t: NotUInt8, t2: T) -> T {
if let x = t as? T {
return x
}
preconditionFailure("??? Profit?")
preconditionFailure()
}
func ConcreteToArchetypeCastC<T>(t: C, t2: T) -> T {
if let x = t as? T {
return x
}
preconditionFailure("??? Profit?")
preconditionFailure()
}
func ConcreteToArchetypeCastD<T>(t: D, t2: T) -> T {
if let x = t as? T {
return x
}
preconditionFailure("??? Profit?")
preconditionFailure()
}

// x -> x where x is not a class.
Expand Down Expand Up @@ -273,14 +273,14 @@ func SuperToArchetypeCastC<T>(c : C, t : T) -> T {
if let x = c as? T {
return x
}
preconditionFailure("??? Profit?")
preconditionFailure()
}

func SuperToArchetypeCastD<T>(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
Expand Down Expand Up @@ -318,7 +318,7 @@ func ExistentialToArchetypeCast<T>(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
Expand Down