-
Notifications
You must be signed in to change notification settings - Fork 440
Initial implementation of declarative and type-safe wrapper #139
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
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.
@akkyie Welcome to the project and this change looks awesome! Some nit-picky comments are inline.
Package.swift
Outdated
} | ||
|
||
package.products.append(.library(name: "SwiftSyntaxBuilder", targets: ["SwiftSyntaxBuilder"])) |
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.
Could you put this alone with SwiftSyntax
? We'd like it to be a dynamic library when building from build script and a static library otherwise.
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.
Should we have this functionality as part of the SwiftSyntax
library instead of a separate SwiftSyntaxBuilder
library? It will simplify the clients since they won't have to import two separate modules.
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.
Since Swift doesn't yet permit dead-stripping anything that's declared public in external modules, adding more code to a module forces its binary size increase on everyone, whether the new APIs are used or not.
So in a case like this, where we have something that's adding value as a layer on top of the underlying library, but isn't critical functionality in and of itself, I think it makes sense to keep it a separate module that folks opt in to.
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.
To clarify, if a library is statically linked with an executable, won't SwiftPM do dead-strip on it and remove public APIs that are not used in the executable? \cc @aciidb0mb3r
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.
Unless the situation has changed since I last looked into it, anything declared as open
or public
is lowered by IRGen as @llvm.used
, preventing it from ever being dead-stripped by the linker (SR-1021). That makes sense in a dylib/framework situation where you don't know what the eventual consumer is using, but ends up also applying to statically linked executables.
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.
Ah interesting, I wasn't aware of 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.
I was just not sure I should do so; did in bd31ddb.
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.
Let's leave it as a separate library while it's still in flux, then we can revisit if we want it to become SwiftSyntax API later.
public typealias Let = Variable<VariableLetMutability> | ||
public typealias Var = Variable<VariableVarMutability> | ||
|
||
public struct Variable<Mutability: VariableMutability>: DeclBuildable { |
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.
Using generic types here for mutability seems to me an overkill. Can we put let
or var
keyword as part of the initializer?
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 you mean it should look like Variable(let: "something")
? I think it's more natural if we can just write Let("something")
and Var("something")
, and afaik we need separate types (or a generic type) for this way.
import SwiftSyntax | ||
|
||
@_functionBuilder | ||
public struct DeclListBuilder { |
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 could be an internal type; so is the buildBlock
function.
|
||
import SwiftSyntax | ||
|
||
public struct StringLiteral: ExprBuildable { |
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.
Could you merge all ExprBuildables into a single file? something like ExprBuildables.swift? along with the protocols.
|
||
import SwiftSyntax | ||
|
||
public struct Struct: DeclBuildable { |
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.
Could you merge all DeclBuildables into a single file? something like DeclBuildables.swift? along with the protocols.
import SwiftSyntax | ||
|
||
@_functionBuilder | ||
public struct SyntaxListBuilder { |
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 type and the buildBlock function could both be internal.
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.
let initializer: ExprBuildable? | ||
|
||
public init(_ name: String, of type: String, value: ExprBuildable? = nil) { | ||
self.name = name |
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 think type
being String
here is not great, but it's a good first step. I'd like to see more declarative wrappers around building types alongside declarations and expressions, and those APIs can eventually subsume this one.
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.
Agreed. In fact, I made a Type
type to express a type when it was just my toy, but couldn't figure it out how a straightforward API should look like.
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 is good to land, we'll want to talk about the high-level API of how we want this to look, from a broad perspective, before committing to it. Thanks for making this, @akkyie!
@swift-ci Please test |
Build failed |
CI shows this error:
|
I'd suggest to try removing |
I agree that we should test public APIs. I added I'm wondering if I can fix it by adding Could you tell me if there is any reason not to do so? |
I don't like You can avoid that affecting the final toolchain by testing with |
@benlangmuir Thank you for your explanations! That convinced me. Made some API public and underscored, and testing with the release build has succeeded on my machine. @nkcsgexi I made a revert 9b5a8ad as tests could not be built without making function builders public. SourceFileTests.swift:10:33: error: 'buildBlock' is inaccessible due to 'internal' protection level
let sourceFile = SourceFile {
^
<unknown>:0: note: 'buildBlock' declared here |
@swift-ci Please test |
There is a bit of "churn" with all these commits in the PR, do you mind squashing them to one commit and doing a force push? |
@akyrtzi, are you aware of the “Squash and merge” button? (Click the down arrow beside “Merge”.) It has the same end effect for the commit history on master, but since it doesn’t alter the pull request, the request’s own commit timeline still corresponds to the conversation and reviews if anyone needs to look back at them in the future. It also avoids the issues force pushing can cause across clones, even when the branch in question was never used. |
Does it preserve the original author? My concern is that if I do the "squash and merge" it will squash and make me the author of the commit instead of @akkyie |
I hadn’t thought much about that until now. From what I can tell, it results in “X authored and Y committed”, a subset of GitHub’s co‐author functionality. But I don’t know how transferable that is to other repository inspection tools. |
9b5a8ad
to
3dc22c9
Compare
@swift-ci Please test |
Thank you everyone so much, for all of your reviews and advice! 🙌 |
…ized_exprs Group the closing paren of exprs into the enclosed open/close breaks.
Implemented declarative and type-safe wrappers around SwiftSyntax, using
@_functionBuilder
.(@akyrtzi thank you for encouraging me to upstream it!)
Example Usage
emits:
Currently implemented
let
andvar
with optional initializer (edad18d)struct
with member definition (w/o attributions, access control, generics etc.) (e8f2d02)import
(w/o importing individual declarations) (1687304)Considarations
@_functionBuilder
, calling them "SyntaxBuilders" sounds natural to me. But we already have SyntaxBuilders; can this conflict be a problem? I'm welcome to change my code if we have a good way to call them.Import("")
orLet("===", of: "@@@")
?And I'm a complete newbie here. Let me know if I have to change anything!