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
13 changes: 13 additions & 0 deletions cl/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,10 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
return
}
}
if isBlankFieldStore(va) {
_ = p.compileValue(b, v.Val)
return
}
if p.rewrites != nil {
if g, ok := va.(*ssa.Global); ok {
if _, ok := p.rewriteInitStore(v, g); ok {
Expand Down Expand Up @@ -1251,6 +1255,15 @@ func (p *context) compileValue(b llssa.Builder, v ssa.Value) llssa.Expr {
panic(fmt.Sprintf("compileValue: unknown value - %T\n", v))
}

func isBlankFieldStore(addr ssa.Value) bool {
field, ok := addr.(*ssa.FieldAddr)
if !ok {
return false
}
_, st, ok := fieldAddrStruct(field)
return ok && st.Field(field.Field).Name() == "_"
}

const rangeOverFuncYieldSynthetic = "range-over-func yield"

func (p *context) rangeFuncCallNeedsDeferDrain(call *ssa.CallCommon) bool {
Expand Down
3 changes: 3 additions & 0 deletions runtime/internal/lib/internal/reflectlite/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ func (t rtype) PkgPath() string {
if t.TFlag&abi.TFlagNamed == 0 {
return ""
}
if t.Kind() == abi.UnsafePointer {
return "unsafe"
}
ut := t.uncommon()
if ut == nil {
return ""
Expand Down
46 changes: 32 additions & 14 deletions runtime/internal/lib/reflect/makefunc.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
for i := 0; i < fd.nin; i++ {
ins[i] = ffiToValue(ffi.Index(args, uintptr(i+1)), fd.ftyp.In[i])
}
fd.fn(ins)
validateMakeFuncResults(fd.fn(ins), fd.ftyp)
}, unsafe.Pointer(&funcData{ftyp: ftyp, fn: fn, nin: len(ftyp.In)}))
case 1:
err = closure.Bind(sig, func(cif *ffi.Signature, ret unsafe.Pointer, args *unsafe.Pointer, userdata unsafe.Pointer) {
Expand All @@ -66,12 +66,8 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
for i := 0; i < fd.nin; i++ {
ins[i] = ffiToValue(ffi.Index(args, uintptr(i+1)), fd.ftyp.In[i])
}
out := fd.fn(ins)
if fd.ftyp.Out[0].IfaceIndir() {
c.Memmove(ret, out[0].ptr, fd.ftyp.Out[0].Size_)
} else {
*(*unsafe.Pointer)(ret) = unsafe.Pointer(out[0].ptr)
}
out := validateMakeFuncResults(fd.fn(ins), fd.ftyp)
storeMakeFuncResult(ret, out[0], fd.ftyp.Out[0])
}, unsafe.Pointer(&funcData{ftyp: ftyp, fn: fn, nin: len(ftyp.In)}))
default:
err = closure.Bind(sig, func(cif *ffi.Signature, ret unsafe.Pointer, args *unsafe.Pointer, userdata unsafe.Pointer) {
Expand All @@ -80,15 +76,13 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
for i := 0; i < fd.nin; i++ {
ins[i] = ffiToValue(ffi.Index(args, uintptr(i+1)), fd.ftyp.In[i])
}
outs := fd.fn(ins)
outs := validateMakeFuncResults(fd.fn(ins), fd.ftyp)
var offset uintptr = 0
alignment := uintptr(cif.RType.Alignment)
for i, out := range outs {
if fd.ftyp.Out[i].IfaceIndir() {
c.Memmove(add(ret, offset, ""), out.ptr, fd.ftyp.Out[i].Size_)
} else {
*(*unsafe.Pointer)(add(ret, offset, "")) = unsafe.Pointer(out.ptr)
}
offset += fd.ftyp.Out[i].Size_
typ := fd.ftyp.Out[i]
storeMakeFuncResult(add(ret, offset, ""), out, typ)
offset += (typ.Size_ + alignment - 1) &^ (alignment - 1)
}
}, unsafe.Pointer(&funcData{ftyp: ftyp, fn: fn, nin: len(ftyp.In)}))
}
Expand All @@ -103,6 +97,30 @@ func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value {
return Value{styp, unsafe.Pointer(fv), flagIndir | flag(Func)}
}

func validateMakeFuncResults(out []Value, ftyp *funcType) []Value {
if len(out) != len(ftyp.Out) {
panic("reflect: wrong return count from function created by MakeFunc")
}
for i, typ := range ftyp.Out {
v := out[i]
if v.typ() == nil {
panic("reflect: function created by MakeFunc returned zero Value")
}
if v.flag&flagRO != 0 {
panic("reflect: function created by MakeFunc returned value obtained from unexported field")
}
out[i] = v.assignTo("reflect: function created by MakeFunc", typ, nil)
}
return out
}

func storeMakeFuncResult(ret unsafe.Pointer, v Value, typ *abi.Type) {
if typ.Size_ == 0 {
return
}
c.Memmove(ret, toFFIArg(v, typ), typ.Size_)
}

func ffiToValue(ptr unsafe.Pointer, typ *abi.Type) (v Value) {
kind := typ.Kind()
v.typ_ = typ
Expand Down
3 changes: 3 additions & 0 deletions runtime/internal/lib/reflect/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,9 @@ func (t *rtype) PkgPath() string {
if t.t.TFlag&abi.TFlagNamed == 0 {
return ""
}
if t.t.Kind() == abi.UnsafePointer {
return "unsafe"
}
ut := t.uncommon()
if ut == nil {
return ""
Expand Down
3 changes: 3 additions & 0 deletions runtime/internal/lib/reflect/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -2284,6 +2284,9 @@ func toFFIArg(v Value, typ *abi.Type) unsafe.Pointer {
case abi.Chan:
return unsafe.Pointer(&v.ptr)
case abi.Func:
if v.flag&flagIndir != 0 {
return v.ptr
}
return unsafe.Pointer(&v.ptr)
case abi.Interface:
i := v.Interface()
Expand Down
14 changes: 12 additions & 2 deletions ssa/abitype.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ func (b Builder) abiExtendedFields(t types.Type, name string) (fields []llvm.Val
for i := 0; i < n; i++ {
if f := t.Field(i); !f.Exported() {
if pkg := f.Pkg(); pkg != nil {
pkgPath = pkg.Path()
pkgPath = reflectPkgPath(pkg)
break
}
}
Expand Down Expand Up @@ -351,11 +351,21 @@ retry:
goto retry
case *types.Named:
pkg := typ.Obj().Pkg()
return pkg, abi.PathOf(pkg)
return pkg, reflectPkgPath(pkg)
}
return nil, b.Pkg.Path()
}

func reflectPkgPath(pkg *types.Package) string {
if pkg == nil {
return ""
}
if pkg.Path() == "command-line-arguments" && pkg.Name() != "" {
return pkg.Name()
}
return abi.PathOf(pkg)
}

func (b Builder) abiUncommonMethodSet(t types.Type) (mset *types.MethodSet, ok bool) {
prog := b.Prog
switch t := types.Unalias(t).(type) {
Expand Down
107 changes: 107 additions & 0 deletions test/go/reflect_type_metadata_makefunc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package gotest

import (
"os"
"path/filepath"
"testing"
)

const reflectTypeMetadataMakeFuncProbe = `package main

import (
"fmt"
"reflect"
"strings"
"unsafe"
)

type foo struct {
bar int
}

var blankFieldEvalCount int

func blankFieldValue() int {
blankFieldEvalCount++
return 2
}

func boolLoop(next func() bool) bool {
for b := next(); b; b = next() {
return true
}
return false
}

func errString(v any) string {
if s, ok := v.(interface{ Error() string }); ok {
return s.Error()
}
return reflect.ValueOf(v).String()
}

func expectReflectMakeFuncPanic() {
defer func() {
err := recover()
if err == nil {
panic("MakeFunc call did not panic")
}
if got := errString(err); !strings.HasPrefix(got, "reflect:") {
panic("MakeFunc panic missing reflect prefix: " + got)
}
}()

fn := reflect.MakeFunc(reflect.TypeOf(func() error { return nil }), func([]reflect.Value) []reflect.Value {
var out [1]reflect.Value
return out[:]
}).Interface().(func() error)
_ = fn()
}

func main() {
if got := reflect.ValueOf(foo{}).Type().Field(0).PkgPath; got != "main" {
panic(fmt.Sprintf("field PkgPath = %q, want main", got))
}
if got := reflect.TypeOf(unsafe.Pointer(nil)).PkgPath(); got != "unsafe" {
panic(fmt.Sprintf("unsafe.Pointer PkgPath = %q, want unsafe", got))
}

x := struct{ a, _, c int }{1, blankFieldValue(), 3}
if blankFieldEvalCount != 1 {
panic(fmt.Sprintf("blank field initializer evaluated %d times, want 1", blankFieldEvalCount))
}
if got := reflect.ValueOf(x).Field(1).Int(); got != 0 {
panic(fmt.Sprintf("blank field reflect value = %d, want 0", got))
}

expectReflectMakeFuncPanic()

nextFalse := reflect.MakeFunc(reflect.TypeOf((func() bool)(nil)), func([]reflect.Value) []reflect.Value {
return []reflect.Value{reflect.ValueOf(false)}
})
if got := reflect.ValueOf(boolLoop).Call([]reflect.Value{nextFalse})[0].Bool(); got {
panic(fmt.Sprintf("false MakeFunc loop result = %v, want false", got))
}

nextTrue := reflect.MakeFunc(reflect.TypeOf((func() bool)(nil)), func([]reflect.Value) []reflect.Value {
return []reflect.Value{reflect.ValueOf(true)}
})
if got := reflect.ValueOf(boolLoop).Call([]reflect.Value{nextTrue})[0].Bool(); !got {
panic(fmt.Sprintf("true MakeFunc loop result = %v, want true", got))
}
}
`

func TestReflectTypeMetadataMakeFuncProbe(t *testing.T) {
dir := t.TempDir()
mainFile := filepath.Join(dir, "main.go")
if err := os.WriteFile(mainFile, []byte(reflectTypeMetadataMakeFuncProbe), 0644); err != nil {
t.Fatal(err)
}

runGoCmd(t, dir, "run", mainFile)

root := findLLGoRoot(t)
t.Setenv("LLGO_ROOT", root)
runGoCmd(t, root, "run", "./cmd/llgo", "run", mainFile)
}
Loading