-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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: additions to support type parameters #47916
Comments
Thanks for sharing. Some initial surface reactions: *Interface.IsConstraint could be renamed to *Interface.IsConstraintOnly or *Interface.HasTypeList, I would find find this more clear, to distinguish whether "IsConstraint" means that the *Interface.IsConstraint is actually used as a constraint. Question: Why
is *Interface on get and Type on set? Shouldn't that be exactly the same type? From A high level, I think everything which provides an interface for instantiation should be coupled with an actual use case (eg as we discussed plans to treat x/tools/go/ssa, maybe it would be used there). I think also this section should describe how one navigates already instantiated types. |
Are the SetTParams/SetRParams methods necessary? They feel out of character for the go/types API, where most data types are immutable. The other SetFoo methods (e.g., Named.SetUnderlying, TypeParams.SetConstraint) exist to break cycles, but at least Signatures can't be in cycles. I don't think Named can be either; e.g., -- I'd suggest the following changes to Info.Inferred:
I think this would considerably simplify the task of decomposing the use of a generic function into its -- We'll probably want to extend the I'd suggest this time, instead of just adding interface with more arguments (a la |
Indeed they are out of character, and in fact the original (internal) draft of this API had new constructors (I think they were called e.g.
Thanks, I think we do need something to simplify I don't see a reason not to take your suggestions.
Just to be clear, suppose I have Perhaps we should have a |
Thanks very much, you're right:
Could you say more about what you mean? I think you mean "all the new APIs related to type and function instances should have examples"? In this case, you're right that it would be good to provide some examples of using |
Examples are good and I think they would suffice, given time constraints for 1.18. But what I was trying to bring up was more encouragement for generally considering use cases. From the point of view of everything that depends on stdlib go/*, its great that you have seriously considered backward compatibility already (for inputs which have no type params), but those things will tend to need to be extended to handle type parameters, and it is not yet clear how they will be extended. So this was more encouragement to consider end-to-end use cases. (The compiler/internal implementations do not have this problem, because they are "the" implementation) I thought of one such example which might be interesting to consider (also pulling in the ast and token changes): the printf vetter. Will it for example work on
More generally, the introduction of typeparams gives a lot of use cases for go/ast and go/types which are not read-only. In the example above, instantiating F for the types in Intish could be one thing the printf checker could do. This feedback is more about process than content: I just have an intuition that keeping tabs of a running end-to-end example use case can help confirm, and possibly guide, the designs of std go/* for type parameters. If you (or we) have got the cycles for this kind of thing, great, I think it will help. But there is no specific content in this feedback point to address, so please don't take it as an objection of any kind to address. (on a side note the development of typeparams based on compiler internal packages makes me curious about the possibility of exposing some of them, like ir) |
Some thoughts about
and the discussion on Inferred above. To me, that the type of a function at a call site may be inferred or not (ie with different syntax) should not affect the representation of the type associated with the function expression at the call site. I think it would be most natural if the Info.Type of a function or method value at a call site were always instantiated (always instantiated if named, but func lits cannot be generic, so maybe that is ok). Then the simple mechanism for types.Named can be used to retrieve the generic type and arguments, and that will be more uniform. Thoughts? Am I missing something? |
As a follow-up, I guess what I am trying to say in the last remark above about Inferred, is it seems like it would be much easier to use if both Signatures and Named had TArgs had TArgs() and Orig(), and then at all call sites, the Info.Type of the caller expression would always be the instantiated type. |
@scott-cotton, quick follow-up below. I'll follow-up in more detail next week (I'm currently traveling).
A couple related points here:
Any examples that inform these two points are very valuable. The printf analyzer suggestion is a good example, because the implementation verifies properties of the underlying type of a variable. This could be updated to handle type parameters by accessing their constraints, considering embedded Unions, and walking their type set expression. However, we have an unexported method on type parameters:
The current APIs don't provide a high-level mechanism for "instantiating" function bodies. However, one good thing about keeping
I need to think about this a bit more. |
After writing this, it occurred to me that it might be helpful to provide a more general 'substitution' API, independent of
That is to say, an API for substituting for type parameters in arbitrary types, not just the types associated with parameterized declarations. |
If there's an explicit (even partial) instantiation like For For
In principle, I like the idea of an API like that (but replacing the first |
This proposal has been added to the active column of the proposals project |
I think this reasoning is more consistent w.r.t. how generics are treated. But I would guess a common use case (such as call graph construction) would tend to want to treat call sites uniformly -- whether or not they have instantiated type parameters. if Types["f"] for generic f at calsite in form f(...) is the instantiated type, then I guess it would be easier for analysers to consider function calls uniformly. Although in any case, the call might be in scope of and make reference to a type parameter, if the type is the instantiated type, then an analyzer only needs to think about type parameters that are not bound. This seems easier to me for any analyzer which needs to treat call sites. One thing is becoming clear: golang.org/x/tools/go has a long way to go to support type parameters. I am not even sure what a call graph is in a go library with type parameters... unless the call graph has type parameters but then maybe there is a type switch that will make it rather hairy to comprehend (all that on top of the different algos and static/dynamic distinctions in place now...) @timothy-king perhaps a tracking issue is in order for golang.org/x/tools/go/....? |
Ack, this is the use case I have in mind from having worked on integrating types2 into cmd/compile. The uniformity in my proposal is that you can always strip away explicit instantiations and package qualification. E.g., in unified IR, there's this logic: go/src/cmd/compile/internal/noder/writer.go Lines 1318 to 1327 in a6ff433
go/src/cmd/compile/internal/noder/writer.go Lines 1773 to 1813 in a6ff433
My suggestions would simplify the first code to just func lookupObj(info *types2.Info, expr syntax.Expr) (obj types2.Object, targs *types2.TypeList) {
// Strip explicit instantiation, if present.
if index, ok := expr.(*syntax.IndexExpr); ok {
if args := unpackListExpr(index.Index); len(args) == 1 {
tv, ok := info.Types[args[0]]
assert(ok)
if tv.IsValue() {
return // normal index expression
}
}
expr = index.X
}
// Strip package qualifier, if present.
if sel, ok := expr.(*syntax.SelectorExpr); ok {
if !isPkgQual(info, sel) {
return // normal selector expression
}
expr = sel.Sel
}
if name, ok := expr.(*syntax.Name); ok {
obj = info.Uses[name]
targs = info.Inferred[name]
}
return
}
I expect most analysis will just ignore the type parameters. More sophisticated analysis will probably want to just treat them similarly to regular parameters. |
@mdempsky thanks for the clarification. I still wonder whether there is existing code which supposes that, for call 'f.(args)', Types[f].(*types.Signature).Param(i) can be assigned from Types[args[I]]. Hard to say the behaviour impact w.r.t. compatibility. |
Sorry, I'm not sure I follow your concern here. I think my suggestion does ensure that would work. In particular, for any ast.CallExpr, I'm recommending that Unless I have your point backwards, and you're questioning whether we actually need to guarantee that? If so, I offer mdempsky/unconvert as an existence proof of a go/types application that expects that to work: https://github.com/mdempsky/unconvert/blob/95ecdbfc0b5f3e65790c43c77874ee5357ad8a8f/unconvert.go#L492 |
@mdempsky sorry I misunderstood, I think we were saying the same thing but weren't aware :) thanks for the link to unconvert; good to see it will work with your suggestion. (I still think it is worth considering if .Orig() and .TArgs can be extended so that it works for call.Fun as well as Named) |
(not sure where to report this, so here it goes) it seems there's a typo in the
should actually read:
|
As pointed out at: golang/go#47916 (comment) Fix the receiver for TParamList methods. Change-Id: Ic26e859a0830447c2926828e29d5f27bf2c5637f Reviewed-on: https://go-review.googlesource.com/c/proposal/+/346089 Reviewed-by: Robert Findley <[email protected]>
@sbinet yes you're right, thanks for pointing that out. Fixed in https://golang.org/cl/346089. |
There has been a lot of discussion so far about x/tools/go already. The one package I am somewhat worried about is |
A brief summary of the current status of this proposal (some of this is contained in the comments above, some of it is from other discussions): We've discussed a couple superficial changes to the proposal.
Additionally, we still need to work out the following non-superficial changes:
At least all of these points need to be resolved. I'll update the proposal doc and top comment of this issue to reflect this (likely tomorrow as today is a holiday in the US). |
Regarding the name I think we should explore other alternative names. |
Change https://golang.org/cl/348376 mentions this issue: |
As discussed on the go/types proposal (#47916), we should spell out the word 'Type', rather than using 'T'. Change-Id: I5f51255eedc07fea61f909b7ecb3093a7fab765e Reviewed-on: https://go-review.googlesource.com/c/go/+/348376 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> TryBot-Result: Go Bot <[email protected]>
Thanks everyone who helped solidify this proposal. This is a large API surface and I'm very grateful for the feedback. I'm not closing this yet as there are a few edge cases that need to be cleaned up (notably some type strings need to be improved, and |
Change https://golang.org/cl/363635 mentions this issue: |
Instantiate already returns an error when validation fails. Panicking on an incorrect number of type arguments means that callers must both pre-validate the number of type arguments and handle resulting errors. Returning an error rather than panicking allows eliminating pre-validation at the call-site. Also update the Instantiate docstring to correct some stale/inaccurate information, and to clarify its behavior more precisely. Updates #47916 Change-Id: I997ef30b3486760a90b0db4c3ea7111280d74a81 Reviewed-on: https://go-review.googlesource.com/c/go/+/363635 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
Change https://golang.org/cl/364534 mentions this issue: |
Change https://golang.org/cl/364535 mentions this issue: |
…atic This CL is a clean port of CL 351335 from go/types to types2. Updates #47916 Change-Id: Idc377fb71d480a51d5e93a348f3a880346011974 Reviewed-on: https://go-review.googlesource.com/c/go/+/364535 Trust: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]>
…rrect len(targs) This CL is a clean port of CL 363635 from go/types to types2. Updates #47916 Change-Id: Ib46758435c31ad9a6a4a63f552503d5afa66b5c0 Reviewed-on: https://go-review.googlesource.com/c/go/+/364534 Trust: Robert Griesemer <[email protected]> Reviewed-by: Robert Findley <[email protected]>
Change https://golang.org/cl/364716 mentions this issue: |
…face This is a port of CL 359016 from types2 to go/types. Some of the code around untyped nil differed (because we have to treat untyped nil differently in go/types for historical reasons). Updates #47916 Change-Id: Ifc428ed977bf2f4f84cc831f1a3527156940d7b8 Reviewed-on: https://go-review.googlesource.com/c/go/+/364716 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
Change https://golang.org/cl/368956 mentions this issue: |
Change https://golang.org/cl/369475 mentions this issue: |
NewTypeList was not part of the go/types API proposal, and was left in by accident. It also shouldn't be necessary, so remove it. Updates #47916 Change-Id: I4db3ccf036ccfb708ecf2c176ea4921fe68089a4 Reviewed-on: https://go-review.googlesource.com/c/go/+/369475 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
Change https://golang.org/cl/373775 mentions this issue: |
For #47694 For #47916 Change-Id: Ieeffaf161da744adfdb4da8aac58a64c109ebcab Reviewed-on: https://go-review.googlesource.com/c/go/+/373775 Trust: Ian Lance Taylor <[email protected]> Reviewed-by: Robert Findley <[email protected]>
This proposal formally introduces the changes we've made to support parameterized functions and types in
go/types
. See the full write-up here:https://go.googlesource.com/proposal/+/master/design/47916-parameterized-go-types.md
Also see the corresponding proposal for
go/ast
andgo/token
: #47781.Any feedback is appreciated. In recognition that this proposal contains a large new API surface, we will not start to evaluate whether discussion is resolved at least a few weeks. If there appears to be consensus that a change is required to the original proposal, we'll update the document and add a note here.
CC @griesemer
Changelog: (changes from the initial proposal)
TParamList
was renamed toTypeParamList
TParams
fields were renamed toTypeParams
TArgs
was renamed toTypeArgs
RParams
was renamed toRecvTypeParams
Orig
was renamed toOrigin
Info.Inferred
was renamed toInfo.Instances
, changed to use the*ast.Ident
as key, and updated to capture all type and signature instantiation.ArgumentError
was tweaked to be more idiomatic.Environment
was renamed toContext
*Signature
type were replaced with a new constructor:NewSignatureType
TypeParams.Index
accessor was added.Interface.IsConstraint
was replaced byInterface.IsMethodSet
.Note: there are a few caveats/discrepancies in the current implementation. I'll keep this updated as they are resolved to coincide with the proposal.
Instantiate
will panic if passed anything other than a*Named
or*Signature
type, or with incorrect lengthtargs
. In the proposal we decided to instead make this return an error, but that is not yet done.The text was updated successfully, but these errors were encountered: