Skip to content
Merged
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
2 changes: 1 addition & 1 deletion builtin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ func TestTypeEx(t *testing.T) {
subst,
&TyOverloadFunc{},
&TyOverloadMethod{},
&TyStaticMethod{},
&TyStaticMember{},
&TyTemplateRecvMethod{},
&TyInstruction{},
&TyOverloadNamed{Obj: types.NewTypeName(0, pkg.Types, "bar", tyInt)},
Expand Down
55 changes: 54 additions & 1 deletion codebuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,51 @@ func aliasNameOf(name string, flag MemberFlag) (string, MemberFlag) {
return "", flag
}

func staticMemberRecv(typ types.Type) *types.Named {
typ = types.Unalias(typ)
if ptr, ok := typ.(*types.Pointer); ok {
typ = types.Unalias(ptr.Elem())
}
if named, ok := typ.(*types.Named); ok {
return named.Origin()
}
return nil
}

func (p *CodeBuilder) staticMember(typ types.Type, name string, flag MemberFlag, src ast.Node) (MemberKind, error) {
named := staticMemberRecv(typ)
if named == nil {
return MemberInvalid, nil
}
p.ensureLoaded(named)
method, obj := lookupStaticMember(named, name)
if obj == nil {
return MemberInvalid, nil
}
if !p.allowAccess(method.Pkg(), method.Name()) {
return MemberInvalid, nil
}
if _, ok := obj.(*types.Func); ok {
return MemberInvalid, nil
}
if flag == MemberFlagRef {
if v, ok := obj.(*types.Var); ok {
p.stk.Ret(1, &internal.Elem{
Val: toObjectExpr(p.pkg, v), Type: &refType{typ: v.Type()}, Src: src,
})
} else {
code, pos, end := p.loadExpr(src)
return MemberInvalid, p.newCodeError(pos, end, fmt.Sprintf("%s is not a variable", code))
}
} else {
p.stk.Ret(1, toObject(p.pkg, obj, src))
}
if p.rec != nil {
p.rec.Member(src, obj)
}
return MemberField, nil
}

// Member access member by its name.
// src should point to the full source node `x.sel`
func (p *CodeBuilder) Member(name string, lhs int, flag MemberFlag, src ...ast.Node) (kind MemberKind, err error) {
Expand All @@ -1141,11 +1186,16 @@ func (p *CodeBuilder) Member(name string, lhs int, flag MemberFlag, src ...ast.N
case p.pkg.utBigInt, p.pkg.utBigRat, p.pkg.utBigFlt:
at = DefaultConv(p.pkg, arg.Type, arg)
}
var t, isType = at.(*TypeType)
if isType {
if kind, err = p.staticMember(t.Type(), name, flag, srcExpr); kind != MemberInvalid || err != nil {
return
}
}
if flag == MemberFlagRef {
kind = p.refMember(at, name, arg.Val, srcExpr, nil)
} else {
var aliasName string
var t, isType = at.(*TypeType)
if isType { // (T).method or (*T).method
at = t.Type()
flag = memberFlagMethodToFunc
Expand Down Expand Up @@ -1344,6 +1394,9 @@ func (p *CodeBuilder) method(
var exact bool
for i, n := 0, o.NumMethods(); i < n; i++ {
method := o.Method(i)
if isStaticValueMember(method) {
continue
}
v := method.Name()
if !p.allowAccess(method.Pkg(), v) {
continue
Expand Down
50 changes: 50 additions & 0 deletions codebuild_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package gogen

import (
"go/constant"
"go/token"
"go/types"
"testing"
Expand Down Expand Up @@ -96,3 +97,52 @@ func TestFindMember(t *testing.T) {
t.Fatalf("expected MemberMethod (1), got %v", kind)
}
}

func TestStaticMemberInternalHelpers(t *testing.T) {
Comment thread
xushiwei marked this conversation as resolved.
pkg := NewPackage("", "foo", nil)
fn := types.NewFunc(token.NoPos, pkg.Types, "XGos_foo_name", types.NewSignatureType(nil, nil, nil, nil, nil, false))
sm := &TyStaticMember{Member: fn}
if got := sm.Obj(); got != fn {
t.Fatalf("TyStaticMember.Obj() = %v, want %v", got, fn)
}
if got := sm.Underlying(); got != sm {
t.Fatalf("TyStaticMember.Underlying() = %v, want self", got)
}
if got := sm.String(); got != "TyStaticMember" {
t.Fatalf("TyStaticMember.String() = %q, want %q", got, "TyStaticMember")
}
method := newMethodEx(pkg.NewType("foo").InitType(pkg, types.Typ[types.Int]), token.NoPos, pkg.Types, "name", sm)
if got, ok := staticMemberObj(method); !ok || got != fn {
t.Fatalf("staticMemberObj(TyStaticMember) = %v, %v, want %v, true", got, ok, fn)
}
sm.funcEx()

var trm TyTemplateRecvMethod
trm.funcEx()
}

func TestStaticMemberLoadsDelayedNamedType(t *testing.T) {
var loaded bool
pkg := NewPackage("", "foo", &Config{
LoadNamed: func(at *Package, typ *types.Named) {
loaded = true
typ.SetUnderlying(types.Typ[types.Int])
obj := types.NewConst(
token.NoPos, at.Types, "XGos_T_name", types.Typ[types.String], constant.MakeString("xgo"),
)
NewStaticMember(typ, token.NoPos, at.Types, "name", obj)
},
})
delayed := types.NewNamed(types.NewTypeName(token.NoPos, pkg.Types, "T", nil), nil, nil)
cb := pkg.CB().Typ(delayed)
kind, err := cb.Member("name", 0, MemberFlagVal)
if err != nil {
t.Fatal("Member:", err)
}
if kind != MemberField {
t.Fatalf("Member(name) = %v, want %v", kind, MemberField)
}
if !loaded {
t.Fatal("LoadNamed was not called")
}
}
62 changes: 62 additions & 0 deletions error_msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,68 @@ func TestErrMemberRef(t *testing.T) {
})
}

func TestErrStaticMember(t *testing.T) {
codeErrorTest(t,
`./foo.gop:1:5: int.name undefined (type int has no method name)`,
func(pkg *gogen.Package) {
pkg.NewFunc(nil, "main", nil, nil, false).BodyStart(pkg).
Typ(types.Typ[types.Int]).MemberVal("name", 0, source("int.name", 1, 5)).
EndStmt().
End()
})
codeErrorTest(t,
`./foo.gop:1:5: T.name is not a variable`,
func(pkg *gogen.Package) {
scope := pkg.Types.Scope()
typ := pkg.NewType("T").InitType(pkg, types.Typ[types.Int])
pkg.NewConstStart(scope, token.NoPos, nil, "XGos_T_name").
Val("xgo").EndInit(1)
gogen.NewStaticMember(typ, token.NoPos, pkg.Types, "name", scope.Lookup("XGos_T_name"))
pkg.NewFunc(nil, "main", nil, nil, false).BodyStart(pkg).
Typ(typ).MemberRef("name", source("T.name", 1, 5)).
EndStmt().
End()
})
codeErrorTest(t,
`./foo.gop:1:7: v.name undefined (type T has no field or method name)`,
func(pkg *gogen.Package) {
scope := pkg.Types.Scope()
typ := pkg.NewType("T").InitType(pkg, types.Typ[types.Int])
pkg.NewConstStart(scope, token.NoPos, nil, "XGos_T_name").
Val("xgo").EndInit(1)
gogen.NewStaticMember(typ, token.NoPos, pkg.Types, "name", scope.Lookup("XGos_T_name"))
pkg.NewFunc(nil, "main", nil, nil, false).BodyStart(pkg).
NewVar(typ, "v").
VarVal("v").MemberVal("name", 0, source("v.name", 1, 7)).
EndStmt().
End()
})
const src = `package foo

const XGoPackage = true

type T int

const XGos_T_name = "xgo"
`
gt := newGoxTest()
_, err := gt.LoadGoPackage("foo", "foo.go", src)
if err != nil {
t.Fatal(err)
}
pkg := gt.NewPackage("", "main")
foo := pkg.Import("foo")
typ := foo.Ref("T").Type()
codeErrorTestEx(t, pkg,
`./foo.gop:1:5: T.name undefined (type foo.T has no method name)`,
func(pkg *gogen.Package) {
pkg.NewFunc(nil, "main", nil, nil, false).BodyStart(pkg).
Typ(typ).MemberVal("name", 0, source("T.name", 1, 5)).
EndStmt().
End()
})
}

func TestErrUnsafe(t *testing.T) {
codeErrorTest(t,
`./foo.gop:6:15: missing argument to function call: unsafe.Sizeof()`,
Expand Down
47 changes: 39 additions & 8 deletions func_ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,17 +224,48 @@ func (p *TyTypeAsParams) funcEx() {}

// ----------------------------------------------------------------------------

type TyStaticMethod struct {
Func types.Object
type TyStaticMember struct {
Member types.Object
}

func (p *TyStaticMethod) Obj() types.Object { return p.Func }
func (p *TyStaticMethod) Underlying() types.Type { return p }
func (p *TyStaticMethod) String() string { return "TyStaticMethod" }
func (p *TyStaticMethod) funcEx() {}
func (p *TyStaticMember) Obj() types.Object { return p.Member }
func (p *TyStaticMember) Underlying() types.Type { return p }
func (p *TyStaticMember) String() string { return "TyStaticMember" }
func (p *TyStaticMember) funcEx() {}

func staticMemberObj(method *types.Func) (types.Object, bool) {
if sig, ok := method.Type().(*types.Signature); ok {
if ext, ok := CheckFuncEx(sig); ok {
if member, ok := ext.(*TyStaticMember); ok {
return member.Obj(), true
}
}
}
return nil, false
}

func NewStaticMethod(typ *types.Named, pos token.Pos, pkg *types.Package, name string, fn types.Object) *types.Func {
return newMethodEx(typ, pos, pkg, name, &TyStaticMethod{fn})
func isStaticValueMember(method *types.Func) bool {
if obj, ok := staticMemberObj(method); ok {
_, isFunc := obj.(*types.Func)
return !isFunc
}
return false
}

// NewStaticMember associates a package-level object with a named type.
// Function objects resolve as static methods; other objects resolve as static fields.
func NewStaticMember(typ *types.Named, pos token.Pos, pkg *types.Package, name string, obj types.Object) *types.Func {
return newMethodEx(typ.Origin(), pos, pkg, name, &TyStaticMember{obj})
}

func lookupStaticMember(typ *types.Named, name string) (*types.Func, types.Object) {
typ = typ.Origin()
if method := lookupMethod(typ, name); method != nil {
if obj, ok := staticMemberObj(method); ok {
return method, obj
}
}
return nil, nil
}

// ----------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions import.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,9 @@ func checkXGotsx(pkg *types.Package, scope *types.Scope, name string, o types.Ob
NewTemplateRecvMethod(m.typ, token.NoPos, pkg, m.name, o)
} else {
if debugImport {
log.Println("==> NewStaticMethod", tname, m.name)
log.Println("==> NewStaticMember", tname, m.name)
}
NewStaticMethod(m.typ, token.NoPos, pkg, m.name, o)
NewStaticMember(m.typ, token.NoPos, pkg, m.name, o)
}
}
case xgoxCh: // XGox_xxx
Expand Down
4 changes: 2 additions & 2 deletions util_gengo.go
Original file line number Diff line number Diff line change
Expand Up @@ -606,8 +606,8 @@ func (p *CodeBuilder) methodSigOf(typ types.Type, flag MemberFlag, arg, ret *Ele
sig := typ.(*types.Signature)
if t, ok := CheckFuncEx(sig); ok {
switch ext := t.(type) {
case *TyStaticMethod:
return p.funcExSigOf(ext.Func, ret)
case *TyStaticMember:
return p.funcExSigOf(ext.Member, ret)
case *TyTemplateRecvMethod:
return p.funcExSigOf(ext.Func, ret)
}
Expand Down
53 changes: 53 additions & 0 deletions xgo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,59 @@ func main() {
`)
}

func TestStaticMember(t *testing.T) {
pkg := newMainPackage()
scope := pkg.Types.Scope()
foo := pkg.NewType("foo").InitType(pkg, types.Typ[types.Int])
pkg.NewConstStart(scope, token.NoPos, nil, "XGos_foo_name").
Val("xgo").EndInit(1)
pkg.NewVarStart(token.NoPos, types.Typ[types.Int], "XGos_foo_count").
Val(100).EndInit(1)
gogen.NewStaticMember(foo, token.NoPos, pkg.Types, "name", scope.Lookup("XGos_foo_name"))
gogen.NewStaticMember(foo, token.NoPos, pkg.Types, "count", scope.Lookup("XGos_foo_count"))
pkg.NewFunc(nil, "main", nil, nil, false).BodyStart(pkg).
DefineVarStart(token.NoPos, "a").Typ(foo).MemberVal("name", 0).EndInit(1).
Typ(foo).MemberRef("count").IncDec(token.INC).EndStmt().
End()
domTest(t, pkg, `package main

type foo int

const XGos_foo_name = "xgo"

var XGos_foo_count int = 100

func main() {
a := XGos_foo_name
XGos_foo_count++
}
`)
}

func TestStaticMemberInstantiatedGenericType(t *testing.T) {
pkg := newMainPackage()
scope := pkg.Types.Scope()
tparam := types.NewTypeParam(types.NewTypeName(token.NoPos, pkg.Types, "T", nil), types.Universe.Lookup("any").Type())
box := pkg.NewType("Box").InitType(pkg, types.Typ[types.Int], tparam)
boxInt := pkg.Instantiate(box, []types.Type{types.Typ[types.Int]})
pkg.NewConstStart(scope, token.NoPos, nil, "XGos_Box_name").
Val("box").EndInit(1)
gogen.NewStaticMember(box, token.NoPos, pkg.Types, "name", scope.Lookup("XGos_Box_name"))
pkg.NewFunc(nil, "main", nil, nil, false).BodyStart(pkg).
DefineVarStart(token.NoPos, "name").Typ(boxInt).MemberVal("name", 0).EndInit(1).
End()
domTest(t, pkg, `package main

type Box[T any] int

const XGos_Box_name = "box"

func main() {
name := XGos_Box_name
}
`)
}

func TestTemplateRecvMethod(t *testing.T) {
pkg := newMainPackage()
bar := pkg.Import("github.com/goplus/gogen/internal/bar")
Expand Down
Loading