Skip to content

Commit 90b1ed1

Browse files
committed
cmd/compile: get untyped constants working in generic functions
types2 will give us a constant with a type T, if an untyped constant is used with another operand of type T (in a provably correct way). When we substitute in the type args during stenciling, we now know the real type of the constant. We may then need to change the BasicLit.val to be the correct type (e.g. convert an int64Val constant to a floatVal constant). Otherwise, later parts of the compiler will be confused. Updated tests list.go and double.go with uses of untyped constants. Change-Id: I9966bbb0dea3a7de1c5a6420f8ad8af9ca84a33e Reviewed-on: https://go-review.googlesource.com/c/go/+/303089 Run-TryBot: Dan Scales <[email protected]> TryBot-Result: Go Bot <[email protected]> Trust: Dan Scales <[email protected]> Trust: Robert Griesemer <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent 095ba22 commit 90b1ed1

File tree

3 files changed

+101
-25
lines changed

3 files changed

+101
-25
lines changed

src/cmd/compile/internal/noder/stencil.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,24 @@ func (subst *subster) node(n ir.Node) ir.Node {
367367
}
368368
ir.EditChildren(m, edit)
369369

370-
if x.Op() == ir.OXDOT {
370+
switch x.Op() {
371+
case ir.OLITERAL:
372+
t := m.Type()
373+
if t != x.Type() {
374+
// types2 will give us a constant with a type T,
375+
// if an untyped constant is used with another
376+
// operand of type T (in a provably correct way).
377+
// When we substitute in the type args during
378+
// stenciling, we now know the real type of the
379+
// constant. We may then need to change the
380+
// BasicLit.val to be the correct type (e.g.
381+
// convert an int64Val constant to a floatVal
382+
// constant).
383+
m.SetType(types.UntypedInt) // use any untyped type for DefaultLit to work
384+
m = typecheck.DefaultLit(m, t)
385+
}
386+
387+
case ir.OXDOT:
371388
// A method value/call via a type param will have been left as an
372389
// OXDOT. When we see this during stenciling, finish the
373390
// typechecking, now that we have the instantiated receiver type.
@@ -377,8 +394,8 @@ func (subst *subster) node(n ir.Node) ir.Node {
377394
m.SetTypecheck(0)
378395
// m will transform to an OCALLPART
379396
typecheck.Expr(m)
380-
}
381-
if x.Op() == ir.OCALL {
397+
398+
case ir.OCALL:
382399
call := m.(*ir.CallExpr)
383400
if call.X.Op() == ir.OTYPE {
384401
// Do typechecking on a conversion, now that we
@@ -419,9 +436,8 @@ func (subst *subster) node(n ir.Node) ir.Node {
419436
// instantiation to be called.
420437
base.FatalfAt(call.Pos(), "Expecting OCALLPART or OTYPE or OFUNCINST or builtin with CALL")
421438
}
422-
}
423439

424-
if x.Op() == ir.OCLOSURE {
440+
case ir.OCLOSURE:
425441
x := x.(*ir.ClosureExpr)
426442
// Need to save/duplicate x.Func.Nname,
427443
// x.Func.Nname.Ntype, x.Func.Dcl, x.Func.ClosureVars, and

test/typeparam/double.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type Number interface {
1616
}
1717

1818
type MySlice []int
19+
type MyFloatSlice []float64
1920

2021
type _SliceOf[E any] interface {
2122
type []E
@@ -29,6 +30,15 @@ func _DoubleElems[S _SliceOf[E], E Number](s S) S {
2930
return r
3031
}
3132

33+
// Test use of untyped constant in an expression with a generically-typed parameter
34+
func _DoubleElems2[S _SliceOf[E], E Number](s S) S {
35+
r := make(S, len(s))
36+
for i, v := range s {
37+
r[i] = v * 2
38+
}
39+
return r
40+
}
41+
3242
func main() {
3343
arg := MySlice{1, 2, 3}
3444
want := MySlice{2, 4, 6}
@@ -47,4 +57,16 @@ func main() {
4757
if !reflect.DeepEqual(got, want) {
4858
panic(fmt.Sprintf("got %s, want %s", got, want))
4959
}
60+
61+
farg := MyFloatSlice{1.2, 2.0, 3.5}
62+
fwant := MyFloatSlice{2.4, 4.0, 7.0}
63+
fgot := _DoubleElems(farg)
64+
if !reflect.DeepEqual(fgot, fwant) {
65+
panic(fmt.Sprintf("got %s, want %s", fgot, fwant))
66+
}
67+
68+
fgot = _DoubleElems2(farg)
69+
if !reflect.DeepEqual(fgot, fwant) {
70+
panic(fmt.Sprintf("got %s, want %s", fgot, fwant))
71+
}
5072
}

test/typeparam/list.go

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ type Ordered interface {
1717
string
1818
}
1919

20-
// List is a linked list of ordered values of type T.
21-
type list[T Ordered] struct {
22-
next *list[T]
20+
// _List is a linked list of ordered values of type T.
21+
type _List[T Ordered] struct {
22+
next *_List[T]
2323
val T
2424
}
2525

26-
func (l *list[T]) largest() T {
26+
func (l *_List[T]) Largest() T {
2727
var max T
2828
for p := l; p != nil; p = p.next {
2929
if p.val > max {
@@ -33,33 +33,71 @@ func (l *list[T]) largest() T {
3333
return max
3434
}
3535

36+
type OrderedNum interface {
37+
type int, int8, int16, int32, int64,
38+
uint, uint8, uint16, uint32, uint64, uintptr,
39+
float32, float64
40+
}
41+
42+
// _ListNum is a linked _List of ordered numeric values of type T.
43+
type _ListNum[T OrderedNum] struct {
44+
next *_ListNum[T]
45+
val T
46+
}
47+
48+
const Clip = 5
49+
50+
// clippedLargest returns the largest in the list of OrderNums, but a max of 5.
51+
// Test use of untyped constant in an expression with a generically-typed parameter
52+
func (l *_ListNum[T]) ClippedLargest() T {
53+
var max T
54+
for p := l; p != nil; p = p.next {
55+
if p.val > max && p.val < Clip {
56+
max = p.val
57+
}
58+
}
59+
return max
60+
}
3661

3762
func main() {
38-
i3 := &list[int]{nil, 1}
39-
i2 := &list[int]{i3, 3}
40-
i1 := &list[int]{i2, 2}
41-
if got, want := i1.largest(), 3; got != want {
63+
i3 := &_List[int]{nil, 1}
64+
i2 := &_List[int]{i3, 3}
65+
i1 := &_List[int]{i2, 2}
66+
if got, want := i1.Largest(), 3; got != want {
4267
panic(fmt.Sprintf("got %d, want %d", got, want))
4368
}
4469

45-
b3 := &list[byte]{nil, byte(1)}
46-
b2 := &list[byte]{b3, byte(3)}
47-
b1 := &list[byte]{b2, byte(2)}
48-
if got, want := b1.largest(), byte(3); got != want {
70+
b3 := &_List[byte]{nil, byte(1)}
71+
b2 := &_List[byte]{b3, byte(3)}
72+
b1 := &_List[byte]{b2, byte(2)}
73+
if got, want := b1.Largest(), byte(3); got != want {
4974
panic(fmt.Sprintf("got %d, want %d", got, want))
5075
}
5176

52-
f3 := &list[float64]{nil, 13.5}
53-
f2 := &list[float64]{f3, 1.2}
54-
f1 := &list[float64]{f2, 4.5}
55-
if got, want := f1.largest(), 13.5; got != want {
77+
f3 := &_List[float64]{nil, 13.5}
78+
f2 := &_List[float64]{f3, 1.2}
79+
f1 := &_List[float64]{f2, 4.5}
80+
if got, want := f1.Largest(), 13.5; got != want {
5681
panic(fmt.Sprintf("got %f, want %f", got, want))
5782
}
5883

59-
s3 := &list[string]{nil, "dd"}
60-
s2 := &list[string]{s3, "aa"}
61-
s1 := &list[string]{s2, "bb"}
62-
if got, want := s1.largest(), "dd"; got != want {
84+
s3 := &_List[string]{nil, "dd"}
85+
s2 := &_List[string]{s3, "aa"}
86+
s1 := &_List[string]{s2, "bb"}
87+
if got, want := s1.Largest(), "dd"; got != want {
6388
panic(fmt.Sprintf("got %s, want %s", got, want))
6489
}
90+
91+
j3 := &_ListNum[int]{nil, 1}
92+
j2 := &_ListNum[int]{j3, 32}
93+
j1 := &_ListNum[int]{j2, 2}
94+
if got, want := j1.ClippedLargest(), 2; got != want {
95+
panic(fmt.Sprintf("got %d, want %d", got, want))
96+
}
97+
g3 := &_ListNum[float64]{nil, 13.5}
98+
g2 := &_ListNum[float64]{g3, 1.2}
99+
g1 := &_ListNum[float64]{g2, 4.5}
100+
if got, want := g1.ClippedLargest(), 4.5; got != want {
101+
panic(fmt.Sprintf("got %f, want %f", got, want))
102+
}
65103
}

0 commit comments

Comments
 (0)