Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions compiler/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,14 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
types.NewVar(token.NoPos, nil, "methods", methodSetType),
)
case *types.Signature:
numIn := typ.Params().Len()
numOut := typ.Results().Len()
typeFieldTypes = append(typeFieldTypes,
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "numIn", types.Typ[types.Uint16]), // high bit = variadic
types.NewVar(token.NoPos, nil, "numOut", types.Typ[types.Uint16]),
types.NewVar(token.NoPos, nil, "inOut", types.NewArray(types.Typ[types.UnsafePointer], int64(numIn+numOut))),
)
// TODO: signature params and return values
}
if hasMethodSet {
// This method set is appended at the start of the struct. It is
Expand Down Expand Up @@ -477,8 +481,31 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
methodSetValue,
}
case *types.Signature:
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
// TODO: params, return values, etc
params := typ.Params()
results := typ.Results()
if params.Len() >= 0x8000 {
c.addError(token.NoPos, fmt.Sprintf("too many function parameters for typecode (%d): %s", params.Len(), typ.String()))
}
if results.Len() >= 0x10000 {
c.addError(token.NoPos, fmt.Sprintf("too many function results for typecode (%d): %s", results.Len(), typ.String()))
}
numIn := uint64(params.Len())
if typ.Variadic() {
numIn |= 0x8000 // variadic flag in high bit
}
inOut := make([]llvm.Value, 0, params.Len()+results.Len())
for i := 0; i < params.Len(); i++ {
inOut = append(inOut, c.getTypeCode(params.At(i).Type()))
}
for i := 0; i < results.Len(); i++ {
inOut = append(inOut, c.getTypeCode(results.At(i).Type()))
}
typeFields = []llvm.Value{
c.getTypeCode(types.NewPointer(typ)),
llvm.ConstInt(c.ctx.Int16Type(), numIn, false),
llvm.ConstInt(c.ctx.Int16Type(), uint64(results.Len()), false),
llvm.ConstArray(c.dataPtrType, inOut),
}
}
// Prepend metadata byte.
typeFields = append([]llvm.Value{
Expand Down
104 changes: 104 additions & 0 deletions src/internal/reflectlite/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,21 @@ type structField struct {
data unsafe.Pointer // various bits of information, packed in a byte array
}

// funcType is the type descriptor for function types. The numIn field uses
// bit 15 (funcTypeVariadic) to indicate whether the function is variadic; the
// remaining bits hold the number of input parameters. The inOut array contains
// numIn+numOut entries: input parameter types followed by output parameter
// types.
type funcType struct {
RawType
ptrTo *RawType
numIn uint16
numOut uint16
Comment thread
jakebailey marked this conversation as resolved.
Outdated
inOut [0]*RawType
}

const funcTypeVariadic = 0x8000

// Method set, as emitted by the compiler.
type methodSet struct {
length uintptr
Expand Down Expand Up @@ -370,6 +385,41 @@ func (t *RawType) String() string {
case Interface:
// TODO(dgryski): Needs actual method set info
return "interface {}"
case Func:
ft := t.funcDescriptor()
numIn := int(ft.numIn &^ funcTypeVariadic)
numOut := int(ft.numOut)
variadic := ft.numIn&funcTypeVariadic != 0
arr := (*[1 << 16]*RawType)(unsafe.Pointer(&ft.inOut))
s := "func("
for i := 0; i < numIn; i++ {
if i > 0 {
s += ", "
}
if variadic && i == numIn-1 {
// final variadic parameter is stored as []T but printed as ...T
s += "..." + arr[i].elem().String()
} else {
s += arr[i].String()
}
}
s += ")"
switch numOut {
case 0:
// no result
case 1:
s += " " + arr[numIn].String()
default:
s += " ("
for i := 0; i < numOut; i++ {
if i > 0 {
s += ", "
}
s += arr[numIn+i].String()
}
s += ")"
}
return s
default:
return t.Kind().String()
}
Expand Down Expand Up @@ -901,6 +951,60 @@ func (t *RawType) ChanDir() ChanDir {
return ChanDir(dir)
}

// funcDescriptor returns the funcType descriptor for t. If t is a named func
// type, it walks through to the underlying signature.
func (t *RawType) funcDescriptor() *funcType {
return (*funcType)(unsafe.Pointer(t.underlying()))
}

func (t *RawType) NumIn() int {
if t.Kind() != Func {
panic(TypeError{"NumIn"})
}
return int(t.funcDescriptor().numIn &^ funcTypeVariadic)
}

func (t *RawType) NumOut() int {
if t.Kind() != Func {
panic(TypeError{"NumOut"})
}
return int(t.funcDescriptor().numOut)
}

func (t *RawType) IsVariadic() bool {
if t.Kind() != Func {
panic(TypeError{"IsVariadic"})
}
return t.funcDescriptor().numIn&funcTypeVariadic != 0
}

func (t *RawType) In(i int) Type {
if t.Kind() != Func {
panic(TypeError{"In"})
}
ft := t.funcDescriptor()
n := int(ft.numIn &^ funcTypeVariadic)
if i < 0 || i >= n {
panic("reflect: Type.In: index out of range")
}
arr := (*[1 << 16]*RawType)(unsafe.Pointer(&ft.inOut))
return arr[i]
}

func (t *RawType) Out(i int) Type {
if t.Kind() != Func {
panic(TypeError{"Out"})
}
ft := t.funcDescriptor()
numIn := int(ft.numIn &^ funcTypeVariadic)
numOut := int(ft.numOut)
if i < 0 || i >= numOut {
panic("reflect: Type.Out: index out of range")
}
arr := (*[1 << 16]*RawType)(unsafe.Pointer(&ft.inOut))
return arr[numIn+i]
}

func (t *RawType) NumMethod() int {

if t.isNamed() {
Expand Down
10 changes: 5 additions & 5 deletions src/reflect/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,11 +434,11 @@ func (t *rawType) Implements(u Type) bool {
}

func (t *rawType) In(i int) Type {
panic("unimplemented: (reflect.Type).In()")
return toType(t.RawType.In(i).(*reflectlite.RawType))
}

func (t *rawType) IsVariadic() bool {
panic("unimplemented: (reflect.Type).IsVariadic()")
return t.RawType.IsVariadic()
}

func (t *rawType) Key() Type {
Expand All @@ -454,15 +454,15 @@ func (t *rawType) MethodByName(name string) (Method, bool) {
}

func (t *rawType) NumIn() int {
panic("unimplemented: (reflect.Type).NumIn()")
return t.RawType.NumIn()
}

func (t *rawType) NumOut() int {
panic("unimplemented: (reflect.Type).NumOut()")
return t.RawType.NumOut()
}

func (t *rawType) Out(i int) Type {
panic("unimplemented: (reflect.Type).Out()")
return toType(t.RawType.Out(i).(*reflectlite.RawType))
}

// A StructField describes a single field in a struct.
Expand Down
28 changes: 28 additions & 0 deletions testdata/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,34 @@ func main() {
}
}
}

// Test reflect on function types (issue #4458).
println("\nfunc type reflection")
for _, fn := range []interface{}{
func() {},
func(int) {},
func(int) int { return 0 },
func(int, string) (bool, error) { return false, nil },
func(...int) {},
func(string, ...int) error { return nil },
} {
t := reflect.TypeOf(fn)
print(t.String(), " NumIn=", t.NumIn(), " NumOut=", t.NumOut(), " Variadic=", t.IsVariadic())
for i := 0; i < t.NumIn(); i++ {
print(" In(", i, ")=", t.In(i).String())
}
for i := 0; i < t.NumOut(); i++ {
print(" Out(", i, ")=", t.Out(i).String())
}
println()
}
// Named func type still resolves to underlying signature for In/Out.
{
type namedFunc func(int) string
var f namedFunc
t := reflect.TypeOf(f)
println(t.String(), "NumIn=", t.NumIn(), "In(0)=", t.In(0).String(), "NumOut=", t.NumOut(), "Out(0)=", t.Out(0).String())
}
}

func emptyFunc() {
Expand Down
9 changes: 9 additions & 0 deletions testdata/reflect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -512,3 +512,12 @@ blue gopher
v.Interface() method
kind: interface
int 5

func type reflection
func() NumIn=0 NumOut=0 Variadic=false
func(int) NumIn=1 NumOut=0 Variadic=false In(0)=int
func(int) int NumIn=1 NumOut=1 Variadic=false In(0)=int Out(0)=int
func(int, string) (bool, error) NumIn=2 NumOut=2 Variadic=false In(0)=int In(1)=string Out(0)=bool Out(1)=error
func(...int) NumIn=1 NumOut=0 Variadic=true In(0)=[]int
func(string, ...int) error NumIn=2 NumOut=1 Variadic=true In(0)=string In(1)=[]int Out(0)=error
main.namedFunc NumIn= 1 In(0)= int NumOut= 1 Out(0)= string
Loading