SE-0196 — Compiler Diagnostic Directives

What is your evaluation of the proposal?

I’m generally in favor of this proposal, but with the #warning("text") syntax. This will allow easier extensions to this type of directive in the future, like the #warning("warning", at:) example mentioned above.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes, it’s a slight a problem, but not insignificant.

Does this proposal fit well with the feel and direction of Swift?

Yes, compiler directives are a natural part of the Swift language.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

None.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Read the proposal and thread.

  • Does this proposal fit well with the feel and direction of Swift?

No. The proposed syntax is inconsistent with other compiler directives.

#warning "This is a warning message"

The #sourceLocation directive for example, looks like a function call:
#sourceLocation(file: "abc.swift", line: 42)

When sourceLocation went through the evolution process the original proposal was to use a syntax that is compatible with the C preprocessor (`#line “abc.swift” 42). At the time swift-evolution decided that we should use a more swifty syntax that is decidedly different from the C preprocessor.

Applying the same rationale, I would expect the syntax for #warning to be

#warning(message: "This is a warning message.")

– adrian

+1. I think that this would be really useful.

I have no problem with that syntax, though message: seems excessive.

1 Like

I agree that message: doesn’t add any value and is cumbersome to type. Removing it doesn’t make the syntax any less swifty.

2 Likes

Alexander Momchilov brought up the idea of using TODO and warning as functions in the standard library with special compiler magic that will warn on their uses.

While these could be useful, I think #warning and #errorhave uses beyond just marking unfinished code that would be unwieldy or impossible with just an expression-oriented approach.

My proposal for TODO was in addition to #warning and #error. Warnings, errors, todos and fixmes are all widely adopted in the community (the Swift community, and the broader programming community), and I think that we should have first class support for them. This could allow programmers to customize how to handle todos and fixmes (e.g. ignore todos, make fixmes warnings. Or warn on todos, error on fixmes, etc.), in the same way we can get our compiler to ignore warnings, emit warnings, or promote warnings to errors.

From the proposal:

Erik Little refined that to instead use special directives #warning and #error in expression position, like:

let somethingSuspect = #warning("This is really the wrong function to call, but I'm being lazy", suspectFunction())

However, I think there’s not much of a benefit to this syntax vs. just adding a #warning above the line:

#warning "This is really the wrong function to call, but I'm being lazy"
let somethingSuspect = suspectFunction()

The first bit of code guarantees that the warning/error/todo/fix me is coupled to the default value. You can’t remove the warning and forget to change the value and you can’t can’t the value and forget to change the warning. @anandabits Put it well:

Don’t you think this could be better handled if warning/todo/fixme were expressions with parameters they passthrough:

return #warning(0, "Incomplete implementation: return number of row") // or `#todo`, `#fixme`

That way the default value and the message associated with it are more tightly coupled

Has there been any discussing into how to get a #warning to make the message appear at the call site, rather than immediately? In a similar fashion to #file, #line:

/*1*/ func printLineNumber(_ lineNumber: Int = #line) {
/*2*/  	print(lineNumber)
/*3*/ }
/*4*/ 
/*5*/ printLineNumber() // => 5, not 2

similarly:

/*1*/ func someCustomWarningHandler() {
/*2*/      #callSiteWarning("Uh oh!") // (crappy) strawman syntax
/*3*/ }
/*4*/ 
/*5*/ someCustomWarningHandler() // emit warning here, not on line 2

Runtime warnings imght be off topic, but I think we should pursue this (in this proposal or otherwise). Opening up the runtime warning system could be really useful

There is no real need for additional directives here, we can just overload the function like directive #warning(). Furthermore todo and fixme are just warnings as well.

// Bikeshedding
func #warning<T>(
  _ message: String,
  at mode: DiagnoseMode = .compiletime,
  value: T
) -> T {
  // emit warning
  return value
}

func #warning(_ message: String, at mode: DiagnoseMode = .compiletime) {
  #warning(message, at: mode, value: ())
}

#warning("message at compile time")
#warning("message at runtime", at: .runtime)
#warning("I'm lazy at compile time", value: 42)
#warning("I'm lazy at rumtime", at: .runtime, defaultValue: 42)

I don’t think this should be assumed. I see great value in treating fixmes as errors, and todos as warnings. Others might disagree and want fixmes as warnings and todos as silent. I think we should accommodate that.

#todo and #fixme carry more semantic information than a #warning, the same way Date carries more semantic information than a String that encodes a date. Even if a #warning could be used to encode it, #todo and #fixme are a stronger type, per se.

1 Like

Any specific example in mind? It could only be me, but I don’t see the difference between #fixme #todo and #warning except for the name.

The recommended way to do call-site / use-site warnings today is deprecation: @available(*, deprecated, message="Uh oh!"). It is a bit verbose, I admit, and some people have mentioned wanting to have a way to produce warnings that don’t say “deprecated”, but those are both improvements that should be independent of Harlan’s proposal.

1 Like

@AlexanderM that could be accomplished if #warning and #error accepted file and line arguments in addition to the message argument.

// produces a warning with the specified `message` at the site specified by `file` and `line`
// and returns a value if specified, calling fatalError if no value was provided
func TODO<T>(_ value: T? = nil, _ message: StaticString? = nil, file: StaticString = #file, line: UInt = #line) -> T {
    #warning(message ?? "TODO", file: file, line: line)
    if let value = value {
        return value
    } else {
        fatalError()
    }
}

This is the kind of flexibility I would like to see. It allows us to define our own warning-producing functions. I’m not sure how feasible implementation of this exact mechanism would be, however.

1 Like

I’m very much in favour of adding #warning and #error directives, preferably with message in parenthesis. I’m intrigued by the idea of runtime warnings, not sure if errors should also be available in such mode. One concern I have though is the syntax, currently runtime directives use @directive, should we implement runtime warnings shouldn’t they use @warning too?

I think I already outlined the purpose: Having #fixme and #todo as separate can allow the compiler’s response to them to be customization using flags, similar to SWIFT_TREAT_WARNINGS_AS_ERRORS.

Furthermore, there are #warnings that aren’t #TODO or #FIXME related. We should make a distinction between them, even if a programmer ultimately sets the compiler to treat all 3 as just warnings.

1 Like

Wouldn’t that require some form of StaticInt? The line would have to guaranteed to be known at compile time.

@jrose I had forgotten that a message argument was available on @available. But it is odd for a function that isn’t really deprecated to have to specify itself as deprecated. This also does not allow the warning message to be customized by the call site which also seems very useful.

I described the behavior I would like to see in a code in a previous post. I suspect that the exact mechanism I described may not be workable but I hope something similar might be possible. I think it is very relevant to the discussion of this proposal even if the result is that it ends up being an alternative or a future direction.

1 Like

I see your point there, but I still have the impression that this still would require tooling support for a different set of messages. Don’t get me wrong, I’d love to see a set of fix-me’s, todo’s and warnings in Xcode sorted and differentiated whether those were at compile time or at runtime. However this makes this proposal much more complex and the probability of it’s success is lowered. Furthermore it requires a full implementation as well.

I think we should keep it simple but keep the door open for more. ;) On the other hand if there is someone who can implement all that, then forget my concerns and go for it.

Yes, it would probably need something like that. That’s why I hedged on the exact mechanism. I expect it would need to look a little bit different. My goal is to demonstrate the kind of function I would like to be able to write.

Terms of Service

Privacy Policy

Cookie Policy