Skip to content

Commit 5dc71aa

Browse files
committed
AST/SIL: support source location in diagnostics for de-serialized debug info
Diagnostics only work with `SourceLoc` which is basically a pointer into a buffer of the loaded source file. But when debug info is de-serialized, the SIL `Location` consists of a filename+line+column. To "convert" this to a `SourceLoc`, the file must be loaded. This change adds `DiagnosticEngine.getLocationFromExternalSource` for this purpose. Also, the new protocol `ProvidingSourceLocation` - to which `SourceLoc` and `Location` conform - help to generalize the helper struct `Diagnostic` and make this "conversion" happen automatically.
1 parent d12cb84 commit 5dc71aa

File tree

9 files changed

+95
-17
lines changed

9 files changed

+95
-17
lines changed

SwiftCompilerSources/Sources/AST/DiagnosticEngine.swift

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,24 +138,40 @@ public struct DiagnosticEngine {
138138
diagnose(id, args, at: position, highlight: highlight, fixIts: fixIts)
139139
}
140140

141-
public func diagnose(_ diagnostic: Diagnostic) {
142-
diagnose(diagnostic.id, diagnostic.arguments, at: diagnostic.position)
141+
public func diagnose<SourceLocation: ProvidingSourceLocation>(_ diagnostic: Diagnostic<SourceLocation>) {
142+
let loc = diagnostic.location.getSourceLocation(diagnosticEngine: self)
143+
diagnose(diagnostic.id, diagnostic.arguments, at: loc)
143144
}
145+
146+
/// Loads the file at `path` and returns a `SourceLoc` pointing to `line` and `column` in the file.
147+
/// Returns nil if the file cannot be loaded.
148+
public func getLocationFromExternalSource(path: StringRef, line: Int, column: Int) -> SourceLoc? {
149+
return SourceLoc(bridged: bridged.getLocationFromExternalSource(path: path._bridged, line: line, column: column))
150+
}
151+
}
152+
153+
/// Something which can provide a `SourceLoc` for diagnostics.
154+
public protocol ProvidingSourceLocation {
155+
func getSourceLocation(diagnosticEngine: DiagnosticEngine) -> SourceLoc?
156+
}
157+
158+
extension SourceLoc: ProvidingSourceLocation {
159+
public func getSourceLocation(diagnosticEngine: DiagnosticEngine) -> SourceLoc? { self }
144160
}
145161

146162
/// A utility struct which allows throwing a Diagnostic.
147-
public struct Diagnostic : Error {
163+
public struct Diagnostic<SourceLocation: ProvidingSourceLocation> : Error {
148164
public let id: DiagID
149165
public let arguments: [DiagnosticArgument]
150-
public let position: SourceLoc?
166+
public let location: SourceLocation
151167

152-
public init(_ id: DiagID, _ arguments: DiagnosticArgument..., at position: SourceLoc?) {
153-
self.init(id, arguments, at: position)
168+
public init(_ id: DiagID, _ arguments: DiagnosticArgument..., at location: SourceLocation) {
169+
self.init(id, arguments, at: location)
154170
}
155171

156-
public init(_ id: DiagID, _ arguments: [DiagnosticArgument], at position: SourceLoc?) {
172+
public init(_ id: DiagID, _ arguments: [DiagnosticArgument], at location: SourceLocation) {
157173
self.id = id
158174
self.arguments = arguments
159-
self.position = position
175+
self.location = location
160176
}
161177
}

SwiftCompilerSources/Sources/Basic/SourceLoc.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212

1313
import BasicBridging
1414

15+
/// Represents a location in source code.
16+
/// It is basically a pointer into a buffer of the loaded source file (managed by `DiagnosticEngine`).
17+
/// In contrast to just having a filename+line+column, this allows displaying the context around
18+
/// the location when printing diagnostics.
1519
public struct SourceLoc {
16-
/// Points into a source file.
1720
public let bridged: BridgedSourceLoc
1821

1922
public init?(bridged: BridgedSourceLoc) {

SwiftCompilerSources/Sources/Optimizer/ModulePasses/EmbeddedSwiftDiagnostics.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ let embeddedSwiftDiagnostics = ModulePass(name: "embedded-swift-diagnostics") {
3737
do {
3838
assert(checker.callStack.isEmpty)
3939
try checker.checkFunction(function)
40-
} catch let error as Diagnostic {
40+
} catch let error as Diagnostic<Location> {
4141
checker.diagnose(error)
4242
} catch {
4343
fatalError("unknown error thrown")
@@ -248,9 +248,9 @@ private struct FunctionChecker {
248248
}
249249
}
250250

251-
mutating func diagnose(_ error: Diagnostic) {
251+
mutating func diagnose(_ error: Diagnostic<Location>) {
252252
var diagPrinted = false
253-
if error.position != nil {
253+
if error.location.hasValidLineNumber {
254254
context.diagnosticEngine.diagnose(error)
255255
diagPrinted = true
256256
}

SwiftCompilerSources/Sources/SIL/ASTExtensions.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,16 @@ extension Conformance {
7676

7777
extension DiagnosticEngine {
7878
public func diagnose(_ id: DiagID, _ args: DiagnosticArgument..., at location: Location) {
79-
diagnose(id, args, at: location.sourceLoc)
79+
diagnose(id, args, at: location.getSourceLocation(diagnosticEngine: self))
8080
}
8181

8282
public func diagnose(_ id: DiagID, _ args: [DiagnosticArgument], at location: Location) {
83-
diagnose(id, args, at: location.sourceLoc)
83+
diagnose(id, args, at: location.getSourceLocation(diagnosticEngine: self))
8484
}
8585
}
8686

87-
extension Diagnostic {
87+
extension Diagnostic where SourceLocation == Location {
8888
public init(_ id: DiagID, _ arguments: DiagnosticArgument..., at location: Location) {
89-
self.init(id, arguments, at: location.sourceLoc)
89+
self.init(id, arguments, at: location)
9090
}
9191
}

SwiftCompilerSources/Sources/SIL/Location.swift

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,19 @@
1313
import SILBridging
1414
import AST
1515

16-
public struct Location: Equatable, CustomStringConvertible {
16+
/// Represents a location in source code.
17+
/// `Location` is used in SIL and by the Optimizer.
18+
///
19+
/// When compiling a Swift file, `Location` is basically a `SourceLoc` + information about the
20+
/// containing debug scope. In this case the `SourceLoc` is directly retrieved from the AST nodes.
21+
///
22+
/// However, for debug info which is de-serialized from a swiftmodule file, the location consists of
23+
/// a filename + line and column indices. From such a location, a `SourceLoc` can only be created by
24+
/// loading the file with `DiagnosticEngine.getLocationFromExternalSource`.
25+
///
26+
/// In case of parsing textual SIL (e.g. with `sil-opt`), which does _not_ contain debug line
27+
/// information, the location is also a `SourceLoc` which points to the textual SIL.
28+
public struct Location: ProvidingSourceLocation, Equatable, CustomStringConvertible {
1729
let bridged: BridgedLocation
1830

1931
public var description: String {
@@ -27,6 +39,24 @@ public struct Location: Equatable, CustomStringConvertible {
2739
return nil
2840
}
2941

42+
public var fileNameAndPosition: (path: StringRef, line: Int, column: Int)? {
43+
if bridged.isFilenameAndLocation() {
44+
let loc = bridged.getFilenameAndLocation()
45+
return (StringRef(bridged: loc.path), loc.line, loc.column)
46+
}
47+
return nil
48+
}
49+
50+
public func getSourceLocation(diagnosticEngine: DiagnosticEngine) -> SourceLoc? {
51+
if let sourceLoc = sourceLoc {
52+
return sourceLoc
53+
}
54+
if let (path, line, column) = fileNameAndPosition {
55+
return diagnosticEngine.getLocationFromExternalSource(path: path, line: line, column: column)
56+
}
57+
return nil
58+
}
59+
3060
/// Keeps the debug scope but marks it as auto-generated.
3161
public var autoGenerated: Location {
3262
Location(bridged: bridged.getAutogeneratedLocation())

include/swift/AST/ASTBridging.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,11 @@ void BridgedDiagnosticEngine_diagnose(
592592
BridgedArrayRef arguments, BridgedSourceLoc highlightStart,
593593
uint32_t hightlightLength, BridgedArrayRef fixIts);
594594

595+
SWIFT_NAME("BridgedDiagnosticEngine.getLocationFromExternalSource(self:path:line:column:)")
596+
BridgedSourceLoc BridgedDiagnostic_getLocationFromExternalSource(
597+
BridgedDiagnosticEngine bridgedEngine, BridgedStringRef path,
598+
SwiftInt line, SwiftInt column);
599+
595600
SWIFT_NAME("getter:BridgedDiagnosticEngine.hadAnyError(self:)")
596601
bool BridgedDiagnosticEngine_hadAnyError(BridgedDiagnosticEngine);
597602

include/swift/SIL/SILBridging.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,12 @@ enum class BridgedMemoryBehavior {
392392
struct BridgedLocation {
393393
uint64_t storage[3];
394394

395+
struct FilenameAndLocation {
396+
BridgedStringRef path;
397+
SwiftInt line;
398+
SwiftInt column;
399+
};
400+
395401
BRIDGED_INLINE BridgedLocation(const swift::SILDebugLocation &loc);
396402
BRIDGED_INLINE const swift::SILDebugLocation &getLoc() const;
397403

@@ -402,6 +408,8 @@ struct BridgedLocation {
402408
BRIDGED_INLINE bool isInlined() const;
403409
BRIDGED_INLINE bool isEqualTo(BridgedLocation rhs) const;
404410
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedSourceLoc getSourceLocation() const;
411+
BRIDGED_INLINE bool isFilenameAndLocation() const;
412+
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE FilenameAndLocation getFilenameAndLocation() const;
405413
BRIDGED_INLINE bool hasSameSourceLocation(BridgedLocation rhs) const;
406414
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE OptionalBridgedDeclObj getDecl() const;
407415
static BRIDGED_INLINE BridgedLocation fromNominalTypeDecl(BridgedDeclObj decl);

include/swift/SIL/SILBridgingImpl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,13 @@ BridgedSourceLoc BridgedLocation::getSourceLocation() const {
639639
swift::SourceLoc sourceLoc = silLoc.getSourceLoc();
640640
return BridgedSourceLoc(sourceLoc.getOpaquePointerValue());
641641
}
642+
bool BridgedLocation::isFilenameAndLocation() const {
643+
return getLoc().getLocation().isFilenameAndLocation();
644+
}
645+
BridgedLocation::FilenameAndLocation BridgedLocation::getFilenameAndLocation() const {
646+
auto fnal = getLoc().getLocation().getFilenameAndLocation();
647+
return {BridgedStringRef(fnal->filename), (SwiftInt)fnal->line, (SwiftInt)fnal->column};
648+
}
642649
bool BridgedLocation::hasSameSourceLocation(BridgedLocation rhs) const {
643650
return getLoc().hasSameSourceLocation(rhs.getLoc());
644651
}

lib/AST/Bridging/DiagnosticsBridging.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "swift/AST/DiagnosticEngine.h"
1616
#include "swift/AST/DiagnosticsCommon.h"
1717
#include "swift/Basic/Assertions.h"
18+
#include "swift/Basic/SourceManager.h"
1819

1920
using namespace swift;
2021

@@ -78,6 +79,14 @@ void BridgedDiagnosticEngine_diagnose(
7879
}
7980
}
8081

82+
BridgedSourceLoc BridgedDiagnostic_getLocationFromExternalSource(
83+
BridgedDiagnosticEngine bridgedEngine, BridgedStringRef path,
84+
SwiftInt line, SwiftInt column) {
85+
auto *d = bridgedEngine.unbridged();
86+
auto loc = d->SourceMgr.getLocFromExternalSource(path.unbridged(), line, column);
87+
return BridgedSourceLoc(loc.getOpaquePointerValue());
88+
}
89+
8190
bool BridgedDiagnosticEngine_hadAnyError(
8291
BridgedDiagnosticEngine bridgedEngine) {
8392
return bridgedEngine.unbridged()->hadAnyError();

0 commit comments

Comments
 (0)