-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[SwiftCompiler] Add DiagnosticEngine bridging #41500
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
Conversation
@swift-ci Please smoke test |
80833c5
to
05d5e0c
Compare
@swift-ci Please smoke test |
public struct CharSourceRange { | ||
public var start: SourceLoc | ||
public var byteLength: Int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these not let
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, if a struct
provides a member-wise initializer, there is no reason to make any of its properties let
. Even if we make these let
, others can
extension CharSourceRange {
mutating func setByteLength(val: Int) {
self = .init(start: start, byteLength: value)
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given this is Swift vs. C++, exposing opportunities for mutating public storage directly on an instance when it is not intended as publicly mutable sounds like an antipattern, in terms of how error-prone this can be in imperative code and when maintaining state integrity. In the extension it would at least be an "atomic" mutation. This is a personal opinion though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @AnthonyLatsis here. Making properties let
enforces a more stateless design of the code, which is good to keep the code simple. If a property is var
, it adds unnecessary complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I changed them to let
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
@_exported import ASTBridging |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API of DiagnosticEngine
should be a pure swift API. So there should be no need to export bridging stuff
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed @_exporeted
init(int val: Int) { | ||
self.init(kind: .int, value: .init(intValue: val)) | ||
} | ||
init(uInt val: UInt) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need UInt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not entirely sure.
Even if the C++ diagnostic ID declared to receive an uint
argument, as far as I can read from the DiagnosticEngine C++ code, it accepts DiagnosticArgument(int)
value too. So maybe it's not needed. Since we can add it later, I just removed it for now.
var bridgedArgs: [BridgedDiagnosticArgument] = [] | ||
var bridgedFixIts: [BridgedDiagnosticFixIt] = [] | ||
|
||
var closure: () -> Void = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, this trick with the closure definitely needs a comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some comment
} | ||
|
||
public protocol DiagnosticArgument { | ||
func withBridgedDiagnosticArgument(_ fn: (BridgedDiagnosticArgument) -> Void) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it's unavoidable to make withBridgedDiagnosticArgument
public.
I suggest you make it an underscored name (_withBridgedDiagnosticArgument
) to indicate it's not part of the public API.
public struct CharSourceRange { | ||
public var start: SourceLoc | ||
public var byteLength: Int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @AnthonyLatsis here. Making properties let
enforces a more stateless design of the code, which is good to keep the code simple. If a property is var
, it adds unnecessary complexity.
self.pointer = UnsafeRawPointer(pointer).assumingMemoryBound(to: UInt8.self) | ||
} | ||
|
||
public init?(bridged: BridgedSourceLoc) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this shouldn't be a public API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In future, in other modules (i.e. Parse
), we want to receiveBasic
's Bridged*
from C++
Like:
//-- Parse/Regex.swift
import Basic
// To be called from C++.
func parseRegexLiteral(_ bridgedLoc: BridgedSourceLoc, _ length: Int, ....) {
let sourceLoc = SourceLoc(bridged: bridgedLoc)
...
}
So init(bridged:)
needs to be public
.
self.init(pointer: pointer) | ||
} | ||
|
||
public var bridged: BridgedSourceLoc { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is in Basic
.
var bridged
is used in DiagnosticEngine.diagnose()
in AST
to pass the bridged values to DiagnosticEngine_diagnose()
C function.
I can make this underscored, but it'll be inconsistent with StringRef or ArrayRef's withBridgedXXX
self.byteLength = byteLength | ||
} | ||
|
||
public init?(bridged: BridgedCharSourceRange) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shouldn't be a public API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same reason as SourceLoc
self.init(start: start, byteLength: bridged.byteLength) | ||
} | ||
|
||
public var bridged: BridgedCharSourceRange { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as SourceLoc
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
public struct SourceLoc { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I always found it confusing that we have multiple data types to describe a source location (in C++): SourceLoc
and SILLocation
.
I think we can do better on the swift side. We should unify AST.SourceLoc
and SIL.Location
and have only a single data type for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SourceLoc
and SILLocation
are very different type. SourceLoc
is basically just a pointer, whereas SILLocation
is a much more complex object. I'm not sure how we could unify them...
|
||
public func diagnose(_ position: SourceLoc?, | ||
_ id: DiagID, | ||
_ args: [DiagnosticArgument], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you make this a var-arg parameter instead of an array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a var-arg variant, but I still want Array
one too. It's convenient when, for example, using Swift's enum as an diagnostic type.
For example:
struct ParserDiagnostic {
enum Message {
case expectedNumDigits(String, Int)
case miscError(String)
case otherError
}
let location: Location
let message: Message
}
func reportDiag(diag: ParseError) {
let location = getSourceLoc(location)
let diagID: DiagID
let arguments: [DiagnosticArguement]
switch diag.message {
case expectedNumDigits(let s, let i):
diagID = .parse_regex_expected_num_digits
arguments = [s, I]
case ...
...
}
engine.diagnose(loc, diagID, arguments)
}
Otherwise, we need to call DiagnositcEngine.diagnose()
from each case
* 'SourceLoc' and 'CharSourceRange' bridging in Basic * New 'AST' bridging. 'DiagID', 'DiagnosticArgument', 'DiagnosticFixIt', and 'DiagnosticEngine'
05d5e0c
to
4d9b65d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, thanks!
@swift-ci Please smoke test |
SourceLoc
andCharSourceRange
bridging inBasic
AST
bridging.DiagID
,DiagnosticArgument
,DiagnosticFixIt
, andDiagnosticEngine
Preparation for Regex literal parsing diagnostics. #41492