From 1ee70da3125cb6339c1bcb0c127cd97a9e1dbe90 Mon Sep 17 00:00:00 2001 From: Dan Scales Date: Sat, 8 Jan 2022 10:33:35 -0800 Subject: [PATCH] cmd/compile: fix the names of methods created during type substitution The names given to methods of types created during type substitution were possible incorrect when the type parameters themselves were nested types. Fixes #50485 Change-Id: I7e0043ed22c26406a5f9d8d51d9e928770a678f6 Reviewed-on: https://go-review.googlesource.com/c/go/+/377494 Reviewed-by: Keith Randall Trust: Dan Scales Run-TryBot: Dan Scales TryBot-Result: Gopher Robot --- src/cmd/compile/internal/typecheck/subr.go | 24 ++- test/typeparam/issue50485.dir/a.go | 239 +++++++++++++++++++++ test/typeparam/issue50485.dir/main.go | 9 + test/typeparam/issue50485.go | 7 + 4 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 test/typeparam/issue50485.dir/a.go create mode 100644 test/typeparam/issue50485.dir/main.go create mode 100644 test/typeparam/issue50485.go diff --git a/src/cmd/compile/internal/typecheck/subr.go b/src/cmd/compile/internal/typecheck/subr.go index da5e9645eaa..04a4ed392fb 100644 --- a/src/cmd/compile/internal/typecheck/subr.go +++ b/src/cmd/compile/internal/typecheck/subr.go @@ -976,7 +976,9 @@ func makeInstName1(name string, targs []*types.Type, hasBrackets bool) string { // function that helps implement a method of an instantiated type). For method nodes // on shape types, we prepend "nofunc.", because method nodes for shape types will // have no body, and we want to avoid a name conflict with the shape-based function -// that helps implement the same method for fully-instantiated types. +// that helps implement the same method for fully-instantiated types. Function names +// are also created at the end of (*Tsubster).typ1, so we append "nofunc" there as +// well, as needed. func MakeFuncInstSym(gf *types.Sym, targs []*types.Type, isMethodNode, hasBrackets bool) *types.Sym { nm := makeInstName1(gf.Name, targs, hasBrackets) if targs[0].HasShape() && isMethodNode { @@ -1273,7 +1275,25 @@ func (ts *Tsubster) typ1(t *types.Type) *types.Type { for i, f := range t.Methods().Slice() { t2 := ts.typ1(f.Type) oldsym := f.Nname.Sym() - newsym := MakeFuncInstSym(oldsym, ts.Targs, true, true) + + // Use the name of the substituted receiver to create the + // method name, since the receiver name may have many levels + // of nesting (brackets) with type names to be substituted. + recvType := t2.Recv().Type + var nm string + if recvType.IsPtr() { + recvType = recvType.Elem() + nm = "(*" + recvType.Sym().Name + ")." + f.Sym.Name + } else { + nm = recvType.Sym().Name + "." + f.Sym.Name + } + if recvType.RParams()[0].HasShape() { + // We add "nofunc" to methods of shape type to avoid + // conflict with the name of the shape-based helper + // function. See header comment of MakeFuncInstSym. + nm = "nofunc." + nm + } + newsym := oldsym.Pkg.Lookup(nm) var nname *ir.Name if newsym.Def != nil { nname = newsym.Def.(*ir.Name) diff --git a/test/typeparam/issue50485.dir/a.go b/test/typeparam/issue50485.dir/a.go new file mode 100644 index 00000000000..3a7c71a711e --- /dev/null +++ b/test/typeparam/issue50485.dir/a.go @@ -0,0 +1,239 @@ +package a + +import "fmt" + +type ImplicitOrd interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 | + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | + ~float32 | ~float64 | + ~string +} + +func LessGiven[T ImplicitOrd]() Ord[T] { + return LessFunc[T](func(a, b T) bool { + return a < b + }) +} + +type Eq[T any] interface { + Eqv(a T, b T) bool +} + +type Ord[T any] interface { + Eq[T] + Less(a T, b T) bool +} + +type LessFunc[T any] func(a, b T) bool + +func (r LessFunc[T]) Eqv(a, b T) bool { + return r(a, b) == false && r(b, a) == false +} + +func (r LessFunc[T]) Less(a, b T) bool { + return r(a, b) +} + +type Option[T any] struct { + v *T +} + +func (r Option[T]) IsDefined() bool { + return r.v != nil +} + +func (r Option[T]) IsEmpty() bool { + return !r.IsDefined() +} + +func (r Option[T]) Get() T { + return *r.v +} + +func (r Option[T]) String() string { + if r.IsDefined() { + return fmt.Sprintf("Some(%v)", r.v) + } else { + return "None" + } +} + +func (r Option[T]) OrElse(t T) T { + if r.IsDefined() { + return *r.v + } + return t +} + +func (r Option[T]) Recover(f func() T) Option[T] { + if r.IsDefined() { + return r + } + t := f() + return Option[T]{&t} +} + +type Func1[A1, R any] func(a1 A1) R + +type Func2[A1, A2, R any] func(a1 A1, a2 A2) R + +func (r Func2[A1, A2, R]) Curried() Func1[A1, Func1[A2, R]] { + return func(a1 A1) Func1[A2, R] { + return Func1[A2, R](func(a2 A2) R { + return r(a1, a2) + }) + } +} + +type HList interface { + sealed() +} + +// Header is constrains interface type, enforce Head type of Cons is HT +type Header[HT any] interface { + HList + Head() HT +} + +// Cons means H :: T +// zero value of Cons[H,T] is not allowed. +// so Cons defined as interface type +type Cons[H any, T HList] interface { + HList + Head() H + Tail() T +} + +type Nil struct { +} + +func (r Nil) Head() Nil { + return r +} + +func (r Nil) Tail() Nil { + return r +} + +func (r Nil) String() string { + return "Nil" +} + +func (r Nil) sealed() { + +} + +type hlistImpl[H any, T HList] struct { + head H + tail T +} + +func (r hlistImpl[H, T]) Head() H { + return r.head +} + +func (r hlistImpl[H, T]) Tail() T { + return r.tail +} + +func (r hlistImpl[H, T]) String() string { + return fmt.Sprintf("%v :: %v", r.head, r.tail) +} + +func (r hlistImpl[H, T]) sealed() { + +} + +func hlist[H any, T HList](h H, t T) Cons[H, T] { + return hlistImpl[H, T]{h, t} +} + +func Concat[H any, T HList](h H, t T) Cons[H, T] { + return hlist(h, t) +} + +func Empty() Nil { + return Nil{} +} +func Some[T any](v T) Option[T] { + return Option[T]{}.Recover(func() T { + return v + }) +} + +func None[T any]() Option[T] { + return Option[T]{} +} + +func Ap[T, U any](t Option[Func1[T, U]], a Option[T]) Option[U] { + return FlatMap(t, func(f Func1[T, U]) Option[U] { + return Map(a, f) + }) +} + +func Map[T, U any](opt Option[T], f func(v T) U) Option[U] { + return FlatMap(opt, func(v T) Option[U] { + return Some(f(v)) + }) +} + +func FlatMap[T, U any](opt Option[T], fn func(v T) Option[U]) Option[U] { + if opt.IsDefined() { + return fn(opt.Get()) + } + return None[U]() +} + +type ApplicativeFunctor1[H Header[HT], HT, A, R any] struct { + h Option[H] + fn Option[Func1[A, R]] +} + +func (r ApplicativeFunctor1[H, HT, A, R]) ApOption(a Option[A]) Option[R] { + return Ap(r.fn, a) +} + +func (r ApplicativeFunctor1[H, HT, A, R]) Ap(a A) Option[R] { + return r.ApOption(Some(a)) +} + +func Applicative1[A, R any](fn Func1[A, R]) ApplicativeFunctor1[Nil, Nil, A, R] { + return ApplicativeFunctor1[Nil, Nil, A, R]{Some(Empty()), Some(fn)} +} + +type ApplicativeFunctor2[H Header[HT], HT, A1, A2, R any] struct { + h Option[H] + fn Option[Func1[A1, Func1[A2, R]]] +} + +func (r ApplicativeFunctor2[H, HT, A1, A2, R]) ApOption(a Option[A1]) ApplicativeFunctor1[Cons[A1, H], A1, A2, R] { + + nh := FlatMap(r.h, func(hv H) Option[Cons[A1, H]] { + return Map(a, func(av A1) Cons[A1, H] { + return Concat(av, hv) + }) + }) + + return ApplicativeFunctor1[Cons[A1, H], A1, A2, R]{nh, Ap(r.fn, a)} +} +func (r ApplicativeFunctor2[H, HT, A1, A2, R]) Ap(a A1) ApplicativeFunctor1[Cons[A1, H], A1, A2, R] { + + return r.ApOption(Some(a)) + +} + +func Applicative2[A1, A2, R any](fn Func2[A1, A2, R]) ApplicativeFunctor2[Nil, Nil, A1, A2, R] { + return ApplicativeFunctor2[Nil, Nil, A1, A2, R]{Some(Empty()), Some(fn.Curried())} +} +func OrdOption[T any](m Ord[T]) Ord[Option[T]] { + return LessFunc[Option[T]](func(t1 Option[T], t2 Option[T]) bool { + if !t1.IsDefined() && !t2.IsDefined() { + return false + } + return Applicative2(m.Less).ApOption(t1).ApOption(t2).OrElse(!t1.IsDefined()) + }) +} + +func Given[T ImplicitOrd]() Ord[T] { + return LessGiven[T]() +} diff --git a/test/typeparam/issue50485.dir/main.go b/test/typeparam/issue50485.dir/main.go new file mode 100644 index 00000000000..88a765bfe9e --- /dev/null +++ b/test/typeparam/issue50485.dir/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "a" +) + +func main() { + _ = a.OrdOption(a.Given[int]()) +} diff --git a/test/typeparam/issue50485.go b/test/typeparam/issue50485.go new file mode 100644 index 00000000000..87b4ff46c1e --- /dev/null +++ b/test/typeparam/issue50485.go @@ -0,0 +1,7 @@ +// compiledir -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 ignored