Skip to content

[Debug] Add _DebugDescription macro #69626

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 10 commits into from
Dec 8, 2023

Conversation

kastiglione
Copy link
Contributor

@kastiglione kastiglione commented Nov 2, 2023

Implementation of the DebugDescription macro pitched on the forums: https://forums.swift.org/t/pitch-debug-description-macro/67711. In this initial commit, the macro is named _DebugDescription to indicate it's internal use at this time, pending Swift Evolution.

rdar://115180949

@kastiglione
Copy link
Contributor Author

@swift-ci test

@kastiglione kastiglione changed the title [Debug] Add DebugDescription macro [Debug] Add _DebugDescription macro Nov 2, 2023
Copy link

@ahmednour25689 ahmednour25689 left a comment

Choose a reason for hiding this comment

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

LGTM few nits

@kastiglione
Copy link
Contributor Author

@swift-ci test

@kastiglione
Copy link
Contributor Author

@swift-ci test windows

@kastiglione kastiglione requested a review from ahoppen November 8, 2023 19:52
// 1. The body must have a single item
// 2. The single item must be a string literal
// 3. Later on, the interpolation in the string literal will be validated.
guard let codeBlock = descriptionProperty.accessorBlock?.accessors.as(CodeBlockItemListSyntax.self),
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, this will be a bit of an issue. A macro applied to a type shouldn't be able to see the bodies of functions defined within that type, because (for example) we need to be able to determine the members and layout of a type without evaluating the bodies of functions. Unfortunately, @hborla and I discussed this and (IIRC) it's described in a proposal, but the compiler fails to enforce it.

I think what we'll end up needing to do here is collect information about the type itself in a member-attribute macro, then use it to add an attribute to the "description" property which can look at the body. I think that can be made to work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

because (for example) we need to be able to determine the members and layout of a type without evaluating the bodies of functions

does this apply to a peer macro? Since a peer macro doesn't introduce any members, the compiler could determine the members and layout independent of expansion of the peer macro.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think what we'll end up needing to do here is collect information about the type itself in a member-attribute macro, then use it to add an attribute to the "description" property which can look at the body. I think that can be made to work.

I think this makes sense except for one missing piece to the puzzle. The macro attached do description won't have the ability to emit a (global) constant to a custom section. Not currently. Perhaps a change to the compiler could allow a macro attached to description to emit data to a custom section. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

correction: it is now possible to emit static properties to custom sections, thanks to this recent change: #69192

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See e2b3659

Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

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

Just a few minor style comments

Comment on lines 14 to 21
public static func expansion<DeclGroup, DeclSyntax, Context>(
of node: AttributeSyntax,
attachedTo declaration: DeclGroup,
providingAttributesFor member: DeclSyntax,
in context: Context
)
throws -> [AttributeSyntax]
where DeclGroup: DeclGroupSyntax, DeclSyntax: DeclSyntaxProtocol, Context: MacroExpansionContext
Copy link
Member

Choose a reason for hiding this comment

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

Can be simplified to

Suggested change
public static func expansion<DeclGroup, DeclSyntax, Context>(
of node: AttributeSyntax,
attachedTo declaration: DeclGroup,
providingAttributesFor member: DeclSyntax,
in context: Context
)
throws -> [AttributeSyntax]
where DeclGroup: DeclGroupSyntax, DeclSyntax: DeclSyntaxProtocol, Context: MacroExpansionContext
public static func expansion(
of node: AttributeSyntax,
attachedTo declaration: some DeclGroupSyntax,
providingAttributesFor member: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
)
throws -> [AttributeSyntax]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will add a comment. The reason it's written this way is because of an lldb bug which doesn't yet support some style generics. The way it's written allows me to po swift-syntax values.

Copy link
Member

Choose a reason for hiding this comment

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

In that case, I would prefer to not use DeclSyntax as a generic type because it shadows the DeclSyntax type from SwiftSyntax.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated this PR to use the some syntax, and kept my local working copy with the generic syntax.

let arguments = argumentList.map(\.expression)
guard let typeIdentifier = String(expr: arguments[0]),
let computedProperties = Array<String>(expr: arguments[1]) else {
assertionFailure("_DebugDescriptionProperty called incorrectly")
Copy link
Member

Choose a reason for hiding this comment

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

In general, I would prefer to emit errors here instead of hitting assertionFailures. As I mentioned above, somebody might redeclare _DebugDescriptionPropertyMacro.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure what scenario would result in broken internals being used for real. Also, that these hypothetical errors would not be in the code written by a user.

Copy link
Member

Choose a reason for hiding this comment

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

I’m just thinking that in general it’s nicer to throw errors than to crash because they tend to be more actionable for the user. Either because they re-declared the macro or because there was some hole that we missed. But up to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I took your suggestion. No more asserts, only diagnostics.

@kastiglione kastiglione requested a review from ahoppen December 6, 2023 18:09
@kastiglione
Copy link
Contributor Author

@swift-ci test

@kastiglione kastiglione requested a review from a team as a code owner December 6, 2023 20:45
@kastiglione
Copy link
Contributor Author

@swift-ci test

@kastiglione
Copy link
Contributor Author

Test failures are due to use of String.contains and CI sets the deployment target to 10.13 where contains isn't available. I will push an update to fix it.

@kastiglione
Copy link
Contributor Author

@swift-ci test

Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

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

This is looking great!


extension MemberAccessExprSyntax {
/// Parse a member access consisting only of property references. Any other syntax throws an error.
fileprivate func propertyChain() throws -> [DeclReferenceExprSyntax] {
Copy link
Member

Choose a reason for hiding this comment

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

It's somewhat stylistic, but you could consider putting this on ExprSyntaxProtocol and dealing with both the DeclReferenceExprSyntax and MemberAccessExprSyntax cases there. Then the client of this function wouldn't have to separate them out.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like it, thanks! Done in e54634c.

@kastiglione
Copy link
Contributor Author

@swift-ci test

1 similar comment
@kastiglione
Copy link
Contributor Author

@swift-ci test

@kastiglione kastiglione merged commit ad585b3 into main Dec 8, 2023
@kastiglione kastiglione deleted the dl/Debug-Add-DebugDescription-macro branch December 8, 2023 23:19
@finagolfin
Copy link
Member

This pull broke building the stdlib with a compiler with SWIFT_BUILD_SWIFT_SYNTAX disabled, as macros are unavailable then:

stdlib/public/core/DebuggerSupport.swift:36:14: error: macros are not supported in this compiler
public macro _DebugDescription() =
             ^
/home/ubuntu/jenkins/workspace/oss-swift-RA-linux-ubuntu-16.04-android/swift/stdlib/public/core/DebuggerSupport.swift:42:14: error: macros are not supported in this compiler
public macro _DebugDescriptionProperty(_ debugIdentifier: String, _ computedProperties: [String]) =
             ^

@kastiglione
Copy link
Contributor Author

Thanks. It seems I should restore the #if $Macros that existed in b2c4c3c.

@kastiglione
Copy link
Contributor Author

@finagolfin #70351

kastiglione added a commit that referenced this pull request Dec 12, 2023
Make `_DebugDescription` macro conditional upon macro availability in the build.

The `Macros` feature is based on the value of `SWIFT_BUILD_SWIFT_SYNTAX`.

See #69626 (comment)
Catfish-Man pushed a commit to Catfish-Man/swift that referenced this pull request Jan 19, 2024
Implementation of the DebugDescription macro pitched on the forums: 
https://forums.swift.org/t/pitch-debug-description-macro/67711. In this initial commit, 
the macro is named `_DebugDescription` to indicate it's internal use at this time, 
pending Swift Evolution.

rdar://115180949
Catfish-Man pushed a commit to Catfish-Man/swift that referenced this pull request Jan 19, 2024
)

Make `_DebugDescription` macro conditional upon macro availability in the build.

The `Macros` feature is based on the value of `SWIFT_BUILD_SWIFT_SYNTAX`.

See swiftlang#69626 (comment)
struct MyStruct1: CustomStringConvertible {
var description: String { "thirty" }
}
// CHECK: static let _lldb_summary = (
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be a good idea for the macro to explicitly declare the type of the _lldb_summary property:

Suggested change
// CHECK: static let _lldb_summary = (
// CHECK: static let _lldb_summary: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) =

While Swift allows you to infer a property's type from its initial value expression, that can impact compiler performance by forcing the compiler to type-check the initial value expression when it's compiling other files. We're generating these declarations anyway, so we might as well generate an explicit type when we do.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks. This was mentioned on the SE discussion just the other day. I assumed, incorrectly, that the way it was generated would be fine. I did a couple benchmarks with reasonable sized tuples and there was a moderate differences in wall time. Do you have any opinion on whether to introduce a local type alias for the tuple (which was suggested in the SE discussion)? I think benefit would be a slightly more readable macro expansion output, but a tuple of bytes isn't particularly readable in the first place.

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.

9 participants