Skip to content
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

[SR-13139] Comment after #sourceLocation causes compiler error #55586

Open
swift-ci opened this issue Jul 3, 2020 · 6 comments
Open

[SR-13139] Comment after #sourceLocation causes compiler error #55586

swift-ci opened this issue Jul 3, 2020 · 6 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself parser Area → compiler: The legacy C++ parser

Comments

@swift-ci
Copy link
Collaborator

swift-ci commented Jul 3, 2020

Previous ID SR-13139
Radar rdar://problem/65088776
Original Reporter ken (JIRA User)
Type Bug
Environment

I'm seeing this with swiftc from Xcode 11.3 and Xcode 11.5, which are:

  • Apple Swift version 5.1.3 (swiftlang-1100.0.282.1 clang-1100.0.33.15)

  • Apple Swift version 5.2.4 (swiftlang-1103.0.32.9 clang-1103.0.32.53)

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, Parser
Assignee None
Priority Medium

md5: b734fb3f192f3b2fb8affd320599900c

Issue Description:

Comments after a #sourceLocation cause a compiler error. A simple one-line program which demonstrates the problem:

#sourceLocation(file: "a", line: 1)  // b

I noticed this when trying out SwiftUI, as apparently its preview system likes to inject #sourceLocation all over the place. For example, putting a comment on the same line as a computed property is enough to trigger this:

struct ContentView: View {
    var i: Int {  // a comment here breaks SwiftUI previews
        return 1
    }
    var body: some View {
        Text("hello, world")
    }
}

I tried to look up in the documentation whether comments here were explicitly allowed or disallowed, and I couldn't find a clear answer. The closest I could find is the "LANGUAGE GUIDE" which says "Comments are ignored by the Swift compiler when your code is compiled", with no special notes of places they might not be allowed.

In the LANGUAGE REFERENCE, "Summary of the Grammar", comments are considered a type of "whitespace", and it looks like technically the only place they're allowed is in an extended string literal, immediately after an "escape-sequence", which in turn is defined as a "" character followed by 0-or-more "#" characters (and nothing else!). I don't see anything in the docs about this, and there seem to be many problems with this grammar summary, so I don't put much stock in it.

Immediately following other #-commands I tried, like #if / #else / #endif, comments are allowed.

@typesanitizer
Copy link

This should "just work" IMO. Thanks for reporting! 🙂

For the sake of completeness, here's a full example:

#sourceLocation(file: "a", line: 1) // a
                              // ^-- error: extra tokens at the end of #sourceLocation directive
func f() {}

Reproduces on master.

@typesanitizer
Copy link

@swift-ci create

@benlangmuir
Copy link
Member

I don't think it's completely obvious what the right behaviour is here. If we ever decided to extend #sourceLocation to allow a non-comment token on the same line, the natural interpretation of that would be that the token gets the declared location.

#sourceLocation(file: "a", line: "57") print(#line) // 57 in this hypothetical extension

But that would be inconsistent with skipping a comment in the same location.

Even if we really do want to skip the comment, what behaviour do we want for multi-line comments?

#sourceLocation(file: "a", line: "57") /*
*/
print(#line) // 57 or 58?

Keeping in mind the following already has defined behaviour for a comment on the following line:

#sourceLocation(file: "a", line: "57")
// comment
print(#line) // 58

And what is the impact on doc comments, since if we are skipping the comment it either ceases to exist or it is in a different "file" than the declaration it would otherwise attach to.

#sourceLocation(file: "a", line: "57") /// A doc comment for S?
struct S {}

In Clang (and I assume GCC), the behaviour is to skip any tokens on the same line as a #line directive, and produce a warning rather than an error.

  #line 77 __FILE__ /**/
  printf("%d\n", __LINE__);
77
  #line 77 __FILE__ /*
  */
  printf("%d\n", __LINE__);
78
  #line 77 __FILE__ printf("%d\n", __LINE__);
no output, warning: extra tokens at end of #line directive [-Wextra-tokens]

@typesanitizer
Copy link

There are two important use cases I see here:

  1. Code generators using #sourceLocation() – these can put the comment on the next or previous line, it's not a big deal, I think.

  2. SwiftUI previews – if these are inserting {{#sourceLocation()}}s, ideally that would not break if people add comments.

Based on these two, I think having skipping behavior seems reasonable for comments starting on the same line as #sourceLocation(). If someone wants alternate behavior, they can add their comment on the preceding/succeeding line as the #sourceLocation(). We shouldn't emit warnings, because that might lead to people getting warned about having comments because of {{#sourceLocation()}}s which were inserted by other tools.

What do you think? Are there major downsides to this approach?

@benlangmuir
Copy link
Member

Code generators using #sourceLocation() – these can put the comment on the next or previous line, it's not a big deal, I think.

If it's not a big deal to move the comment, doesn't that argue that we do not need to change anything? Or do you see a reason that SwiftUI Previews (which is a particular code generator) need something that other code generators do not?

Are there major downsides to this approach?

I don't know about "major" 🙂. I'm concerned about us locking ourselves out of future directions. In particular, this already has defined behaviour:

#sourceLocation(file: "a", line: "57")
/*comment*/X // location is 58:12, comment is not skipped

If we ever want to allow a non-comment on the same line as the sourceLocation, we should not skip the comment here to be consistent:

#sourceLocation(file: "a", line: "57") /*comment*/X

And it's still not clear to me what this means

#sourceLocation(file: "a", line: "57") /* start here
keep going here*/
X // is this 57:1 or 58:1 ?

I am not opposed to skipping the comment, but I am not convinced that it is obvious how it behaves, and it feels like enough of a language change that we shouldn't just treat it as a bug.

@swift-ci
Copy link
Collaborator Author

swift-ci commented Jul 9, 2020

Comment by Ken Harris (JIRA)

(Disclaimer: I've written programs in Swift for a few years, but I've never worked on the compiler. I'm writing as a user rather than a maintainer.)

I was surprised to read Ben's remark about potential inconsistency. The documentation (on docs.swift.org) for the #sourceLocation statement says that the #sourceLocation(file:,line🙂 form "changes the value of #line", and the #sourceLocation() form "resets the source code location back to the default line numbering". What difference does it make if there's a comment in between?

Here was my misunderstanding. There's nothing in the docs which says #line is automatically incremented between those two statements, so I assumed that it meant all of the source code between #sourceLocation(file:X,line:Y) and its closing #sourceLocation() was considered to be on line Y of whatever the original source was. If you want to override #line, I thought, you're opting out of the auto-increment behavior – which I assumed is what was meant by "default line numbering". The #line docs say only "The line number on which it appears", not that it's a counter which is incremented even when overridden.

I tried some test programs and discovered that this is not at all the case. The #sourceLocation(file:,line🙂 form changes the #line counter to the specified value, but at the next line (surprise), and it still increments at each newline (surprise). (So it actually sets #line=Y-1?) Furthermore, the second form without the first form is an error, but they're not like paired grouping symbols (surprise). The first form enters a special mode, and the second form exits it. It's a flag, not a stack, but it's an error to try to reset it twice (surprise). You can put 10 of the first form, but then still only 1 of the second form.

I guess what I'm getting at is that the documentation for #sourceLocation is ambiguous (and counterintuitive to me in many ways), and the documentation for comments is inaccurate (see original issue), so if I were writing a program which depended on the intersection of these features working precisely, I'd set aside the docs and just test the bleep out of it, with the actual versions of the compiler that I wished to support.

I appreciate wanting to maintain a "natural interpretation" of more complex cases in the future, but I never would have guessed the actual behavior of today's compiler from a careful reading of the language reference. But maybe I'm just a weirdo!

I definitely expect a big warning, though, if the rule for comments is "you can put them anywhere in your program, except in this one place".

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself parser Area → compiler: The legacy C++ parser
Projects
None yet
Development

No branches or pull requests

3 participants