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

cmd/compile: missing "declared but not used" error for unused variable in type switch with no cases #23116

Closed
dc0d opened this issue Dec 13, 2017 · 20 comments

Comments

Projects
None yet
8 participants
@dc0d
Copy link

commented Dec 13, 2017

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

1.9.2

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

Ubuntu 16.04 ("amd64" architecture)

What did you do?

This code compiles and generates no errors. Why?

func f(v interface{}) {
	switch x := v.(type) {
	}
}

https://play.golang.org/p/BnQbG6RKRB

What did you expect to see?

A compilation error.

What did you see instead?

It worked.

@ALTree

This comment has been minimized.

Copy link
Member

commented Dec 13, 2017

Similar to #873 and #2162. Apparently the patch that fixed those missed the case with no case. If you add e.g. a case int: to your code, an unused error is correctly emitted.

func f(v interface{}) {
	switch x := v.(type) {
	case int:
	}
}
x declared and not used

@ALTree ALTree changed the title Unused Variable Generates No Error cmd/compile: missing "declared but not used" error for unused variable in type switch with no cases Dec 13, 2017

@ALTree ALTree added this to the Go1.11 milestone Dec 13, 2017

@ALTree

This comment has been minimized.

Copy link
Member

commented Dec 13, 2017

On the other hand, the spec says:

The TypeSwitchGuard may include a short variable declaration. When that form is used, the variable is declared at the end of the TypeSwitchCase in the implicit block of each clause.

Note: "the variable is declared [..] in the implicit block of each clause".

One could argue that no case clause means that no variable is declared... and thus there are no unused variables(?)

@dc0d

This comment has been minimized.

Copy link
Author

commented Dec 13, 2017

Thanks for the response. And this case is obviously a rare one and does not happen regularly. Yet (IMHO) it is not consistent with "Go common sense", despite the fact it is explicitly stated and explained inside the language spec.

Seems this could remain as it is without doing any harm (since as far as I understand the "x := v.(type)" part has no side effects). Or it can have a low priority.

@mvdan

This comment has been minimized.

Copy link
Member

commented Dec 15, 2017

I agree that, even if the compiler is technically correct, the lack of an error is unexpected to any Go programmer not familiar with all the spec details. I think we should add the error.

I wonder if a change in the spec would be necessary for cmd/compile and go/types to add the error, though. I don't know to what extent are the reference tools able to add stuff of their own that isn't in the spec explicitly.

/cc @griesemer @mdempsky

@griesemer griesemer self-assigned this Dec 15, 2017

@griesemer

This comment has been minimized.

Copy link
Contributor

commented Dec 15, 2017

go/types does report an error, but @ALTree 's comment regarding the spec phrasing is interesting. The type switch syntax is somewhat unfortunate and requires some "cheating" in the compiler to get things right with those implicitly declared variables.

In many ways it would be much clearer (though more verbose) if one would write type switches like

type switch v {
case x int: ...
case y float32: ...
}

where the x, v are variables of the respective type in each case (and one couldn't declare a variable if there are multiple types per case). But I digress. Should address existing situation.

@dc0d

This comment has been minimized.

Copy link
Author

commented Dec 15, 2017

I can not provide any logical/qualified reasoning on what this should be, but an opinion, which is, this case is against Go common-sense and switch statement is my favorite part of Go syntax and I hope it does not loose any features, in the process.

@griesemer

This comment has been minimized.

Copy link
Contributor

commented Jan 3, 2018

As an extra reference point, gccgo does report an error in this case, per @ianlancetaylor.

@griesemer

This comment has been minimized.

Copy link
Contributor

commented Jan 3, 2018

I am leaning towards "working as intended". The spec is pretty clear and even has an example of a type switch that's rewritten into a sequence of type assertions. Given that rewrite, it's obvious that there wouldn't be an error for an empty type switch. I still propose to slightly rephrase the spec wording from:

The TypeSwitchGuard may include a short variable declaration. When that form is used, the variable is declared at the end of the TypeSwitchCase in the implicit block of each clause.

to

The TypeSwitchGuard may take the form of a variable declaration. When that form is used, the variable is declared at the end of the TypeSwitchCase in the implicit block of each clause.

to emphasize that the guard only "looks like" a declaration, but doesn't in fact declare a variable in the guard (only in the cases).

Consequently:

  • the compiler doesn't need a fix
  • go/types needs a (trivial) fix
  • gccgo may need a (trivial?) fix

The alternative would require a fix in the compiler, and also a clarification in the spec. It may be more "obvious" to a Go programmer, but doesn't quite hold up to the spec as is.

@rsc, @iant, @r, any thoughts?

@mvdan

This comment has been minimized.

Copy link
Member

commented Jan 3, 2018

If it is decided that the compiler doesn't need a fix, perhaps we could add a warning to vet (or another analysis tool) instead.

@mdempsky

This comment has been minimized.

Copy link
Member

commented Jan 3, 2018

I'm leaning towards working as intended too.

The spec uses "declare" to mean binding an identifier to an object. For example, dot-import declarations say all of the imported package's exported identifiers are "declared" in the importing file's scope.

For type switches, the spec states that the variable is "declared" at the end of each case. If there are no cases, then the variable was never "declared," so the compiler is erroneous to complain that it was declared and not used. (Basically @ALTree's argument above.)

I agree with @griesemer's suggestion to clarify that the TypeSwitchGuard syntax only looks like a short variable declaration.

@mdempsky

This comment has been minimized.

Copy link
Member

commented Jan 3, 2018

The spec is pretty clear and even has an example of a type switch that's rewritten into a sequence of type assertions.

Ironically within the context of this discussion, the rewritten example has three declared-but-unused i variables. :)

@griesemer

This comment has been minimized.

Copy link
Contributor

commented Jan 3, 2018

@mdempsky Should probably fix that somehow when addressing this issue.

@jimmyfrasche

This comment has been minimized.

Copy link
Member

commented Jan 5, 2018

As a data point, I actually have a minor use case for the current behavior in my next project: generating cases programatically (for example, an interface with an unexported tag method used as a sum type).

For that to work, I need to type check the package containing the possibly empty switch to figure out how to populate the cases. If this were flagged as an error in the spec, then it wouldn't be possible to generate the cases on something like

switch v := v.(type) {
}

and it would first have to be rewritten as

switch v.(type) {
}

before invoking my (currently-hypothetical) program from one's editor followed by adding back the v := .

Not a huge deal, certainly, but it would be annoying and a common "gotcha" of using such a tool. I don't expect this to swing the decision one way or the other, but the current spec does have a pragmatic benefit.

A vet warning would be good, too, though.

@griesemer

This comment has been minimized.

Copy link
Contributor

commented Jan 5, 2018

Thanks @jimmyfrasche for this example; this further supports the current behavior.

@jimmyfrasche

This comment has been minimized.

Copy link
Member

commented Jan 23, 2018

Less hypothetical now https://github.com/jimmyfrasche/closed/tree/master/cmds/fillswitch

I've only used it half a dozen times but have already managed to trip myself on this issue and needed to rewrite the switch to placate go/types

@griesemer

This comment has been minimized.

Copy link
Contributor

commented Jan 23, 2018

@jimmyfrasche In case of the empty switch in your code, couldn't you add

default:
   _ = v

as a (temporary) work-around?

@jimmyfrasche

This comment has been minimized.

Copy link
Member

commented Jan 23, 2018

yes that would be equivalent but since this is for use in an editor as you're writing code removing then re-adding the v := is fewer keystrokes and in a less objective sense not as disruptive to the flow

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2018

Decision: We should report an unused variable error for a type switch that uses x := v.(type) even if there are no cases.

@griesemer

This comment has been minimized.

Copy link
Contributor

commented Mar 12, 2018

Some more information about this decision: Despite some arguments in favor of the status quo, the majority of the proposal review team felt pretty strongly that this was "just a bug". Notably, gccgo and go/types already support report an error.

With respect to the spec wording: While the spec does say that the variable is declared in each case branch, it is sufficient for the variable to be used at least once - it doesn't need to be used in each case branch. At least from the programmer's point of view this makes it look like there's only one variable that needs to be used, which is the one declared in the short variable declaration in the switch clause. Thus, this would be a more technical argument if favor of the decision.

@mdempsky mdempsky self-assigned this Mar 13, 2018

@gopherbot

This comment has been minimized.

Copy link

commented Mar 13, 2018

Change https://golang.org/cl/100459 mentions this issue: cmd/compile: reject type switch with guarded declaration and no cases

@gopherbot gopherbot closed this in e601c07 Mar 13, 2018

@golang golang locked and limited conversation to collaborators Mar 13, 2019

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
You can’t perform that action at this time.