Skip to content

go/types: statically inferring the run-time properties of type parameters #71464

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

Closed
fbbdev opened this issue Jan 28, 2025 · 4 comments
Closed
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool
Milestone

Comments

@fbbdev
Copy link

fbbdev commented Jan 28, 2025

[NOTE: I am tentatively associating this issue with go/types, but refraining initially from marking it as a proposal because there are multiple solutions with different scopes and I am unsure about the best course of action.]

Currently and by design, the only methods that can be called on values whose type is a parameter are those explicitly defined by their constraint, either directly or by embedding. That however does not match the runtime semantics of those values, e.g. the outcome of dynamic type assertions or reflection methods; for example (playground):

package main

type T1 struct{}

func (T1) Method() {}

type T2 struct{}

func (*T2) Method() {}

type Constraint interface {
	T1 | *T2
}

type Mset interface {
	Method()
}

func Fn[T Constraint](x T) {
	//x.Method() // ERROR
	//x.(Mset).Method() // ERROR!
	any(x).(Mset).Method() // Works
}

func main() {
	Fn(T1{})
	Fn(&T2{})
}

The facilities provided in go/types are geared towards compile-time type checking: for example types.Implements(<T>, <Mset>) returns false. No alternatives are provided to infer runtime behavior, which makes some kinds of static analysis exceedingly hard.

In my case, for example, I want to infer how encoding/json.Marshal is going to behave on values of parametric type, specifically whether all members of the type set either implement encoding.TextMarshaler or have a string as underlying type, and are therefore usable as map keys, for arbitrary type constraints.

It is true that I can access the full definition of a type constraint and compute its type set. I considered copying the private code that go/types uses to do so, but was turned off by its complexity and the associated maintenance burden. It would be so much better if go/types could make this knowledge available through a public API.

I can think of a number of solutions, which are not necessarily mutually exclusive; from the easiest to the most radical:

  1. add one or more predicates for runtime behavior, e.g. RuntimeAssertableTo or similar;
  2. add a public API for enumerating the type set of a constraint, in addition to the method set;
  3. publish the currently private go/types._TypeSet API (not sure why it has been made private, and what the drawbacks would be);
  4. change the spec to have the method set of constraint interfaces include the intersection of the method sets of their type terms.

For my purposes I'd be happy with option (2) or (3) — (1) would be useful but not sufficient, and perhaps not a very good API.

Personally I'd very much like to see something like (4) adopted, as it would eliminate a large part of the compile-time/run-time mismatch and add some use cases for generics. Not sure whether it would fall under the umbrella of #70128; I went back in time a bit but couldn't find any issue related to method access on type parameters. It seems to me more lightweight and approachable than other proposals, but again perhaps I'm missing some known hurdles.

I'd be happy to write a proposal, but as noted above I'm not sure about the approach to pick.

@gabyhelp gabyhelp added the LanguageProposal Issues describing a requested change to the Go language specification. label Jan 28, 2025
@findleyr
Copy link
Member

FWIW, #61013 discusses exposing an API for normalized terms.
We've been using something like https://pkg.go.dev/golang.org/x/exp/typeparams#NormalTerms inside x/tools for a while now (too long).

I'll promote that issue to a proposal.

@findleyr
Copy link
Member

@fbbdev I wrote up a more concrete proposal for accessing the normalized term set of a type:
#61013 (comment)

Please let me know if that would serve your needs (feel free to comment directly on that issue).

@adonovan adonovan added this to the Go1.25 milestone Jan 28, 2025
@adonovan adonovan added LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool and removed LanguageProposal Issues describing a requested change to the Go language specification. labels Jan 28, 2025
@fbbdev
Copy link
Author

fbbdev commented Jan 29, 2025

@findleyr thanks so much for the pointers.

We've been using something like https://pkg.go.dev/golang.org/x/exp/typeparams#NormalTerms

I had only looked into x/go, didn't think of looking into x/exp. That should provide an effective workaround for the time being.

@findleyr
Copy link
Member

I had only looked into x/go, didn't think of looking into x/exp.

...and you shouldn't need to. We should have upstreamed an equivalent API by now, but time is fleeting. Let's close this as a dupe of #61013. Thanks for the nudge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool
Projects
None yet
Development

No branches or pull requests

4 participants