Skip to content

[embedded] In -assert-config Debug, print errors in assertions, preconditions, fatalErrors #72817

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 7 commits into from
Apr 10, 2024
14 changes: 14 additions & 0 deletions include/swift/SIL/SILFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,20 @@ class SILFunction
WasmImportModuleAndField = std::make_pair(module, field);
}

bool isExternForwardDeclaration() const {
if (isExternalDeclaration()) {
if (auto declContext = getDeclContext()) {
if (auto decl = declContext->getAsDecl()) {
if (decl->getAttrs().hasAttribute<ExternAttr>())
return true;
if (decl->getAttrs().hasAttribute<SILGenNameAttr>())
return true;
}
}
}
return false;
}

/// Returns true if this function belongs to a declaration that returns
/// an opaque result type with one or more availability conditions that are
/// allowed to produce a different underlying type at runtime.
Expand Down
6 changes: 3 additions & 3 deletions lib/SIL/IR/Linker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ void SILLinkerVisitor::maybeAddFunctionToWorklist(SILFunction *F,
bool setToSerializable) {
SILLinkage linkage = F->getLinkage();
assert((!setToSerializable || F->hasValidLinkageForFragileRef() ||
hasSharedVisibility(linkage)) &&
hasSharedVisibility(linkage) || F->isExternForwardDeclaration()) &&
"called function has wrong linkage for serialized function");

if (!F->isExternalDeclaration()) {
Expand Down Expand Up @@ -422,7 +422,7 @@ void SILLinkerVisitor::visitMetatypeInst(MetatypeInst *MI) {
}

void SILLinkerVisitor::visitGlobalAddrInst(GlobalAddrInst *GAI) {
if (!Mod.getASTContext().LangOpts.hasFeature(Feature::Embedded))
if (!Mod.getOptions().EmbeddedSwift)
return;

SILGlobalVariable *G = GAI->getReferencedGlobal();
Expand All @@ -448,7 +448,7 @@ void SILLinkerVisitor::process() {
Fn->setSerialized(IsSerialized_t::IsNotSerialized);
}

if (Fn->getModule().getASTContext().LangOpts.hasFeature(Feature::Embedded) &&
if (Fn->getModule().getOptions().EmbeddedSwift &&
Fn->getModule().getASTContext().LangOpts.DebuggerSupport) {
// LLDB requires that functions with bodies are not external.
Fn->setLinkage(stripExternalFromLinkage(Fn->getLinkage()));
Expand Down
3 changes: 1 addition & 2 deletions lib/SIL/IR/Linker.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,7 @@ class SILLinkerVisitor : public SILInstructionVisitor<SILLinkerVisitor, void> {
/// Is the current mode link all? Link all implies we should try and link
/// everything, not just transparent/shared functions.
bool isLinkAll() const {
return Mode == LinkingMode::LinkAll ||
Mod.getASTContext().LangOpts.hasFeature(Feature::Embedded);
return Mode == LinkingMode::LinkAll || Mod.getOptions().EmbeddedSwift;
}

void linkInVTable(ClassDecl *D);
Expand Down
10 changes: 8 additions & 2 deletions stdlib/public/core/Assert.swift
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,14 @@ public func fatalError(
_ message: @autoclosure () -> StaticString = StaticString(),
file: StaticString = #file, line: UInt = #line
) -> Never {
_assertionFailure("Fatal error", message(), file: file, line: line,
flags: _fatalErrorFlags())
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

Expand Down
5 changes: 5 additions & 0 deletions stdlib/public/core/AssertCommon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ internal func _assertionFailure(
}
}
}
#else
_embeddedReportFatalErrorInFile(prefix: prefix, message: message, file: file,
line: line)
#endif
Builtin.int_trap()
}
Expand Down Expand Up @@ -183,6 +186,8 @@ internal func _assertionFailure(
_ prefix: StaticString, _ message: StaticString,
flags: UInt32
) -> Never {
_embeddedReportFatalError(prefix: prefix, message: message)

Builtin.int_trap()
}
#endif
Expand Down
15 changes: 15 additions & 0 deletions stdlib/public/core/EmbeddedRuntime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,18 @@ public func swift_clearSensitive(buf: UnsafeMutableRawPointer, nbytes: Int) {
}
}

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

@usableFromInline
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)
}
26 changes: 26 additions & 0 deletions test/SIL/modules-extern.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: %empty-directory(%t)
// RUN: %{python} %utils/split_file.py -o %t %s

// RUN: %target-swift-frontend -emit-module -o %t/MyModule.swiftmodule %t/MyModule.swift -parse-as-library
// RUN: %target-swift-frontend -c -I %t %t/Main.swift -o %t/a.o

// BEGIN MyModule.swift

@_extern(c)
@_alwaysEmitIntoClient
func some_c_api()

@_transparent
public func publicFuncInAModule() {
some_c_api()
}

// BEGIN Main.swift

import MyModule

@_extern(c)
func some_c_api()

some_c_api()
publicFuncInAModule()
26 changes: 26 additions & 0 deletions test/SIL/modules-extern2.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: %empty-directory(%t)
// RUN: %{python} %utils/split_file.py -o %t %s

// RUN: %target-swift-frontend -emit-module -o %t/MyModule.swiftmodule %t/MyModule.swift -parse-as-library
// RUN: %target-swift-frontend -c -I %t %t/Main.swift -o %t/a.o

// BEGIN MyModule.swift

@_silgen_name("some_forward_declared_api")
@_alwaysEmitIntoClient
func some_forward_declared_api()

@_transparent
public func publicFuncInAModule() {
some_forward_declared_api()
}

// BEGIN Main.swift

import MyModule

@_silgen_name("some_forward_declared_api")
func some_forward_declared_api()

some_forward_declared_api()
publicFuncInAModule()
8 changes: 8 additions & 0 deletions test/embedded/Inputs/unbuffered-putchar.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <stdlib.h>
#include <stdio.h>

int putchar(int c) {
putchar_unlocked(c);
fflush(stdout);
return 0;
}
33 changes: 33 additions & 0 deletions test/embedded/modules-extern.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RUN: %empty-directory(%t)
// RUN: %{python} %utils/split_file.py -o %t %s

// RUN: %target-swift-frontend -emit-module -o %t/MyModule.swiftmodule %t/MyModule.swift -enable-experimental-feature Embedded -parse-as-library
// RUN: %target-swift-frontend -c -I %t %t/Main.swift -enable-experimental-feature Embedded -o %t/a.o

// REQUIRES: swift_in_compiler
// REQUIRES: OS=macosx || OS=linux-gnu

// BEGIN MyModule.swift

@_extern(c)
func some_c_api()

@_transparent
public func publicFuncInAModule() {
internalFuncInAModule()
}

@usableFromInline
internal func internalFuncInAModule() {
some_c_api()
}

// BEGIN Main.swift

import MyModule

@_extern(c)
func some_c_api()

some_c_api()
publicFuncInAModule()
31 changes: 31 additions & 0 deletions test/embedded/traps-fatalerror-exec.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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

func test() {
fatalError("task failed successfully")
// CHECK-MESSAGE: {{.*}}/traps-fatalerror-exec.swift:[[@LINE-1]]: Fatal error: task failed successfully
// CHECK-NOMESSAGE-NOT: Fatal error
// CHECK-NOMESSAGE-NOT: task failed successfully
}

test()
29 changes: 29 additions & 0 deletions test/embedded/traps-fatalerror-ir.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: %target-swift-emit-ir -enable-experimental-feature Embedded -wmo %s | %FileCheck %s --check-prefix=CHECK-MESSAGE
// RUN: %target-swift-emit-ir -enable-experimental-feature Embedded -wmo %s -O | %FileCheck %s --check-prefix=CHECK-NOMESSAGE
// RUN: %target-swift-emit-ir -enable-experimental-feature Embedded -wmo %s -Osize | %FileCheck %s --check-prefix=CHECK-NOMESSAGE
// RUN: %target-swift-emit-ir -enable-experimental-feature Embedded -wmo %s -O -assert-config Debug | %FileCheck %s --check-prefix=CHECK-MESSAGE
// RUN: %target-swift-emit-ir -enable-experimental-feature Embedded -wmo %s -Osize -assert-config Debug | %FileCheck %s --check-prefix=CHECK-MESSAGE

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

public func test() {
fatalError("task failed successfully")
}

// CHECK-MESSAGE: define {{.*}}void @"$s4main4testyyF"(){{.*}} {
// CHECK-MESSAGE: entry:
// CHECK-MESSAGE: {{.*}}call {{.*}}void @"$ss17_assertionFailure__
// CHECK-MESSAGE-SAME: Fatal error
// CHECK-MESSAGE-SAME: task failed successfully
// CHECK-MESSAGE-SAME: traps-fatalerror-ir.swift
// CHECK-MESSAGE: unreachable
// CHECK-MESSAGE: }

// CHECK-NOMESSAGE: define {{.*}}void @"$s4main4testyyF"(){{.*}} {
// 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: }
49 changes: 49 additions & 0 deletions test/embedded/traps-multiple-preconditions-ir.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// RUN: %target-swift-emit-ir -enable-experimental-feature Embedded -wmo -Xllvm -link-embedded-runtime=0 %s -O | %FileCheck %s --check-prefix=CHECK-NOMESSAGE
// RUN: %target-swift-emit-ir -enable-experimental-feature Embedded -wmo -Xllvm -link-embedded-runtime=0 %s -Osize | %FileCheck %s --check-prefix=CHECK-NOMESSAGE
// RUN: %target-swift-emit-ir -enable-experimental-feature Embedded -wmo -Xllvm -link-embedded-runtime=0 %s -O -assert-config Debug | %FileCheck %s --check-prefix=CHECK-MESSAGE
// RUN: %target-swift-emit-ir -enable-experimental-feature Embedded -wmo -Xllvm -link-embedded-runtime=0 %s -Osize -assert-config Debug | %FileCheck %s --check-prefix=CHECK-MESSAGE

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

@_extern(c)
public func external()

public func test(i: Int) {
precondition(i % 2 == 0, "precondition 1")
external()
precondition(i % 3 == 0, "precondition 3")
external()
precondition(i % 5 == 0, "precondition 5")
external()
precondition(i % 7 == 0, "precondition 7")
}

// "Non-production builds" - We expect 4 separate _assertionFailure() calls with different values
// CHECK-MESSAGE: define {{.*}}void @"$s4main4test1iySi_tF"(i64 %0) {{.*}}{
// CHECK-MESSAGE: call {{.*}}@"$ss17_assertionFailure
// CHECK-MESSAGE: unreachable
// CHECK-MESSAGE: call {{.*}}@"$ss17_assertionFailure
// CHECK-MESSAGE: unreachable
// CHECK-MESSAGE: call {{.*}}@"$ss17_assertionFailure
// CHECK-MESSAGE: unreachable
// CHECK-MESSAGE: call {{.*}}@"$ss17_assertionFailure
// CHECK-MESSAGE: unreachable
// CHECK-MESSAGE: }

// "Production builds" - We expect 4 separate trap blocks in the IR.
// CHECK-NOMESSAGE: define {{.*}}void @"$s4main4test1iySi_tF"(i64 %0) {{.*}}{
// CHECK-NOMESSAGE: tail call void asm sideeffect "", "n"(i32 0) #3
// CHECK-NOMESSAGE: tail call void @llvm.trap()
// CHECK-NOMESSAGE: unreachable
// CHECK-NOMESSAGE: tail call void asm sideeffect "", "n"(i32 1) #3
// CHECK-NOMESSAGE: tail call void @llvm.trap()
// CHECK-NOMESSAGE: unreachable
// CHECK-NOMESSAGE: tail call void asm sideeffect "", "n"(i32 2) #3
// CHECK-NOMESSAGE: tail call void @llvm.trap()
// CHECK-NOMESSAGE: unreachable
// CHECK-NOMESSAGE: tail call void asm sideeffect "", "n"(i32 3) #3
// CHECK-NOMESSAGE: tail call void @llvm.trap()
// CHECK-NOMESSAGE: unreachable
// CHECK-NOMESSAGE: }
32 changes: 32 additions & 0 deletions test/embedded/traps-precondition-exec.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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

func test(i: Int) {
precondition(i == 0, "task failed successfully")
// CHECK-MESSAGE: {{.*}}/traps-precondition-exec.swift:[[@LINE-1]]: Precondition failed: task failed successfully
// CHECK-NOMESSAGE-NOT: Fatal error
// CHECK-NOMESSAGE-NOT: Precondition failed
// CHECK-NOMESSAGE-NOT: task failed successfully
}

test(i: 42)
31 changes: 31 additions & 0 deletions test/embedded/traps-preconditionfailure-exec.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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

func test() {
preconditionFailure("task failed successfully")
// CHECK-MESSAGE: {{.*}}/traps-preconditionfailure-exec.swift:[[@LINE-1]]: Fatal error: task failed successfully
// CHECK-NOMESSAGE-NOT: Fatal error
// CHECK-NOMESSAGE-NOT: task failed successfully
}

test()