Skip to content

[embedded] Allow string-interpolating fatalError in Embedded Swift #79581

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
12 changes: 11 additions & 1 deletion stdlib/public/core/Assert.swift
Original file line number Diff line number Diff line change
Expand Up @@ -275,16 +275,26 @@ public func preconditionFailure(
/// - line: The line number to print along with `message`. The default is the
/// line number where `fatalError(_:file:line:)` is called.
@_transparent
@_unavailableInEmbedded
#if $Embedded
@_disfavoredOverload
#endif
public func fatalError(
_ message: @autoclosure () -> String = String(),
file: StaticString = #file, line: UInt = #line
) -> Never {
#if !$Embedded
_assertionFailure("Fatal error", message(), file: file, line: line,
flags: _fatalErrorFlags())
#else
if _isDebugAssertConfiguration() {
_assertionFailure("Fatal error", message(), file: file, line: line,
flags: _fatalErrorFlags())
} else {
Builtin.condfail_message(true._value,
StaticString("fatal error").unsafeRawPointer)
Builtin.unreachable()
}
#endif
}

#if $Embedded
Expand Down
11 changes: 10 additions & 1 deletion stdlib/public/core/AssertCommon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,16 @@ internal func _assertionFailure(
#if !$Embedded
@inline(never)
#else
@_disfavoredOverload
@inline(__always)
#endif
@_semantics("programtermination_point")
@_unavailableInEmbedded
internal func _assertionFailure(
_ prefix: StaticString, _ message: String,
file: StaticString, line: UInt,
flags: UInt32
) -> Never {
#if !$Embedded
prefix.withUTF8Buffer {
(prefix) -> Void in
var message = message
Expand All @@ -158,6 +159,14 @@ internal func _assertionFailure(
}
}
}
#else
if _isDebugAssertConfiguration() {
var message = message
message.withUTF8 { (messageUTF8) -> Void in
_embeddedReportFatalErrorInFile(prefix: prefix, message: messageUTF8, file: file, line: line)
}
}
#endif

Builtin.int_trap()
}
Expand Down
11 changes: 11 additions & 0 deletions stdlib/public/core/EmbeddedPrint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ public func print(_ object: some CustomStringConvertible, terminator: StaticStri
}
}

func print(_ buf: UnsafeBufferPointer<UInt8>, terminator: StaticString = "\n") {
for c in buf {
putchar(CInt(c))
}
var p = terminator.utf8Start
while p.pointee != 0 {
putchar(CInt(p.pointee))
p += 1
}
}

func printCharacters(_ buf: UnsafeRawBufferPointer) {
for unsafe c in unsafe buf {
putchar(CInt(c))
Expand Down
12 changes: 12 additions & 0 deletions stdlib/public/core/EmbeddedRuntime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -527,17 +527,29 @@ public func swift_clearSensitive(buf: UnsafeMutableRawPointer, nbytes: Int) {
}

@usableFromInline
@inline(never)
func _embeddedReportFatalError(prefix: StaticString, message: StaticString) {
print(prefix, terminator: "")
if message.utf8CodeUnitCount > 0 { print(": ", terminator: "") }
print(message)
}

@usableFromInline
@inline(never)
func _embeddedReportFatalErrorInFile(prefix: StaticString, message: StaticString, file: StaticString, line: UInt) {
print(file, terminator: ":")
print(line, terminator: ": ")
print(prefix, terminator: "")
if message.utf8CodeUnitCount > 0 { print(": ", terminator: "") }
print(message)
}

@usableFromInline
@inline(never)
func _embeddedReportFatalErrorInFile(prefix: StaticString, message: UnsafeBufferPointer<UInt8>, file: StaticString, line: UInt) {
print(file, terminator: ":")
print(line, terminator: ": ")
print(prefix, terminator: "")
if message.count > 0 { print(": ", terminator: "") }
print(message)
}
33 changes: 33 additions & 0 deletions test/embedded/traps-fatalerror-exec2.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RUN: %empty-directory(%t)
// RUN: %target-clang -x c -c %S/Inputs/unbuffered-putchar.c -o %t/unbuffered-putchar.o

// RUN: %target-build-swift -enable-experimental-feature Embedded -wmo -runtime-compatibility-version none %s -Xlinker %t/unbuffered-putchar.o -o %t/a.out
// RUN: not --crash %t/a.out 2>&1 | %FileCheck %s --check-prefix=CHECK-MESSAGE

// RUN: %target-build-swift -enable-experimental-feature Embedded -wmo -runtime-compatibility-version none %s -Xlinker %t/unbuffered-putchar.o -o %t/a.out -O
// RUN: not --crash %t/a.out 2>&1 | %FileCheck %s --check-prefix=CHECK-NOMESSAGE

// RUN: %target-build-swift -enable-experimental-feature Embedded -wmo -runtime-compatibility-version none %s -Xlinker %t/unbuffered-putchar.o -o %t/a.out -Osize
// RUN: not --crash %t/a.out 2>&1 | %FileCheck %s --check-prefix=CHECK-NOMESSAGE

// RUN: %target-build-swift -enable-experimental-feature Embedded -wmo -runtime-compatibility-version none %s -Xlinker %t/unbuffered-putchar.o -o %t/a.out -O -assert-config Debug
// RUN: not --crash %t/a.out 2>&1 | %FileCheck %s --check-prefix=CHECK-MESSAGE

// RUN: %target-build-swift -enable-experimental-feature Embedded -wmo -runtime-compatibility-version none %s -Xlinker %t/unbuffered-putchar.o -o %t/a.out -Osize -assert-config Debug
// RUN: not --crash %t/a.out 2>&1 | %FileCheck %s --check-prefix=CHECK-MESSAGE

// REQUIRES: swift_in_compiler
// REQUIRES: executable_test
// REQUIRES: optimized_stdlib
// REQUIRES: OS=macosx || OS=linux-gnu
// REQUIRES: swift_test_mode_optimize_none
// REQUIRES: swift_feature_Embedded

func testWithInterpolation(i: Int) {
fatalError("task failed successfully \(i)")
// CHECK-MESSAGE: {{.*}}/traps-fatalerror-exec2.swift:[[@LINE-1]]: Fatal error: task failed successfully 42
// CHECK-NOMESSAGE-NOT: Fatal error
// CHECK-NOMESSAGE-NOT: task failed successfully 42
}

testWithInterpolation(i: 42)
20 changes: 20 additions & 0 deletions test/embedded/traps-fatalerror-ir.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,23 @@ public func test() {
// CHECK-NOMESSAGE-NEXT: tail call void @llvm.trap()
// CHECK-NOMESSAGE-NEXT: unreachable
// CHECK-NOMESSAGE-NEXT: }

public func testWithInterpolation(i: Int) {
fatalError("task failed successfully \(i)")
}

// CHECK-MESSAGE: define {{.*}}void @"$e4main21testWithInterpolation1iySi_tF"(i64 %0){{.*}} {
// CHECK-MESSAGE: entry:
// CHECK-MESSAGE: task failed successfully
// CHECK-MESSAGE: {{.*}}call {{.*}}void @"${{(es17_assertionFailure__|es31_embeddedReportFatalErrorInFile6prefix7message4file4lineys12StaticStringV_SRys5UInt8VGAGSutF)}}
// CHECK-MESSAGE-SAME: Fatal error
// CHECK-MESSAGE-SAME: traps-fatalerror-ir.swift
// CHECK-MESSAGE: unreachable
// CHECK-MESSAGE: }

// CHECK-NOMESSAGE: define {{.*}}void @"$e4main21testWithInterpolation1iySi_tF"(i64 %0){{.*}} {
// CHECK-NOMESSAGE-NEXT: entry:
// CHECK-NOMESSAGE-NEXT: tail call void asm sideeffect "", "n"(i32 0)
// CHECK-NOMESSAGE-NEXT: tail call void @llvm.trap()
// CHECK-NOMESSAGE-NEXT: unreachable
// CHECK-NOMESSAGE-NEXT: }