diff --git a/cl/internal/typesutil/api.go b/cl/internal/typesutil/api.go index 00183b276..0699ccfc8 100644 --- a/cl/internal/typesutil/api.go +++ b/cl/internal/typesutil/api.go @@ -24,27 +24,27 @@ import ( "github.com/goplus/gox" ) -// An operandMode specifies the (addressing) mode of an operand. -type operandMode byte +// An OperandMode specifies the (addressing) mode of an operand. +type OperandMode byte const ( - invalid operandMode = iota // operand is invalid - novalue // operand represents no value (result of a function call w/o result) - builtin // operand is a built-in function - typexpr // operand is a type - constant_ // operand is a constant; the operand's typ is a Basic type - variable // operand is an addressable variable - mapindex // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment) - value // operand is a computed value - commaok // like value, but operand may be used in a comma,ok expression - commaerr // like commaok, but second value is error, not boolean - cgofunc // operand is a cgo function + Invalid OperandMode = iota // operand is invalid + NoValue // operand represents no value (result of a function call w/o result) + Builtin // operand is a built-in function + TypExpr // operand is a type + Constant // operand is a constant; the operand's typ is a Basic type + Variable // operand is an addressable variable + MapIndex // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment) + Value // operand is a computed value + CommaOK // like value, but operand may be used in a comma,ok expression + CommaErr // like commaok, but second value is error, not boolean + CgoFunc // operand is a cgo function ) // TypeAndValue reports the type and value (for constants) // of the corresponding expression. type TypeAndValue struct { - mode operandMode + mode OperandMode Type types.Type Value constant.Value } @@ -55,16 +55,17 @@ func NewTypeAndValueForType(typ types.Type) (ret types.TypeAndValue) { typ = t.Type() } ret.Type = typ - (*TypeAndValue)(unsafe.Pointer(&ret)).mode = typexpr + (*TypeAndValue)(unsafe.Pointer(&ret)).mode = TypExpr return } -func NewTypeAndValueForValue(typ types.Type, val constant.Value) (ret types.TypeAndValue) { - var mode operandMode +func NewTypeAndValueForValue(typ types.Type, val constant.Value, mode OperandMode) (ret types.TypeAndValue) { + switch t := typ.(type) { + case *gox.TypeType: + typ = t.Type() + } if val != nil { - mode = constant_ - } else { - mode = value + mode = Constant } ret.Type = typ ret.Value = val @@ -72,30 +73,18 @@ func NewTypeAndValueForValue(typ types.Type, val constant.Value) (ret types.Type return } -func NewTypeAndValueForVariable(typ types.Type) (ret types.TypeAndValue) { - ret.Type = typ - (*TypeAndValue)(unsafe.Pointer(&ret)).mode = variable - return -} - -func NewTypeAndValueForMapIndex(typ types.Type) (ret types.TypeAndValue) { - ret.Type = typ - (*TypeAndValue)(unsafe.Pointer(&ret)).mode = mapindex - return -} - func NewTypeAndValueForCallResult(typ types.Type, val constant.Value) (ret types.TypeAndValue) { - var mode operandMode + var mode OperandMode if typ == nil { ret.Type = &types.Tuple{} - mode = novalue + mode = NoValue } else { ret.Type = typ if val != nil { ret.Value = val - mode = constant_ + mode = Constant } else { - mode = value + mode = Value } } (*TypeAndValue)(unsafe.Pointer(&ret)).mode = mode @@ -103,22 +92,22 @@ func NewTypeAndValueForCallResult(typ types.Type, val constant.Value) (ret types } func NewTypeAndValueForObject(obj types.Object) (ret types.TypeAndValue) { - var mode operandMode + var mode OperandMode var val constant.Value switch v := obj.(type) { case *types.Const: - mode = constant_ + mode = Constant val = v.Val() case *types.TypeName: - mode = typexpr + mode = TypExpr case *types.Var: - mode = variable + mode = Variable case *types.Func: - mode = value + mode = Value case *types.Builtin: - mode = builtin + mode = Builtin case *types.Nil: - mode = value + mode = Value } ret.Type = obj.Type() ret.Value = val diff --git a/cl/internal/typesutil/api_test.go b/cl/internal/typesutil/api_test.go index 1a4af2788..a726797f9 100644 --- a/cl/internal/typesutil/api_test.go +++ b/cl/internal/typesutil/api_test.go @@ -31,18 +31,10 @@ func TestTypeAndValue(t *testing.T) { if !ret.IsType() { t.Fatal("NewTypeAndValueForType: not type?") } - ret = NewTypeAndValueForValue(tyInt, constant.MakeInt64(1)) + ret = NewTypeAndValueForValue(tyInt, constant.MakeInt64(1), Constant) if ret.Value == nil { t.Fatal("NewTypeAndValueForValue: not const?") } - ret = NewTypeAndValueForVariable(tyInt) - if !ret.Addressable() { - t.Fatal("NewTypeAndValueForVariable: not variable?") - } - ret = NewTypeAndValueForMapIndex(tyInt) - if !(!ret.Addressable() && ret.Assignable()) { - t.Fatal("NewTypeAndValueForVariable: not map?") - } ret = NewTypeAndValueForCallResult(tyInt, nil) if !ret.IsValue() { t.Fatal("NewTypeAndValueForCall: not value?") @@ -72,7 +64,7 @@ func TestTypeAndValue(t *testing.T) { if !ret.IsValue() { t.Fatal("NewTypeAndValueForObject: not value?") } - ret = NewTypeAndValueForValue(types.Typ[types.UntypedNil], nil) + ret = NewTypeAndValueForValue(types.Typ[types.UntypedNil], nil, Value) if !ret.IsNil() { t.Fatal("NewTypeAndValueForValue: not nil?") } diff --git a/cl/recorder.go b/cl/recorder.go index a8ef3fa7a..385a7b501 100644 --- a/cl/recorder.go +++ b/cl/recorder.go @@ -22,6 +22,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" ) @@ -60,51 +61,51 @@ func newTypeRecord(rec Recorder) *typesRecorder { return &typesRecorder{rec, make(map[ast.Expr]types.TypeAndValue)} } -func (rec *typesRecorder) recordTypeValue(ctx *blockCtx, expr ast.Expr) { - e := ctx.cb.Get(-1) - rec.Type(expr, typesutil.NewTypeAndValueForValue(e.Type, e.CVal)) -} - -func (rec *typesRecorder) recordTypeVariable(ctx *blockCtx, expr ast.Expr) { - e := ctx.cb.Get(-1) - t, _ := gox.DerefType(e.Type) - rec.Type(expr, typesutil.NewTypeAndValueForVariable(t)) -} - -func (rec *typesRecorder) recordTypeMapIndex(ctx *blockCtx, expr ast.Expr) { +func (rec *typesRecorder) recordTypeValue(ctx *blockCtx, expr ast.Expr, mode typesutil.OperandMode) { e := ctx.cb.Get(-1) t, _ := gox.DerefType(e.Type) - rec.Type(expr, typesutil.NewTypeAndValueForMapIndex(t)) + rec.Type(expr, typesutil.NewTypeAndValueForValue(t, e.CVal, mode)) } func (rec *typesRecorder) indexExpr(ctx *blockCtx, expr *ast.IndexExpr) { if tv, ok := rec.types[expr.X]; ok { switch tv.Type.(type) { case *types.Map: - rec.recordTypeMapIndex(ctx, expr) + rec.recordTypeValue(ctx, expr, typesutil.MapIndex) return case *types.Slice: - rec.recordTypeVariable(ctx, expr) + rec.recordTypeValue(ctx, expr, typesutil.Variable) return } } switch expr.X.(type) { case *ast.CompositeLit: - rec.recordTypeValue(ctx, expr) + rec.recordTypeValue(ctx, expr, typesutil.Value) + default: + rec.recordTypeValue(ctx, expr, typesutil.Variable) + } +} + +func (rec *typesRecorder) unaryExpr(ctx *blockCtx, expr *ast.UnaryExpr) { + switch expr.Op { + case token.ARROW: + rec.recordTypeValue(ctx, expr, typesutil.CommaOK) default: - rec.recordTypeVariable(ctx, expr) + rec.recordTypeValue(ctx, expr, typesutil.Value) } } func (rec *typesRecorder) recordCallExpr(ctx *blockCtx, v *ast.CallExpr, fnt types.Type) { e := ctx.cb.Get(-1) - rec.Type(v.Fun, typesutil.NewTypeAndValueForValue(fnt, nil)) + if _, ok := rec.types[v.Fun]; !ok { + rec.Type(v.Fun, typesutil.NewTypeAndValueForValue(fnt, nil, typesutil.Value)) + } rec.Type(v, typesutil.NewTypeAndValueForCallResult(e.Type, e.CVal)) } func (rec *typesRecorder) recordCompositeLit(ctx *blockCtx, v *ast.CompositeLit, typ types.Type) { rec.Type(v.Type, typesutil.NewTypeAndValueForType(typ)) - rec.Type(v, typesutil.NewTypeAndValueForValue(typ, nil)) + rec.Type(v, typesutil.NewTypeAndValueForValue(typ, nil, typesutil.Value)) } func (rec *typesRecorder) recordType(ctx *blockCtx, typ ast.Expr, t types.Type) { @@ -120,14 +121,14 @@ func (rec *typesRecorder) recordExpr(ctx *blockCtx, expr ast.Expr, rhs bool) { switch v := expr.(type) { case *ast.Ident: case *ast.BasicLit: - rec.recordTypeValue(ctx, v) + rec.recordTypeValue(ctx, v, typesutil.Value) case *ast.CallExpr: case *ast.SelectorExpr: - rec.recordTypeVariable(ctx, v) + rec.recordTypeValue(ctx, v, typesutil.Variable) case *ast.BinaryExpr: - rec.recordTypeValue(ctx, v) + rec.recordTypeValue(ctx, v, typesutil.Value) case *ast.UnaryExpr: - rec.recordTypeValue(ctx, v) + rec.unaryExpr(ctx, v) case *ast.FuncLit: case *ast.CompositeLit: case *ast.SliceLit: @@ -136,20 +137,27 @@ func (rec *typesRecorder) recordExpr(ctx *blockCtx, expr ast.Expr, rhs bool) { rec.indexExpr(ctx, v) case *ast.IndexListExpr: case *ast.SliceExpr: - rec.recordTypeValue(ctx, v) + rec.recordTypeValue(ctx, v, typesutil.Value) case *ast.StarExpr: - rec.recordTypeVariable(ctx, v) + rec.recordTypeValue(ctx, v, typesutil.Variable) case *ast.ArrayType: + rec.recordTypeValue(ctx, v, typesutil.TypExpr) case *ast.MapType: + rec.recordTypeValue(ctx, v, typesutil.TypExpr) case *ast.StructType: + rec.recordTypeValue(ctx, v, typesutil.TypExpr) case *ast.ChanType: + rec.recordTypeValue(ctx, v, typesutil.TypExpr) case *ast.InterfaceType: + rec.recordTypeValue(ctx, v, typesutil.TypExpr) case *ast.ComprehensionExpr: case *ast.TypeAssertExpr: + rec.recordTypeValue(ctx, v, typesutil.CommaOK) case *ast.ParenExpr: - rec.recordTypeValue(ctx, v) + rec.recordTypeValue(ctx, v, typesutil.Value) case *ast.ErrWrapExpr: case *ast.FuncType: + rec.recordTypeValue(ctx, v, typesutil.TypExpr) case *ast.Ellipsis: case *ast.KeyValueExpr: default: diff --git a/go.mod b/go.mod index 7298c84fb..cce3b0e33 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,4 @@ require ( golang.org/x/sys v0.14.0 // indirect ) -retract ( - v1.1.12 -) +retract v1.1.12 diff --git a/x/typesutil/info_test.go b/x/typesutil/info_test.go index 2380c7239..61304e964 100644 --- a/x/typesutil/info_test.go +++ b/x/typesutil/info_test.go @@ -378,3 +378,30 @@ func test() { } `) } + +func TestTypeAssert(t *testing.T) { + testInfo(t, `package main + +func test() { + var a interface{} = 100 + if n, ok := a.(int); ok { + _ = n + } +} +`) +} + +func TestChan(t *testing.T) { + testInfo(t, `package main + +func test() { + var ch chan int + select { + case n, ok := <-ch: + _ = n + _ = ok + break + } +} +`) +}