Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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, "numIn", types.Typ[types.Uint8]),
types.NewVar(token.NoPos, nil, "numOut", types.Typ[types.Uint8]), // high bit = variadic
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
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() >= 0x100 {
c.addError(token.NoPos, fmt.Sprintf("too many function parameters for typecode (%d): %s", params.Len(), typ.String()))
}
if results.Len() >= 0x80 {
c.addError(token.NoPos, fmt.Sprintf("too many function results for typecode (%d): %s", results.Len(), typ.String()))
}
numOut := uint64(results.Len())
if typ.Variadic() {
numOut |= 0x80 // 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{
llvm.ConstInt(c.ctx.Int8Type(), uint64(params.Len()), false),
llvm.ConstInt(c.ctx.Int8Type(), numOut, false),
c.getTypeCode(types.NewPointer(typ)),
llvm.ConstArray(c.dataPtrType, inOut),
}
}
// Prepend metadata byte.
typeFields = append([]llvm.Value{
Expand Down
112 changes: 112 additions & 0 deletions src/internal/reflectlite/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,27 @@ type structField struct {
data unsafe.Pointer // various bits of information, packed in a byte array
}

// funcType is the type descriptor for function types. The numOut field uses
// bit 7 (funcTypeVariadic) to indicate whether the function is variadic; the
// remaining bits hold the number of output parameters. The variadic flag is
// stored in numOut rather than numIn because functions are more likely to have
// a large number of parameters than results, so leaving numIn with a full
// uint8 range is preferable. The inOut array contains numIn+numOut entries:
// input parameter types followed by output parameter types.
//
// The numIn and numOut fields are placed before ptrTo so that they fit in the
// padding between RawType (1 byte) and the pointer-aligned ptrTo field, which
// keeps ptrTo at the same offset as in elemType (used by pointerTo).
type funcType struct {
RawType
numIn uint8
numOut uint8
ptrTo *RawType
inOut [0]*RawType
}

const funcTypeVariadic = 0x80

// Method set, as emitted by the compiler.
type methodSet struct {
length uintptr
Expand Down Expand Up @@ -308,6 +329,8 @@ func pointerTo(t *RawType) *RawType {
panic("reflect: cannot make *****T type")
case Struct:
return (*structType)(unsafe.Pointer(t)).ptrTo
case Func:
return (*funcType)(unsafe.Pointer(t)).ptrTo
default:
return (*elemType)(unsafe.Pointer(t)).ptrTo
}
Expand Down Expand Up @@ -370,6 +393,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)
numOut := int(ft.numOut &^ funcTypeVariadic)
variadic := ft.numOut&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 +959,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)
}

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

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

func (t *RawType) In(i int) Type {
if t.Kind() != Func {
panic(TypeError{"In"})
}
ft := t.funcDescriptor()
n := int(ft.numIn)
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)
numOut := int(ft.numOut &^ funcTypeVariadic)
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