From 3fff213ac24dc3b9b92c8a1f5f18ec0c97bac4c0 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Tue, 31 Aug 2021 08:01:44 -0700 Subject: [PATCH] cmd/compile: add CONVIFACE nodes needed in generic code due to assignments Added new function earlyTransformAssign() to add needed CONVIFACE nodes due to assignments in generic functions. Fixes #48049 Change-Id: I7cd9cee6ecf34ed2ef0743d1b17645b9f520fa00 Reviewed-on: https://go-review.googlesource.com/c/go/+/347914 Trust: Dan Scales Run-TryBot: Dan Scales TryBot-Result: Go Bot Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/stmt.go | 3 ++ src/cmd/compile/internal/noder/transform.go | 53 +++++++++++++++++++++ test/typeparam/issue48049.go | 33 +++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 test/typeparam/issue48049.go diff --git a/src/cmd/compile/internal/noder/stmt.go b/src/cmd/compile/internal/noder/stmt.go index eeb994d343..146761c23f 100644 --- a/src/cmd/compile/internal/noder/stmt.go +++ b/src/cmd/compile/internal/noder/stmt.go @@ -101,6 +101,8 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { n.Def = initDefn(n, names) if delay { + earlyTransformAssign(n, lhs, rhs) + n.X, n.Y = lhs[0], rhs[0] n.SetTypecheck(3) return n } @@ -115,6 +117,7 @@ func (g *irgen) stmt(stmt syntax.Stmt) ir.Node { n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, lhs, rhs) n.Def = initDefn(n, names) if delay { + earlyTransformAssign(n, lhs, rhs) n.SetTypecheck(3) return n } diff --git a/src/cmd/compile/internal/noder/transform.go b/src/cmd/compile/internal/noder/transform.go index 180891b5b5..b278f3db09 100644 --- a/src/cmd/compile/internal/noder/transform.go +++ b/src/cmd/compile/internal/noder/transform.go @@ -365,6 +365,59 @@ assignOK: } } +// Version of transformAssign that can run on generic code that adds CONVIFACE calls +// as needed (and rewrites multi-value calls). +func earlyTransformAssign(stmt ir.Node, lhs, rhs []ir.Node) { + cr := len(rhs) + if len(rhs) == 1 { + if rtyp := rhs[0].Type(); rtyp != nil && rtyp.IsFuncArgStruct() { + cr = rtyp.NumFields() + } + } + + // x,y,z = f() + _, isCallExpr := rhs[0].(*ir.CallExpr) + if isCallExpr && cr > len(rhs) { + stmt := stmt.(*ir.AssignListStmt) + stmt.SetOp(ir.OAS2FUNC) + r := rhs[0].(*ir.CallExpr) + rtyp := r.Type() + + mismatched := false + failed := false + for i := range lhs { + result := rtyp.Field(i).Type + + if lhs[i].Type() == nil || result == nil { + failed = true + } else if lhs[i] != ir.BlankNode && !types.Identical(lhs[i].Type(), result) { + mismatched = true + } + } + if mismatched && !failed { + typecheck.RewriteMultiValueCall(stmt, r) + } + return + } + + // x, ok = y + if len(lhs) != len(rhs) { + assert(len(lhs) == 2 && len(rhs) == 1) + // TODO(danscales): deal with case where x or ok is an interface + // type. We want to add CONVIFACE now, but that is tricky, because + // the rhs may be AS2MAPR, AS2RECV, etc. which has two result values, + // and that is not rewritten until the order phase (o.stmt, as2ok). + return + } + + // Check for interface conversion on each assignment + for i, r := range rhs { + if lhs[i].Type() != nil && lhs[i].Type().IsInterface() { + rhs[i] = assignconvfn(r, lhs[i].Type()) + } + } +} + // Corresponds to typecheck.typecheckargs. Really just deals with multi-value calls. func transformArgs(n ir.InitNode) { var list []ir.Node diff --git a/test/typeparam/issue48049.go b/test/typeparam/issue48049.go new file mode 100644 index 0000000000..3a005142df --- /dev/null +++ b/test/typeparam/issue48049.go @@ -0,0 +1,33 @@ +// run -gcflags=-G=3 + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() { + Gooer2[byte]() +} + +type Fooer[T any] interface { + Foo(p T) +} + +type fooer1[T any] struct{} + +func (fooer1[T]) Foo(T) {} + +type fooer2[T any] struct { + r []Fooer[T] +} + +//go:noinline +func (mr fooer2[T]) Foo(p T) { + mr.r[0] = fooer1[T]{} + return +} + +func Gooer2[T any]() Fooer[T] { + return fooer2[T]{} +} -- 2.50.0