mirror of https://github.com/golang/go.git
go/types: first cut at supporting real/imag/complex with type parameters
Change-Id: Ic0dba1a5e758942a03d114df10cb5536cf432985
This commit is contained in:
parent
e4a39e916f
commit
660f7c38d1
|
|
@ -265,7 +265,21 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
}
|
||||
|
||||
// the argument types must be of floating-point type
|
||||
if !isFloat(x.typ) {
|
||||
f := func(x Type) Type {
|
||||
if t, _ := x.Underlying().(*Basic); t != nil {
|
||||
switch t.kind {
|
||||
case Float32:
|
||||
return Typ[Complex64]
|
||||
case Float64:
|
||||
return Typ[Complex128]
|
||||
case UntypedFloat:
|
||||
return Typ[UntypedComplex]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
resTyp := applyTypeFunc(f, x.typ)
|
||||
if resTyp == nil {
|
||||
check.invalidArg(x.pos(), "arguments have type %s, expected floating-point", x.typ)
|
||||
return
|
||||
}
|
||||
|
|
@ -277,20 +291,6 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
x.mode = value
|
||||
}
|
||||
|
||||
// determine result type
|
||||
var res BasicKind
|
||||
switch x.typ.Underlying().(*Basic).kind {
|
||||
case Float32:
|
||||
res = Complex64
|
||||
case Float64:
|
||||
res = Complex128
|
||||
case UntypedFloat:
|
||||
res = UntypedComplex
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
resTyp := Typ[res]
|
||||
|
||||
if check.Types != nil && x.mode != constant_ {
|
||||
check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ, x.typ))
|
||||
}
|
||||
|
|
@ -383,7 +383,21 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
}
|
||||
|
||||
// the argument must be of complex type
|
||||
if !isComplex(x.typ) {
|
||||
f := func(x Type) Type {
|
||||
if t, _ := x.Underlying().(*Basic); t != nil {
|
||||
switch t.kind {
|
||||
case Complex64:
|
||||
return Typ[Float32]
|
||||
case Complex128:
|
||||
return Typ[Float64]
|
||||
case UntypedComplex:
|
||||
return Typ[UntypedFloat]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
resTyp := applyTypeFunc(f, x.typ)
|
||||
if resTyp == nil {
|
||||
check.invalidArg(x.pos(), "argument has type %s, expected complex type", x.typ)
|
||||
return
|
||||
}
|
||||
|
|
@ -399,20 +413,6 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
x.mode = value
|
||||
}
|
||||
|
||||
// determine result type
|
||||
var res BasicKind
|
||||
switch x.typ.Underlying().(*Basic).kind {
|
||||
case Complex64:
|
||||
res = Float32
|
||||
case Complex128:
|
||||
res = Float64
|
||||
case UntypedComplex:
|
||||
res = UntypedFloat
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
resTyp := Typ[res]
|
||||
|
||||
if check.Types != nil && x.mode != constant_ {
|
||||
check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ))
|
||||
}
|
||||
|
|
@ -647,6 +647,51 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
return true
|
||||
}
|
||||
|
||||
// applyTypeFunc applies f to x. If x is a type parameter,
|
||||
// the result is an anonymous type parameter constrained by
|
||||
// an unnamed contract. The type constraints of that contract
|
||||
// are computed by applying f to each of the contract type
|
||||
// constraints of x. If any of these applications of f return
|
||||
// nil, applyTypeFunc returns nil.
|
||||
// If x is not a type parameter, the result is f(x).
|
||||
func applyTypeFunc(f func(Type) Type, x Type) Type {
|
||||
if tp, _ := x.Underlying().(*TypeParam); tp != nil {
|
||||
// Test if t satisfies the requirements for the argument
|
||||
// type and collect possible result types at the same time.
|
||||
var resTypes []Type
|
||||
if !tp.Interface().is(func(x Type) bool {
|
||||
if r := f(x); r != nil {
|
||||
resTypes = append(resTypes, r)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(gri) Would it be ok to return just the one type
|
||||
// if len(resType) == 1? What about top-level
|
||||
// uses of real() where the result is used to
|
||||
// define type and initialize a variable?
|
||||
|
||||
// construct a suitable new type parameter
|
||||
tpar := NewTypeName(token.NoPos, nil /* = Universe pkg */, "<type parameter literal>", nil)
|
||||
resTyp := NewTypeParam(tpar, 0, nil) // assigns type to tpar as a side-effect
|
||||
|
||||
// construct a corresponding interface and contract
|
||||
iface := &Interface{allMethods: markComplete, types: resTypes}
|
||||
contr := &Contract{
|
||||
TParams: []*TypeName{tpar},
|
||||
IFaces: map[*TypeName]*Interface{tpar: iface},
|
||||
}
|
||||
resTyp.contr = contr
|
||||
|
||||
return resTyp
|
||||
}
|
||||
|
||||
return f(x)
|
||||
}
|
||||
|
||||
// makeSig makes a signature for the given argument and result types.
|
||||
// Default types are used for untyped arguments, and res may be nil.
|
||||
func makeSig(res Type, args ...Type) *Signature {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file shows examples of contract declarations.
|
||||
// They are not type-checked at the moment.
|
||||
|
||||
package p
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ func is(typ Type, what BasicInfo) bool {
|
|||
case *Basic:
|
||||
return t.info&what != 0
|
||||
case *TypeParam:
|
||||
return t.contr.ifaceAt(t.index).is(func(typ Type) bool { return is(typ, what) })
|
||||
return t.Interface().is(func(typ Type) bool { return is(typ, what) })
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,4 +143,21 @@ func sum(type T Integer)(data []T) T {
|
|||
s += x
|
||||
}
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
// Contracts and built-ins whose result type depends on the argument type.
|
||||
|
||||
contract FloatComplex(F, C) {
|
||||
F float32, float64
|
||||
C complex64, complex128
|
||||
}
|
||||
|
||||
func _(type F, C FloatComplex)(c C) {
|
||||
_ = real(c)
|
||||
_ = imag(c)
|
||||
re := real(c)
|
||||
im := imag(c)
|
||||
var _ F = re // TODO(gri) why does this work? (no type conversion needed?)
|
||||
var _ F = im // TODO(gri) why does this work? (no type conversion needed?)
|
||||
c = C(complex(re, im))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -521,6 +521,9 @@ type Parameterized struct {
|
|||
}
|
||||
|
||||
// A Contract represents a contract.
|
||||
// TODO(gri) Do we need the ability to represent unnamed type parameter literals?
|
||||
// For instance, when creating (result) type parameters out of whole cloth
|
||||
// say for the result type of real/imag(x) where x is of a parameterized type.
|
||||
type Contract struct {
|
||||
TParams []*TypeName
|
||||
IFaces map[*TypeName]*Interface
|
||||
|
|
@ -559,6 +562,7 @@ func NewTypeParam(obj *TypeName, index int, contr *Contract) *TypeParam {
|
|||
// Interface returns the type parameter's interface as
|
||||
// specified via its contract. If there is no contract,
|
||||
// the result is the empty interface.
|
||||
// TODO(gri) should this be Underlying instead?
|
||||
func (t *TypeParam) Interface() *Interface {
|
||||
return t.contr.ifaceAt(t.index)
|
||||
}
|
||||
|
|
@ -578,7 +582,7 @@ func (c *Chan) Underlying() Type { return c }
|
|||
func (t *Named) Underlying() Type { return t.underlying }
|
||||
func (p *Parameterized) Underlying() Type { return p.tname.typ.Underlying() }
|
||||
func (c *Contract) Underlying() Type { return c }
|
||||
func (t *TypeParam) Underlying() Type { return t }
|
||||
func (t *TypeParam) Underlying() Type { return t } // TODO(gri) should this return t.Interface() instead?
|
||||
|
||||
func (b *Basic) String() string { return TypeString(b, nil) }
|
||||
func (a *Array) String() string { return TypeString(a, nil) }
|
||||
|
|
|
|||
Loading…
Reference in New Issue