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: Compiler Type Inferencing on Methods With Narrowing Types #60975

Closed
carmichaeljr opened this issue Jun 23, 2023 · 5 comments
Closed
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.

Comments

@carmichaeljr
Copy link

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

$ go version
go dev branch

Does this issue reproduce with the latest release?

Yes, on go tip

What did you do?

There are two scenarios here.

Scenario 1:
https://go.dev/play/p/z_69dFItT4D?v=gotip

// You can edit this code!
// Click here and start typing.
package main

import "fmt"

type Thing[T any] struct {
	A T
}

func (t Thing[T]) PrintThing(inputParam int) {
	fmt.Printf("Print thing 'self': %v\n", t)
	fmt.Printf("Print thing param: %v\n", inputParam)
}

func (t Thing[int]) PrintThing2(inputParam int) {
	fmt.Printf("Print thing 2 'self': %v\n", t)
	fmt.Printf("Print thing 2 param: %v\n", inputParam)
}

func main() {
	one := Thing[int]{}
	two := Thing[string]{"Two"}

	one.PrintThing(1)
	one.PrintThing2(1)

	two.PrintThing(1)
	two.PrintThing2(1)     // Would expect a compiler error saying PrintThing2 cannot be called using variable two because types Thing[int] and Thing[string] do not match.
        // Comment out the line above to see the result of the next line.
	two.PrintThing2("str") // Looking past the expected behavior, the inputParam type is no longer correct. It should be an int but for some reason only accepts strings
}

// When ```twoPrintThing2(1)``` is not commented out:
// ./prog.go:29:18: cannot use 1 (untyped int constant) as string value in argument to two.PrintThing2
// 
// Go build failed.
//
// When ```twoPrintThing2(1)``` is commented out:
// Print thing 'self': {0}
// Print thing param: 1
// Print thing 2 'self': {0}
// Print thing 2 param: 1
// Print thing 'self': {Two}
// Print thing param: 1
// Print thing 2 'self': {Two}
// Print thing 2 param: str       <-- This demonstrates secondary issue
// 
// Program exited

Scenario 2:
https://go.dev/play/p/PNQ2bWh3BO-?v=gotip

// You can edit this code!
// Click here and start typing.
package main

import "fmt"

type Thing[T any] struct {
	A T
}

func (t Thing[T]) PrintThing(inputParam int) {
	fmt.Printf("Print thing 'self': %v\n", t)
	fmt.Printf("Print thing param: %v\n", inputParam)
	intAsArg(inputParam)
}

func (t Thing[int]) PrintThing2(inputParam int) {
	fmt.Printf("Print thing 2 'self': %v\n", t)
	fmt.Printf("Print thing 2 param: %v\n", inputParam)
	intAsArg(inputParam)
}

func intAsArg(v int) {
	//
}

func main() {
	one := Thing[int]{}
	two := Thing[string]{"Two"}

	one.PrintThing(1)
	one.PrintThing2(1)

	two.PrintThing(1)
	two.PrintThing2(1)     // Would expect a compiler error saying PrintThing2 cannot be called using variable two because types Thing[int] and Thing[string] do not match.
}

// ./prog.go:20:11: cannot use inputParam (variable of type int /* with int declared at ./prog.go:17:15 */ constrained by any) as int value in argument to intAsArg
// ./prog.go:35:18: cannot use 1 (untyped int constant) as string value in argument to two.PrintThing2
// 
// Go build failed.

What did you expect to see?

For scenario 1: A compiler error saying PrintThing2 cannot be called using variable two because types Thing[int] and Thing[string] do not match. I can understand how this scenario would be an unsupported edge case because of the complications of having to dynamically filter method bindings upon struct creation, but if methods really are just functions, then I could also see this being a reasonable feature to have implemented. If this feature is allowed, then there is a secondary issue where the inputParam argument changes type.

For scenario 2: This builds off of scenario 1. In this case the compiler throws a rather confusing error saying that an int is not an int. I do not see why the inputParam variable would be constrained by any when it is strictly declared as in int. I would expect to be able to call intAsArg without issue. Of note is the same error regarding the inputParam changing its type is present.

@dmitshur dmitshur changed the title Compiler Type Inferencing on Methods With Narrowing Types cmd/compile: Compiler Type Inferencing on Methods With Narrowing Types Jun 23, 2023
@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Jun 23, 2023
@dmitshur dmitshur added NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. and removed compiler/runtime Issues related to the Go compiler and/or runtime. labels Jun 23, 2023
@dmitshur
Copy link
Contributor

Thanks for reporting. Does this also happen in Go 1.20 and/or 1.19, or did something change at Go tip only?

@dmitshur dmitshur added the compiler/runtime Issues related to the Go compiler and/or runtime. label Jun 23, 2023
@seankhliao
Copy link
Member

This doesn't mean only apply when the type is int. It means use int as the name of the type parameter, shadowing the predeclared int type.

func (t Thing[int]) PrintThing2(inputParam int) {}
// is equivalent to
func (t Thing[T]) PrintThing2(inputParam T) {}

@seankhliao seankhliao closed this as not planned Won't fix, can't repro, duplicate, stale Jun 23, 2023
@seankhliao
Copy link
Member

see also #48123

@timothy-king
Copy link
Contributor

@seankhliao Any sense for whether we have seen this confusion enough for a vet check?

@seankhliao
Copy link
Member

There's also a faq entry https://go.dev/doc/faq#types_in_method_declaration

There are some reports
#60017 #58100 #57182 #52058 #49984 #48813

But doesn't appear to be common in committed code:
https://github.com/search?type=code&q=language%3AGo+%2F%5Efunc+%5C%28%5B%5E%29%5D*%5C%5B%28%28%5C%5B%5C%5D%29%3F%28u%3Fint.%3F.%3F%7Cfloat..%7Cbyte%7Crune%7Cstring%7Cbool%29%28%2C+%29%3F%29%2B%5C%5D%5C%29+.*%2F

So maybe it is more of an education problem, since you're unlikely to do it once you know it doesn't work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

5 participants