-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvar_declaration.go
More file actions
169 lines (154 loc) · 4.5 KB
/
Copy pathvar_declaration.go
File metadata and controls
169 lines (154 loc) · 4.5 KB
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package analysis
import (
"fmt"
"unicode"
"github.com/ProCode-Software/klar/internal/ast"
)
// Variable represents a variable type.
type Variable struct {
Type
Object *Object
VarKind VariableKind
}
// o's type is set to the returned variable.
func NewVariable(o *Object, kind VariableKind, typ Type) *Variable {
vr := &Variable{Object: o, VarKind: kind, Type: typ}
o.Type = vr
return vr
}
func (v *Variable) Underlying() Type { return v.Type }
func (v *Variable) Kind() Kind { return v.Type.Kind() }
func (v *Variable) String() string {
return v.Type.String()
/*
var kind, name string
switch v.VarKind {
default:
kind = "var"
case SelfVar:
kind = "self"
case FuncParamVar:
kind = "param"
case StructFieldVar:
kind = "field"
}
if v.Object != nil && v.VarKind != SelfVar {
name = " " + v.Object.name
}
return fmt.Sprintf("%s%s: %s", kind, name, v.Type)
*/
}
func (*Variable) objKind() {}
type VariableKind uint8
const (
_ VariableKind = iota
LocalVar // Locally declared variable
TopLevelVar // Module-level variable
SelfVar // self
FuncParamVar // Function parameter
StructFieldVar // Struct field
PipelineVar // value
)
type Constant struct {
Type Type
Value ConstValue // TODO
}
func (c *Constant) Kind() Kind { return c.Type.Kind() }
func (c *Constant) Underlying() Type { return c.Type }
func (*Constant) objKind() {}
func (c *Constant) String() string { return fmt.Sprintf("%s (%v)", c.Type, c.Value) }
func (c *Checker) checkVarDecl(o *Object) {
var (
vr = o.Type.(*Variable)
vinfo = o.info.varInfo
rhsExpr = vinfo.rhs
rhs *Expr
)
vr.Type = InvalidType
// Use the cached expression or check the RHS
if *vinfo.rhsExpr != nil {
rhs = *vinfo.rhsExpr
} else {
rhs = NewExpr(o.LookupContext()).withHint(vinfo.expType)
// Attach the statement context to the value so it can return out
// of the variable's parent function and access loop labels.
// func x() {
// y := when 1 + 1 { 2 -> return, _ -> 3 }
// }
if vinfo.sctx != nil {
rhs.stmtCtx = vinfo.sctx
}
c.checkExpr(rhsExpr, rhs)
rhs.Type = c.toTyped(rhs.Type, vinfo.expType, rhsExpr, rhs.Context.File)
*vinfo.rhsExpr = rhs
}
// TODO: Go calls check.initVar, which checks if the expression is untyped
// nil, sets untyped values to typed types, and calls check.assignment.
// Check compatibility with the explicit type annotation
if vinfo.expType != nil {
if !Compatible(rhs.Type, vinfo.expType) {
err := typeMismatch(vinfo.expType, rhs.Type, rhsExpr.GetRange())
err.AddHighlight(
"An explicit type of "+quote(vinfo.expType.String())+" was declared",
o.info.node.(*ast.VariableDeclaration).ExplicitType.GetRange(),
)
c.fileError(err, o.File)
}
// Always set the RHS type to the explicit type if provided
rhs.Type = vinfo.expType
}
// Destructure the RHS
for dest, typ := range c.followDestructure(
vinfo.lhs, rhs.Type, rhs.Context.File, rhsExpr.GetRange(), true,
) {
// TODO: Evaluate followDestructure only once per vinfo.rhsExpr, and cache
// the types of other variables using the same rhsExpr.
if sym, ok := dest.(*ast.Symbol); ok && sym.Identifier == o.Name {
vr.Type = typ
break
}
}
if vr.Type == nil {
panic(o.Name + " not yielded by followDestructure or it yielded a nil Type")
// vr.Type = InvalidType
}
}
func (c *Checker) checkConstDecl(o *Object) {
var (
cnst = o.Type.(*Constant)
vinfo = o.info.varInfo
val = vinfo.rhs
rhs *Expr
)
// Use the cached expression or check the RHS
if *vinfo.rhsExpr != nil {
rhs = *vinfo.rhsExpr
} else {
rhs = NewExpr(o.LookupContext(), constExpr).withHint(vinfo.expType)
// See comment in [Checker.checkVarDecl]
if vinfo.sctx != nil {
rhs.stmtCtx = vinfo.sctx
}
c.checkExpr(val, rhs)
rhs.Type = c.toTyped(rhs.Type, vinfo.expType, val, rhs.Context.File)
*vinfo.rhsExpr = rhs
}
// TODO: destructure
cnst.Value = rhs.ConstValue()
if cnst.Value == nil {
cnst.Value = UnknownConst{} // Ensure this is never nil if checking fails
}
cnst.Type = rhs.Type
// TODO: Go type checker calls check.assignment
}
// IsConst returns true if the given name is a constant name
// (all uppercase). Digits and underscores are allowed.
func IsConst(name string) bool {
for _, r := range name {
// Some characters, like CJK, are neither upper nor lower case. Allow them.
if unicode.IsLower(r) && !unicode.IsUpper(r) {
return false
}
}
return true
}