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

go/types, types2: 'any' in go/types.Universe should be of type Alias #66921

Open
findleyr opened this issue Apr 19, 2024 · 22 comments
Open

go/types, types2: 'any' in go/types.Universe should be of type Alias #66921

findleyr opened this issue Apr 19, 2024 · 22 comments
Assignees
Labels
NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. release-blocker
Milestone

Comments

@findleyr
Copy link
Contributor

findleyr commented Apr 19, 2024

Just noticed that the predeclared types any, byte, and rune in go/types.Universe are not actually of type go/types.Alias. This matters: I noticed it while working on type writing for the x/tools inliner.

I'll put together a fix. I would not be surprised if additional tests fail as a result of this fix, since this would be the first time we have an Alias type in the universe scope.

(EDIT: per discussion, let's just fix this for any. Basic types are special anyway).

CC @griesemer @adonovan

@findleyr findleyr added this to the Go1.23 milestone Apr 19, 2024
@findleyr findleyr self-assigned this Apr 19, 2024
@findleyr
Copy link
Contributor Author

Note: this poses a rather unfortunate problem for switching GODEBUG values inside tests.

@gopherbot
Copy link

Change https://go.dev/cl/580355 mentions this issue: go/types, types: make any, byte, and rune actual Alias types.

@findleyr
Copy link
Contributor Author

@griesemer @adonovan after staring at the implications of this for a minute, I think the only way to do it properly is to insist that the only way to change GODEBUG=gotypesalias is at startup. There's no way that switching in tests can work when universeAny, universeByte, and universeRune are written to types.Universe and types.Typ.

@timothy-king has created a GODEBUG=gotypesalias=1 builder, and we should use that technique for creating a GODEBUG=gotypesalias=0 builder.

@adonovan
Copy link
Member

adonovan commented Apr 19, 2024

Just noticed that the predeclared types any, byte, and rune in go/types.Universe are not actually of type go/types.Alias. This matters: I noticed it while working on type writing for the x/tools inliner.

Why does it matter exactly? The defined types string and int are not actually represented like other defined types astypes.Named, because they don't have a declaration or TypeName. It seems to me that byte falls into a similar category.

I'll put together a fix. I would not be surprised if additional tests fail as a result of this fix, since this would be the first time we have an Alias type in the universe scope.

What would be the fix? What would the TypeName be for a universal Alias?

@griesemer @adonovan after staring at the implications of this for a minute, I think the only way to do it properly is to insist that the only way to change GODEBUG=gotypesalias is at startup. There's no way that switching in tests can work when universeAny, universeByte, and universeRune are written to types.Universe and types.Typ.

Arguably this is evidence that it's not the right fix.

@timothy-king has created a GODEBUG=gotypesalias=1 builder, and we should use that technique for creating a GODEBUG=gotypesalias=0 builder.

We should flip that builder to "0", since we don't need a "1" builder: it's the default at tip.

@findleyr
Copy link
Contributor Author

Why does it matter exactly?

The spec says that they are aliases, and there are various places where we need to check for distinguished pointers to print the right name. (search for types2?.Universe.Lookup("any") to find these special cases). Several of those places also have apologies that the "hack" wouldn't be necessary with proper alias support. Now we have that support.

It is inconsistent for them not to be Aliases. Why shouldn't we fix this properly?

What would be the fix? What would the TypeName for a universal Alias?

A new TypeName? I don't understand the question.

Arguably this is evidence that it's not the right fix.

I don't see how that follows. We have a global that uses the new API. Any time a global is affected by a GODEBUG value, it seems that such a fix would be required. I don't understand the philosophical point.

@adonovan
Copy link
Member

A new TypeName? I don't understand the question.

Sorry, I was under the misapprehension that all TypeNames had valid positions, but of course that's not true for all the built-in types. Ignore me.

@findleyr
Copy link
Contributor Author

Thanks. Will wait for @griesemer to opine before spending too much more time here. See the associated CL for the proposed fix. It wasn't hard to get go/types/types2 tests passing, and I suspect just a few more changes are required in the compiler / importers.

Do we want to do this? It's now or never, I suppose.

Aside:

The defined types string and int are not actually represented like other defined types astypes.Named, because they don't have a declaration or TypeName

FWIW, I think that's no problem. Basic types are defined types. But byte and rune are aliases, and suffer from the same loss of information as other aliases.

Put differently, don't we eventually want the definition of TypeName.IsAlias to be _, ok := obj.typ.(*Alias); return ok?

@findleyr findleyr changed the title go/types: aliases in go/types.Universe should be of type go/types.Alias go/types, types2: aliases in go/types.Universe should be of type go/types.Alias Apr 19, 2024
@adonovan
Copy link
Member

Do we want to do this? It's now or never, I suppose.
Put differently, don't we eventually want the definition of TypeName.IsAlias to be _, ok := obj.typ.(*Alias); return ok?

Yeah, it's definitely a simpler representation. The question is how much churn will it generate. Only experience will tell.

@findleyr findleyr added the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Apr 19, 2024
@findleyr findleyr changed the title go/types, types2: aliases in go/types.Universe should be of type go/types.Alias go/types, types2: aliases in go/types.Universe should be of type Alias Apr 19, 2024
@findleyr
Copy link
Contributor Author

Hmm, well byte and rune are distinguished Basic types, i.e. their type carries their name. Therefore it is less important to make this change for them than for any.

@griesemer
Copy link
Contributor

I'm worried about introducing more problems with this than solving.
I think byte and rune can be left alone.
The only other type where it matters is any and I think there it only matters for type equality when any is not at the top level (e.g., func(any) and func(interface{}) must be considered identical). Can we just have a special check for this in the identity check and then remove it once we switch solidly to Alias nodes?

@griesemer
Copy link
Contributor

To be clearer about this: we could define any as its own type (definition) but then treat it as identical with interface{}. Maybe the way to do this is to (dynamically) introduce an Alias node (or skip it) depending on the current setting if AliasEnabled.

@findleyr
Copy link
Contributor Author

findleyr commented Apr 19, 2024

@griesemer we already have special handling for any throughout the toolchain. If we're going to change the representation of any, it should be to Alias. Storing any in a Named seems quite problematic.

Leaving byte and rune alone sounds fine, since they have existing for a long time and in any case have access to their Name (and Basic types are special anyway). But any is a real outlier. It would be nice not to have to special-case any throughout tools that needs to format or rewrite source code.

@griesemer
Copy link
Contributor

@findleyr Sounds good. So maybe any is simply always represented with an Alias node?

@findleyr
Copy link
Contributor Author

So maybe any is simply always represented with an Alias node?

@griesemer that would be problematic for tools that still use gotypesalias=0. I think we need to change the representation based on the GODEBUG environment variable at startup. Perhaps we can just get away with inconsistent behavior if GODEBUG is subsequently modified in tests...

This may also be a problem for bootstrapping.

@adonovan
Copy link
Member

adonovan commented Apr 19, 2024

"Once at startup" seems like the only logical way to choose the representation of a global variable, but many of our tests need to call Setenv to 1 or 0 later on, and this may lead to inconsistency.

Perhaps we need to establish and assume that all our tests are run in both modes, so tests need only skip when run with the opposite mode from what they need.

@findleyr findleyr changed the title go/types, types2: aliases in go/types.Universe should be of type Alias go/types, types2: 'any' in go/types.Universe should be of type Alias Apr 22, 2024
@adonovan
Copy link
Member

@findleyr Sounds good. So maybe any is simply always represented with an Alias node?

Perhaps it would be less trouble to make this change later, once we've ditched gotypesalias=0 forever. Then there is no mode-dependence; we've just made the representation of any a little more faithful to the spec.

@findleyr
Copy link
Contributor Author

@adonovan I think we should rip the band-aid off now. The sooner we make this change, the sooner we can avoid workarounds like https://go-review.git.corp.google.com/c/tools/+/580835/4/internal/refactor/inline/inline.go#2874.

@adonovan
Copy link
Member

That workaround seems a lot simpler to me than the problem of initializing a global based on the value of an environment variable that we expect tests to modify.

@findleyr
Copy link
Contributor Author

@adonovan that is just one of several, and it's likely there are more workarounds for any that don't exist yet should.

I don't see why we should wait, and then have to make yet another breaking change in Go 1.24 or Go 1.25.
Less ergonomic testing seems easier than having to make another breaking alias-related change in a future Go version.

@adonovan
Copy link
Member

I don't see why we should wait, and then have to make yet another breaking change in Go 1.24 or Go 1.25. Less ergonomic testing seems easier than having to make another breaking alias-related change in a future Go version.

My gut feeling is that the less ergonomic testing could be a major hardship for users of go/types, whereas the specific representation of the type any should be a fairly narrow change.

If I understand correctly, the workaround in the above code is just to detect shadowing declarations of any, which should be very rare. If you had forgotten to add the workaround, you would temporarily have a bug, but this bug is not a new problem due to Aliases, it's one that would have already been present in this tool (and similar ones like rename) due to the lack of aliases. And when Aliases come along, the bug would become fixed.

@findleyr
Copy link
Contributor Author

Decision: we're going to switch the implementation of any based on gotypesalias, and hijack Scope.Lookup to do the right thing based on the current debug value.

I'm doing this in the above CL (as well as adding some safeguards against improper concurrent use), but as always it's proving trickier than one would hope. Should be able to get it working tomorrow morning.

@gopherbot
Copy link

Change https://go.dev/cl/582335 mentions this issue: gopls/internal/golang/completion: fix the isEmptyInterface predicate

gopherbot pushed a commit to golang/tools that referenced this issue Apr 29, 2024
The isEmptyInterface predicate had a TODO noting that it should probably
be considering the underlying type. After looking at usage, I agree that
this was probably simple any oversight, but didn't matter because most
uses of the empty interface were uses of any or interface{}, both of
which were an interface.

But with CL 580355, as the 'any' type is becoming a types.Alias, and
this logic is caused inference scoring to change and gopls tests to
fail. Fix isEmptyInterface to check underlying, and add a bit of
commentary for the future.

For golang/go#66921

Change-Id: I7ba3db1b04d83dda0372d9c39b943965f4d8c955
Reviewed-on: https://go-review.googlesource.com/c/tools/+/582335
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. release-blocker
Projects
None yet
Development

No branches or pull requests

4 participants