Skip to content

[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

Merged
merged 1 commit into from
Feb 25, 2022

Conversation

rintaro
Copy link
Member

@rintaro rintaro commented Feb 21, 2022

  • SourceLoc and CharSourceRange bridging in Basic
  • New AST bridging. DiagID, DiagnosticArgument, DiagnosticFixIt, and DiagnosticEngine

Preparation for Regex literal parsing diagnostics. #41492

@rintaro
Copy link
Member Author

rintaro commented Feb 21, 2022

@swift-ci Please smoke test

@rintaro
Copy link
Member Author

rintaro commented Feb 21, 2022

@swift-ci Please smoke test

Comment on lines 48 to 50
public struct CharSourceRange {
public var start: SourceLoc
public var byteLength: Int
Copy link
Collaborator

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?

Copy link
Member Author

@rintaro rintaro Feb 21, 2022

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)
  }
}

Copy link
Collaborator

@AnthonyLatsis AnthonyLatsis Feb 21, 2022

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.

Copy link
Contributor

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.

Copy link
Member Author

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

@rintaro rintaro requested a review from eeckstein February 22, 2022 02:54
//
//===----------------------------------------------------------------------===//

@_exported import ASTBridging
Copy link
Contributor

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

Copy link
Member Author

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) {
Copy link
Contributor

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?

Copy link
Member Author

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 = {
Copy link
Contributor

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.

Copy link
Member Author

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)
Copy link
Contributor

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.

Comment on lines 48 to 50
public struct CharSourceRange {
public var start: SourceLoc
public var byteLength: Int
Copy link
Contributor

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) {
Copy link
Contributor

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

Copy link
Member Author

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Copy link
Member Author

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) {
Copy link
Contributor

@eeckstein eeckstein Feb 23, 2022

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

Copy link
Member Author

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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Copy link
Member Author

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 {
Copy link
Contributor

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.

Copy link
Member Author

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],
Copy link
Contributor

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?

Copy link
Member Author

@rintaro rintaro Feb 24, 2022

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'
Copy link
Contributor

@eeckstein eeckstein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, thanks!

@rintaro
Copy link
Member Author

rintaro commented Feb 25, 2022

@swift-ci Please smoke test

@rintaro rintaro merged commit c9e3699 into swiftlang:main Feb 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants