Represent Swift errors as DW_TAG_thrown_type in DWARF

This allows the debugger to display them alongside the function's
return value when finish-ing a function.

rdar://problem/29481673
This commit is contained in:
Adrian Prantl
2017-04-27 09:45:54 -07:00
parent 2f31479428
commit d50448a55f
3 changed files with 79 additions and 32 deletions

View File

@@ -697,7 +697,7 @@ IRGenDebugInfo::emitFunction(const SILDebugScope *DS, llvm::Function *Fn,
llvm::DITemplateParameterArray TemplateParameters = nullptr;
llvm::DISubprogram *Decl = nullptr;
// Various flags
// Various flags.
bool IsLocalToUnit = Fn ? Fn->hasInternalLinkage() : true;
bool IsDefinition = true;
bool IsOptimized = Opts.Optimize;
@@ -722,9 +722,20 @@ IRGenDebugInfo::emitFunction(const SILDebugScope *DS, llvm::Function *Fn,
== SILFunctionType::Representation::Block)
Flags |= llvm::DINode::FlagAppleBlock;
// Get the throws information.
llvm::DITypeArray Error = nullptr;
if (FnTy)
if (auto ErrorInfo = FnTy->getOptionalErrorResult()) {
auto DTI = DebugTypeInfo::getFromTypeInfo(
nullptr, nullptr, ErrorInfo->getType(),
IGM.getTypeInfo(IGM.silConv.getSILType(*ErrorInfo)));
Error = DBuilder.getOrCreateArray({getOrCreateType(DTI)}).get();
}
// Construct the DISubprogram.
llvm::DISubprogram *SP = DBuilder.createFunction(
Scope, Name, LinkageName, File, Line, DIFnTy, IsLocalToUnit, IsDefinition,
ScopeLine, Flags, IsOptimized, TemplateParameters, Decl);
ScopeLine, Flags, IsOptimized, TemplateParameters, Decl, Error);
if (Fn && !Fn->isDeclaration())
Fn->setSubprogram(SP);

View File

@@ -0,0 +1,34 @@
// RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s
// REQUIRES: CPU=i386
class Obj {}
enum MyError : Error {
case Simple
case WithObj(Obj)
}
// i386 does not pass swifterror in a register. To support debugging of the
// thrown error we create a shadow stack location holding the address of the
// location that holds the pointer to the error instead.
func simple(_ placeholder: Int64) throws -> () {
// CHECK: define {{.*}}void @_T06Errors6simpleys5Int64VKF(i64, %swift.refcounted* swiftself, %swift.error**)
// CHECK: call void @llvm.dbg.declare
// CHECK: call void @llvm.dbg.declare({{.*}}, metadata ![[ERROR:[0-9]+]], metadata ![[DEREF:[0-9]+]])
// CHECK: ![[ERROR]] = !DILocalVariable(name: "$error", arg: 2,
// CHECK-SAME: type: ![[ERRTY:.*]], flags: DIFlagArtificial)
// CHECK: ![[ERRTY]] = !DICompositeType({{.*}}identifier: "_T0s5Error_pD"
// CHECK: ![[DEREF]] = !DIExpression(DW_OP_deref)
throw MyError.Simple
}
func obj() throws -> () {
throw MyError.WithObj(Obj())
}
public func foo() {
do {
try simple(1)
try obj()
}
catch {}
}

View File

@@ -1,34 +1,36 @@
// RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s
// REQUIRES: CPU=i386
class Obj {}
public enum E : Error { case Err }
enum MyError : Error {
case Simple
case WithObj(Obj)
// Function throws.
public func throwError() throws { throw E.Err }
// CHECK: !DISubprogram(name: "throwError", {{.*}}thrownTypes: ![[THROWN:.*]])
// CHECK: ![[THROWN]] = !{![[ERROR:[0-9]+]]}
// CHECK: ![[ERROR]] = !DICompositeType(tag: DW_TAG_structure_type,
// CHECK-SAME: name: "Error"
// Function rethrows.
public func rethrow(fn : (() throws -> ())) rethrows { try fn() }
// CHECK: !DISubprogram(name: "rethrow", {{.*}}thrownTypes: ![[THROWN:.*]])
public class C {
// Initializer throws.
init() throws { throw E.Err }
// CHECK: !DISubprogram(name: "init", {{.*}}line: [[@LINE-1]],
// CHECK-SAME: thrownTypes: ![[THROWN:.*]])
// Initializer rethrows.
init(fn : (() throws -> ())) rethrows {
// CHECK: !DISubprogram(name: "init", {{.*}}line: [[@LINE-1]],
// CHECK-SAME: thrownTypes: ![[THROWN:.*]])
try fn()
}
}
// i386 does not pass swifterror in a register. To support debugging of the
// thrown error we create a shadow stack location holding the address of the
// location that holds the pointer to the error instead.
func simple(_ placeholder: Int64) throws -> () {
// CHECK: define {{.*}}void @_T06Errors6simpleys5Int64VKF(i64, %swift.refcounted* swiftself, %swift.error**)
// CHECK: call void @llvm.dbg.declare
// CHECK: call void @llvm.dbg.declare({{.*}}, metadata ![[ERROR:[0-9]+]], metadata ![[DEREF:[0-9]+]])
// CHECK: ![[ERROR]] = !DILocalVariable(name: "$error", arg: 2,
// CHECK-SAME: type: ![[ERRTY:.*]], flags: DIFlagArtificial)
// CHECK: ![[ERRTY]] = !DICompositeType({{.*}}identifier: "_T0s5Error_pD"
// CHECK: ![[DEREF]] = !DIExpression(DW_OP_deref)
throw MyError.Simple
}
func obj() throws -> () {
throw MyError.WithObj(Obj())
}
public func foo() {
do {
try simple(1)
try obj()
}
catch {}
}
// Negative tests.
// CHECK: !DISubprogram(name: "returnThrowing",
// CHECK-NOT: thrownTypes:
public func returnThrowing() -> (() throws -> ()) { return throwError }
// CHECK: !DISubprogram(name: "takesThrowing",
// CHECK-NOT: thrownTypes:
public func takesThrowing(fn : (() throws -> ())) {}