-
Notifications
You must be signed in to change notification settings - Fork 7
/
checkunaryexpr.go
121 lines (112 loc) · 3.36 KB
/
checkunaryexpr.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package eval
import (
"reflect"
"go/ast"
"go/token"
)
func checkUnaryExpr(unary *ast.UnaryExpr, env Env) (*UnaryExpr, []error) {
aexpr := &UnaryExpr{UnaryExpr: unary}
x, errs := CheckExpr(unary.X, env)
if errs == nil || x.IsConst() {
if t, err := expectSingleType(x); err != nil {
errs = append(errs, err)
} else if unary.Op == token.AND { // address of
if !isAddressableOrCompositeLit(x) {
printableX := fakeCheckExpr(unary.X, env)
printableX.setKnownType(knownType{t})
errs = append(errs, ErrInvalidAddressOf{printableX})
}
t := x.KnownType()[0]
if ct, ok := t.(ConstType); ok {
if ct == ConstNil {
errs = append(errs, ErrUntypedNil{x})
} else {
ptrT := reflect.PtrTo(unhackType(ct.DefaultPromotion()))
aexpr.knownType = knownType{ptrT}
}
} else {
ptrT := reflect.PtrTo(unhackType(t))
aexpr.knownType = knownType{ptrT}
}
aexpr.X = x
} else if unary.Op == token.ARROW { // <-
if (t.Kind() != reflect.Chan) || (t.ChanDir() | reflect.RecvDir == 0) {
errs = append(errs, ErrInvalidRecvFrom{x})
}
} else {
aexpr.X = x
// All numeric and bool unary expressions do not change type
aexpr.knownType = knownType(x.KnownType())
if x.IsConst() {
if ct, ok := t.(ConstType); ok {
c, moreErrs := evalConstUnaryExpr(aexpr, x, ct)
if moreErrs != nil {
errs = append(errs, moreErrs...)
}
aexpr.constValue = c
} else {
c, moreErrs := evalConstTypedUnaryExpr(aexpr, x)
if moreErrs != nil {
errs = append(errs, moreErrs...)
}
aexpr.constValue = c
}
} else {
if !isUnaryOpDefinedOn(unary.Op, t) {
errs = append(errs, ErrInvalidUnaryOperation{aexpr})
}
}
}
}
aexpr.X = x
return aexpr, errs
}
func evalConstUnaryExpr(unary *UnaryExpr, x Expr, xT ConstType) (constValue, []error) {
switch xT.(type) {
case ConstIntType, ConstRuneType, ConstFloatType, ConstComplexType:
xx := x.Const().Interface().(*ConstNumber)
return evalConstUnaryNumericExpr(unary, xx)
case ConstBoolType:
xx := x.Const().Bool()
return evalConstUnaryBoolExpr(unary, xx)
default:
return constValue{}, []error{ErrInvalidUnaryOperation{unary}}
}
}
func evalConstTypedUnaryExpr(unary *UnaryExpr, x Expr) (constValue, []error) {
t := x.KnownType()[0]
if xx, ok := convertTypedToConstNumber(x.Const()); ok {
zz, errs := evalConstUnaryNumericExpr(unary, xx)
if !reflect.Value(zz).IsValid() {
return constValue{}, errs
}
rr, moreErrs := promoteConstToTyped(xx.Type, zz, t, x)
return rr, append(errs, moreErrs...)
} else if t.Kind() == reflect.Bool {
return evalConstUnaryBoolExpr(unary, x.Const().Bool())
}
return constValue{}, []error{ErrInvalidUnaryOperation{unary}}
}
func evalConstUnaryNumericExpr(unary *UnaryExpr, x *ConstNumber) (constValue, []error) {
switch unary.Op {
case token.ADD:
return constValueOf(x), nil
case token.SUB:
zero := &ConstNumber{Type: x.Type}
return constValueOf(zero.Sub(zero, x)), nil
case token.XOR:
if x.Type.IsIntegral() {
minusOne := NewConstInt64(-1)
return constValueOf(minusOne.Xor(minusOne, x)), nil
}
}
return constValue{}, []error{ErrInvalidUnaryOperation{unary}}
}
func evalConstUnaryBoolExpr(unary *UnaryExpr, x bool) (constValue, []error) {
switch unary.Op {
case token.NOT:
return constValueOf(!x), nil
default:
return constValue{}, []error{ErrInvalidUnaryOperation{unary}}
}
}