Repository: goplus/gop
Pull request #1545 (2023-11-14 14:13:19)

typesutil: update TypeAndValue
cl/internal/typesutil: update TypeAndValue

fix https://github.com/goplus/gop/issues/1537

```
package main

type Person struct {
	name string
	age  int8
}

func test() {
	p := Person{
		name: "jack",
	}
	_ = p.name
}
```


```
   info_test.go:106: ====== check types pass (count: 8) ======
        000:  3:13 | struct {
        	name string
        	age  int8
        } *ast.StructType                | type    : struct{name string; age int8} | type
        001:  4: 7 | string              *ast.Ident                     | type    : string | type
        002:  5: 7 | int8                *ast.Ident                     | type    : int8 | type
        003:  9: 7 | Person              *ast.Ident                     | type    : main.Person | type
        004:  9: 7 | Person{
        	name: "jack",
        } *ast.CompositeLit              | value   : main.Person | value
        005: 10: 9 | "jack"              *ast.BasicLit                  | value   : string = "jack" | constant
        006: 12: 6 | p                   *ast.Ident                     | var     : main.Person | variable
        007: 12: 6 | p.name              *ast.SelectorExpr              | var     : string | variable
        
    info_test.go:106: ====== check defs pass (count: 5) ======
        000:  3: 6 | Person              | type main.Person struct{name string; age int8}
        001:  4: 2 | name                | field name string
        002:  5: 2 | age                 | field age int8
        003:  8: 6 | test                | func main.test()
        004:  9: 2 | p                   | var p main.Person
        
    info_test.go:106: ====== check uses pass (count: 6) ======
        000:  4: 7 | string              | type string
        001:  5: 7 | int8                | type int8
        002:  9: 7 | Person              | type main.Person struct{name string; age int8}
        003: 10: 3 | name                | field name string
        004: 12: 6 | p                   | var p main.Person
        005: 12: 8 | name                | field name string
```


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/goplus/gop/pull/1545?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) Report
Attention: `55 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`0e106b9`)](https://app.codecov.io/gh/goplus/gop/commit/0e106b9efa4e50cdfa3b23d9acedfebffbb9ce1b?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) 91.23% compared to head [(`ccfa7c6`)](https://app.codecov.io/gh/goplus/gop/pull/1545?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) 87.96%.

| [Files](https://app.codecov.io/gh/goplus/gop/pull/1545?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) | Patch % | Lines |
|---|---|---|
| [cl/internal/typesutil/api.go](https://app.codecov.io/gh/goplus/gop/pull/1545?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus#diff-Y2wvaW50ZXJuYWwvdHlwZXN1dGlsL2FwaS5nbw==) | 20.28% | [55 Missing :warning: ](https://app.codecov.io/gh/goplus/gop/pull/1545?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1545      +/-   ##
==========================================
- Coverage   91.23%   87.96%   -3.27%     
==========================================
  Files          34       39       +5     
  Lines        9786    10257     +471     
==========================================
+ Hits         8928     9023      +95     
- Misses        693     1058     +365     
- Partials      165      176      +11     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/goplus/gop/pull/1545?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goplus).


Patch:
diff --git a/cl/compile.go b/cl/compile.go
index bdb9d3d2..d6d64bba 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -28,6 +28,7 @@ import (
 
 	"github.com/goplus/gop/ast"
 	"github.com/goplus/gop/ast/fromgo"
+	"github.com/goplus/gop/cl/internal/typesutil"
 	"github.com/goplus/gop/token"
 	"github.com/goplus/gox"
 	"github.com/goplus/gox/cpackages"
@@ -220,7 +221,7 @@ type goxRecorder struct {
 
 // Member maps identifiers to the objects they denote.
 func (p *goxRecorder) Member(id ast.Node, obj types.Object) {
-	tv := types.TypeAndValue{Type: obj.Type()}
+	tv := typesutil.NewTypeAndValueForObject(obj)
 	switch v := id.(type) {
 	case *ast.SelectorExpr:
 		sel := v.Sel
diff --git a/cl/expr.go b/cl/expr.go
index 00d90fe9..3752c652 100644
--- a/cl/expr.go
+++ b/cl/expr.go
@@ -158,12 +158,11 @@ find:
 		e := ctx.cb.Get(-1)
 		if oldo != nil && gox.IsTypeEx(e.Type) {
 			rec.Use(ident, oldo)
+			rec.Type(ident, typesutil.NewTypeAndValueForObject(oldo))
 			return
 		}
 		rec.Use(ident, o)
-		typ, _ := gox.DerefType(e.Type)
-		tv := typesutil.NewTypeAndValue(typ, e.CVal)
-		rec.Type(ident, tv)
+		rec.Type(ident, typesutil.NewTypeAndValueForObject(o))
 	}
 	return
 }
@@ -199,6 +198,7 @@ func compileExprLHS(ctx *blockCtx, expr ast.Expr) {
 		compileIndexExprLHS(ctx, v)
 	case *ast.SelectorExpr:
 		compileSelectorExprLHS(ctx, v)
+		recordTypesVariable(ctx, v, -1)
 	case *ast.StarExpr:
 		compileStarExprLHS(ctx, v)
 	default:
@@ -210,6 +210,21 @@ func twoValue(inFlags []int) bool {
 	return inFlags != nil && (inFlags[0]&clCallWithTwoValue) != 0
 }
 
+func recordTypesValue(ctx *blockCtx, expr ast.Expr, n int) {
+	if rec := ctx.recorder(); rec != nil {
+		e := ctx.cb.Get(n)
+		rec.Type(expr, typesutil.NewTypeAndValueForValue(e.Type, e.CVal))
+	}
+}
+
+func recordTypesVariable(ctx *blockCtx, expr ast.Expr, n int) {
+	if rec := ctx.recorder(); rec != nil {
+		e := ctx.cb.Get(n)
+		t, _ := gox.DerefType(e.Type)
+		rec.Type(expr, typesutil.NewTypeAndValueForVariable(t))
+	}
+}
+
 func compileExpr(ctx *blockCtx, expr ast.Expr, inFlags ...int) {
 	switch v := expr.(type) {
 	case *ast.Ident:
@@ -220,6 +235,7 @@ func compileExpr(ctx *blockCtx, expr ast.Expr, inFlags ...int) {
 		compileIdent(ctx, v, flags)
 	case *ast.BasicLit:
 		compileBasicLit(ctx, v)
+		recordTypesValue(ctx, v, -1)
 	case *ast.CallExpr:
 		flags := 0
 		if inFlags != nil {
@@ -234,8 +250,10 @@ func compileExpr(ctx *blockCtx, expr ast.Expr, inFlags ...int) {
 		compileSelectorExpr(ctx, v, flags)
 	case *ast.BinaryExpr:
 		compileBinaryExpr(ctx, v)
+		recordTypesValue(ctx, v, -1)
 	case *ast.UnaryExpr:
 		compileUnaryExpr(ctx, v, twoValue(inFlags))
+		recordTypesValue(ctx, v, -1)
 	case *ast.FuncLit:
 		compileFuncLit(ctx, v)
 	case *ast.CompositeLit:
@@ -359,10 +377,6 @@ func compileSelectorExprLHS(ctx *blockCtx, v *ast.SelectorExpr) {
 		}
 	default:
 		compileExpr(ctx, v.X)
-		if rec := ctx.recorder(); rec != nil {
-			e := ctx.cb.Get(-1)
-			rec.Type(v.X, typesutil.NewTypeAndValue(e.Type, e.CVal))
-		}
 	}
 	ctx.cb.MemberRef(v.Sel.Name, v)
 }
@@ -383,7 +397,7 @@ func compileSelectorExpr(ctx *blockCtx, v *ast.SelectorExpr, flags int) {
 		compileExpr(ctx, v.X)
 		if rec := ctx.recorder(); rec != nil {
 			e := ctx.cb.Get(-1)
-			rec.Type(v.X, typesutil.NewTypeAndValue(e.Type, e.CVal))
+			rec.Type(v.X, typesutil.NewTypeAndValueForType(e.Type))
 		}
 	}
 	if err := compileMember(ctx, v, v.Sel.Name, flags); err != nil {
@@ -544,6 +558,9 @@ func compileCallExpr(ctx *blockCtx, v *ast.CallExpr, inFlags int) {
 	for fn != nil {
 		err := compileCallArgs(fn, fnt, ctx, v, ellipsis, flags)
 		if err == nil {
+			if rec := ctx.recorder(); rec != nil {
+				rec.Type(v, typesutil.NewTypeAndValueForCallResult(ctx.cb.Get(-1).Type))
+			}
 			break
 		}
 		if fn.next == nil {
@@ -794,6 +811,9 @@ func compileStructLitInKeyVal(ctx *blockCtx, elts []ast.Expr, t *types.Struct, t
 			err := ctx.newCodeErrorf(name.Pos(), "%s undefined (type %v has no field or method %s)", src, typ, name.Name)
 			panic(err)
 		}
+		if rec := ctx.recorder(); rec != nil {
+			rec.Use(name, t.Field(idx))
+		}
 		switch expr := kv.Value.(type) {
 		case *ast.LambdaExpr, *ast.LambdaExpr2:
 			sig := checkLambdaFuncType(ctx, expr, t.Field(idx).Type(), clLambaField, kv.Key)
@@ -878,6 +898,9 @@ func compileCompositeLit(ctx *blockCtx, v *ast.CompositeLit, expected types.Type
 	}
 	if t, ok := underlying.(*types.Struct); ok && kind == compositeLitKeyVal {
 		compileStructLitInKeyVal(ctx, v.Elts, t, typ, v)
+		if rec := ctx.recorder(); rec != nil {
+			rec.Type(v, typesutil.NewTypeAndValueForValue(typ, nil))
+		}
 		if hasPtr {
 			ctx.cb.UnaryOp(gotoken.AND)
 		}
@@ -892,6 +915,10 @@ func compileCompositeLit(ctx *blockCtx, v *ast.CompositeLit, expected types.Type
 		ctx.cb.MapLit(nil, n<<1)
 		return
 	}
+	if rec := ctx.recorder(); rec != nil {
+		rec.Type(v.Type, typesutil.NewTypeAndValueForType(typ))
+		rec.Type(v, typesutil.NewTypeAndValueForValue(typ, nil))
+	}
 	switch underlying.(type) {
 	case *types.Slice:
 		ctx.cb.SliceLitEx(typ, n<<kind, kind == compositeLitKeyVal, v)
diff --git a/cl/func_type_and_var.go b/cl/func_type_and_var.go
index 23ee6fdc..4622a029 100644
--- a/cl/func_type_and_var.go
+++ b/cl/func_type_and_var.go
@@ -25,6 +25,7 @@ import (
 	"strconv"
 
 	"github.com/goplus/gop/ast"
+	"github.com/goplus/gop/cl/internal/typesutil"
 	"github.com/goplus/gop/token"
 	"github.com/goplus/gox"
 )
@@ -112,7 +113,12 @@ func toParam(ctx *blockCtx, fld *ast.Field, args []*gox.Param) []*gox.Param {
 
 // -----------------------------------------------------------------------------
 
-func toType(ctx *blockCtx, typ ast.Expr) types.Type {
+func toType(ctx *blockCtx, typ ast.Expr) (t types.Type) {
+	if rec := ctx.recorder(); rec != nil {
+		defer func() {
+			rec.Type(typ, typesutil.NewTypeAndValueForType(t))
+		}()
+	}
 	switch v := typ.(type) {
 	case *ast.Ident:
 		ctx.idents = append(ctx.idents, v)
@@ -228,6 +234,7 @@ func toIdentType(ctx *blockCtx, ident *ast.Ident) (ret types.Type) {
 		defer func() {
 			if obj != nil {
 				rec.Use(ident, obj)
+				rec.Type(ident, typesutil.NewTypeAndValueForObject(obj))
 			}
 		}()
 	}
@@ -341,7 +348,11 @@ func toStructType(ctx *blockCtx, v *ast.StructType) *types.Struct {
 			}
 		}
 	}
-	return types.NewStruct(fields, tags)
+	st := types.NewStruct(fields, tags)
+	if rec != nil {
+		rec.Type(v, typesutil.NewTypeAndValueForType(st))
+	}
+	return st
 }
 
 func toFieldTag(v *ast.BasicLit) string {
diff --git a/cl/internal/typesutil/api.go b/cl/internal/typesutil/api.go
index e5827a9d..000ce81f 100644
--- a/cl/internal/typesutil/api.go
+++ b/cl/internal/typesutil/api.go
@@ -49,13 +49,66 @@ type TypeAndValue struct {
 	Value constant.Value
 }
 
-func NewTypeAndValue(typ types.Type, val constant.Value) (ret types.TypeAndValue) {
-	if t, ok := typ.(*gox.TypeType); ok {
-		ret.Type = t.Type()
-		(*TypeAndValue)(unsafe.Pointer(&ret)).mode = typexpr
+func NewTypeAndValueForType(typ types.Type) (ret types.TypeAndValue) {
+	switch t := typ.(type) {
+	case *gox.TypeType:
+		typ = t.Type()
+	}
+	ret.Type = types.Default(typ)
+	(*TypeAndValue)(unsafe.Pointer(&ret)).mode = typexpr
+	return
+}
+
+func NewTypeAndValueForValue(typ types.Type, val constant.Value) (ret types.TypeAndValue) {
+	var mode operandMode
+	if val != nil {
+		mode = constant_
 	} else {
-		ret.Type = typ
-		ret.Value = val
+		mode = value
+	}
+	ret.Type = types.Default(typ)
+	ret.Value = val
+	(*TypeAndValue)(unsafe.Pointer(&ret)).mode = mode
+	return
+}
+
+func NewTypeAndValueForVariable(typ types.Type) (ret types.TypeAndValue) {
+	ret.Type = typ
+	(*TypeAndValue)(unsafe.Pointer(&ret)).mode = variable
+	return
+}
+
+func NewTypeAndValueForCallResult(typ types.Type) (ret types.TypeAndValue) {
+	if typ == nil {
+		ret.Type = &types.Tuple{}
+		(*TypeAndValue)(unsafe.Pointer(&ret)).mode = novalue
+		return
+	}
+	ret.Type = typ
+	(*TypeAndValue)(unsafe.Pointer(&ret)).mode = value
+	return
+}
+
+func NewTypeAndValueForObject(obj types.Object) (ret types.TypeAndValue) {
+	var mode operandMode
+	var val constant.Value
+	switch v := obj.(type) {
+	case *types.Const:
+		mode = constant_
+		val = v.Val()
+	case *types.TypeName:
+		mode = typexpr
+	case *types.Var:
+		mode = variable
+	case *types.Func:
+		mode = value
+	case *types.Builtin:
+		mode = builtin
+	case *types.Nil:
+		mode = value
 	}
+	ret.Type = obj.Type()
+	ret.Value = val
+	(*TypeAndValue)(unsafe.Pointer(&ret)).mode = mode
 	return
 }


Test patch:
diff --git a/cl/internal/typesutil/api_test.go b/cl/internal/typesutil/api_test.go
index 684f2d86..ec3a9f76 100644
--- a/cl/internal/typesutil/api_test.go
+++ b/cl/internal/typesutil/api_test.go
@@ -25,13 +25,55 @@ import (
 )
 
 func TestTypeAndValue(t *testing.T) {
-	ty := gox.NewTypeType(types.Typ[types.Int])
-	ret := NewTypeAndValue(ty, nil)
+	tyInt := types.Typ[types.Int]
+	ty := gox.NewTypeType(tyInt)
+	ret := NewTypeAndValueForType(ty)
 	if !ret.IsType() {
-		t.Fatal("NewTypeAndValue: not type?")
+		t.Fatal("NewTypeAndValueForType: not type?")
 	}
-	ret = NewTypeAndValue(types.Typ[types.Int], constant.MakeInt64(1))
+	ret = NewTypeAndValueForValue(tyInt, constant.MakeInt64(1))
 	if ret.Value == nil {
-		t.Fatal("NewTypeAndValue: not const?")
+		t.Fatal("NewTypeAndValueForValue: not const?")
+	}
+	ret = NewTypeAndValueForVariable(tyInt)
+	if !ret.Addressable() {
+		t.Fatal("NewTypeAndValueForVariable: not variable?")
+	}
+	ret = NewTypeAndValueForCallResult(tyInt)
+	if !ret.IsValue() {
+		t.Fatal("NewTypeAndValueForCall: not value?")
+	}
+	ret = NewTypeAndValueForCallResult(nil)
+	if !ret.IsVoid() {
+		t.Fatal("NewTypeAndValueForCall: not void?")
+	}
+	pkg := types.NewPackage("main", "main")
+	ret = NewTypeAndValueForObject(types.NewConst(0, pkg, "v", tyInt, constant.MakeInt64(100)))
+	if ret.Value == nil {
+		t.Fatal("NewTypeAndValueForObject: not const?")
+	}
+	ret = NewTypeAndValueForObject(types.NewTypeName(0, pkg, "MyInt", tyInt))
+	if !ret.IsType() {
+		t.Fatal("NewTypeAndValueForObject: not type?")
+	}
+	ret = NewTypeAndValueForObject(types.NewVar(0, pkg, "v", tyInt))
+	if !ret.Addressable() {
+		t.Fatal("NewTypeAndValueForObject: not variable?")
+	}
+	ret = NewTypeAndValueForObject(types.NewFunc(0, pkg, "fn", types.NewSignature(nil, nil, nil, false)))
+	if !ret.IsValue() {
+		t.Fatal("NewTypeAndValueForObject: not value?")
+	}
+	ret = NewTypeAndValueForValue(types.Typ[types.UntypedNil], nil)
+	if !ret.IsNil() {
+		t.Fatal("NewTypeAndValueForValue: not nil?")
+	}
+	ret = NewTypeAndValueForObject(types.Universe.Lookup("nil"))
+	if !ret.IsNil() {
+		t.Fatal("NewTypeAndValueForObject: not nil?")
+	}
+	ret = NewTypeAndValueForObject(types.Universe.Lookup("len"))
+	if !ret.IsBuiltin() {
+		t.Fatal("NewTypeAndValueForObject: not builtin?")
 	}
 }
diff --git a/x/typesutil/info_test.go b/x/typesutil/info_test.go
index 38d65491..840ab8d2 100644
--- a/x/typesutil/info_test.go
+++ b/x/typesutil/info_test.go
@@ -83,8 +83,7 @@ func testInfo(t *testing.T, src interface{}) {
 	if err != nil {
 		t.Fatal("parserGoSource error", err)
 	}
-	// TODO check types
-	//testItems(t, "types", typesList(fset, info.Types), goTypesList(fset, goinfo.Types))
+	testItems(t, "types", typesList(fset, info.Types), goTypesList(fset, goinfo.Types))
 	testItems(t, "defs", defsList(fset, info.Defs, true), goDefsList(fset, goinfo.Defs, true))
 	testItems(t, "uses", usesList(fset, info.Uses), goUsesList(fset, goinfo.Uses))
 	// TODO check selections
@@ -335,7 +334,7 @@ var v4 *T = &T{100,200};
 `)
 }
 
-func _TestStruct(t *testing.T) {
+func TestStruct(t *testing.T) {
 	testInfo(t, `package main
 
 type Person struct {