]>
Commit | Line | Data |
---|---|---|
1 | package hil | |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | "sync" | |
6 | ||
7 | "github.com/hashicorp/hil/ast" | |
8 | ) | |
9 | ||
10 | // TypeCheck implements ast.Visitor for type checking an AST tree. | |
11 | // It requires some configuration to look up the type of nodes. | |
12 | // | |
13 | // It also optionally will not type error and will insert an implicit | |
14 | // type conversions for specific types if specified by the Implicit | |
15 | // field. Note that this is kind of organizationally weird to put into | |
16 | // this structure but we'd rather do that than duplicate the type checking | |
17 | // logic multiple times. | |
18 | type TypeCheck struct { | |
19 | Scope ast.Scope | |
20 | ||
21 | // Implicit is a map of implicit type conversions that we can do, | |
22 | // and that shouldn't error. The key of the first map is the from type, | |
23 | // the key of the second map is the to type, and the final string | |
24 | // value is the function to call (which must be registered in the Scope). | |
25 | Implicit map[ast.Type]map[ast.Type]string | |
26 | ||
27 | // Stack of types. This shouldn't be used directly except by implementations | |
28 | // of TypeCheckNode. | |
29 | Stack []ast.Type | |
30 | ||
31 | err error | |
32 | lock sync.Mutex | |
33 | } | |
34 | ||
35 | // TypeCheckNode is the interface that must be implemented by any | |
36 | // ast.Node that wants to support type-checking. If the type checker | |
37 | // encounters a node that doesn't implement this, it will error. | |
38 | type TypeCheckNode interface { | |
39 | TypeCheck(*TypeCheck) (ast.Node, error) | |
40 | } | |
41 | ||
42 | func (v *TypeCheck) Visit(root ast.Node) error { | |
43 | v.lock.Lock() | |
44 | defer v.lock.Unlock() | |
45 | defer v.reset() | |
46 | root.Accept(v.visit) | |
47 | ||
48 | // If the resulting type is unknown, then just let the whole thing go. | |
49 | if v.err == errExitUnknown { | |
50 | v.err = nil | |
51 | } | |
52 | ||
53 | return v.err | |
54 | } | |
55 | ||
56 | func (v *TypeCheck) visit(raw ast.Node) ast.Node { | |
57 | if v.err != nil { | |
58 | return raw | |
59 | } | |
60 | ||
61 | var result ast.Node | |
62 | var err error | |
63 | switch n := raw.(type) { | |
64 | case *ast.Arithmetic: | |
65 | tc := &typeCheckArithmetic{n} | |
66 | result, err = tc.TypeCheck(v) | |
67 | case *ast.Call: | |
68 | tc := &typeCheckCall{n} | |
69 | result, err = tc.TypeCheck(v) | |
70 | case *ast.Conditional: | |
71 | tc := &typeCheckConditional{n} | |
72 | result, err = tc.TypeCheck(v) | |
73 | case *ast.Index: | |
74 | tc := &typeCheckIndex{n} | |
75 | result, err = tc.TypeCheck(v) | |
76 | case *ast.Output: | |
77 | tc := &typeCheckOutput{n} | |
78 | result, err = tc.TypeCheck(v) | |
79 | case *ast.LiteralNode: | |
80 | tc := &typeCheckLiteral{n} | |
81 | result, err = tc.TypeCheck(v) | |
82 | case *ast.VariableAccess: | |
83 | tc := &typeCheckVariableAccess{n} | |
84 | result, err = tc.TypeCheck(v) | |
85 | default: | |
86 | tc, ok := raw.(TypeCheckNode) | |
87 | if !ok { | |
88 | err = fmt.Errorf("unknown node for type check: %#v", raw) | |
89 | break | |
90 | } | |
91 | ||
92 | result, err = tc.TypeCheck(v) | |
93 | } | |
94 | ||
95 | if err != nil { | |
96 | pos := raw.Pos() | |
97 | v.err = fmt.Errorf("At column %d, line %d: %s", | |
98 | pos.Column, pos.Line, err) | |
99 | } | |
100 | ||
101 | return result | |
102 | } | |
103 | ||
104 | type typeCheckArithmetic struct { | |
105 | n *ast.Arithmetic | |
106 | } | |
107 | ||
108 | func (tc *typeCheckArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) { | |
109 | // The arguments are on the stack in reverse order, so pop them off. | |
110 | exprs := make([]ast.Type, len(tc.n.Exprs)) | |
111 | for i, _ := range tc.n.Exprs { | |
112 | exprs[len(tc.n.Exprs)-1-i] = v.StackPop() | |
113 | } | |
114 | ||
115 | // If any operand is unknown then our result is automatically unknown | |
116 | for _, ty := range exprs { | |
117 | if ty == ast.TypeUnknown { | |
118 | v.StackPush(ast.TypeUnknown) | |
119 | return tc.n, nil | |
120 | } | |
121 | } | |
122 | ||
123 | switch tc.n.Op { | |
124 | case ast.ArithmeticOpLogicalAnd, ast.ArithmeticOpLogicalOr: | |
125 | return tc.checkLogical(v, exprs) | |
126 | case ast.ArithmeticOpEqual, ast.ArithmeticOpNotEqual, | |
127 | ast.ArithmeticOpLessThan, ast.ArithmeticOpGreaterThan, | |
128 | ast.ArithmeticOpGreaterThanOrEqual, ast.ArithmeticOpLessThanOrEqual: | |
129 | return tc.checkComparison(v, exprs) | |
130 | default: | |
131 | return tc.checkNumeric(v, exprs) | |
132 | } | |
133 | ||
134 | } | |
135 | ||
136 | func (tc *typeCheckArithmetic) checkNumeric(v *TypeCheck, exprs []ast.Type) (ast.Node, error) { | |
137 | // Determine the resulting type we want. We do this by going over | |
138 | // every expression until we find one with a type we recognize. | |
139 | // We do this because the first expr might be a string ("var.foo") | |
140 | // and we need to know what to implicit to. | |
141 | mathFunc := "__builtin_IntMath" | |
142 | mathType := ast.TypeInt | |
143 | for _, v := range exprs { | |
144 | // We assume int math but if we find ANY float, the entire | |
145 | // expression turns into floating point math. | |
146 | if v == ast.TypeFloat { | |
147 | mathFunc = "__builtin_FloatMath" | |
148 | mathType = v | |
149 | break | |
150 | } | |
151 | } | |
152 | ||
153 | // Verify the args | |
154 | for i, arg := range exprs { | |
155 | if arg != mathType { | |
156 | cn := v.ImplicitConversion(exprs[i], mathType, tc.n.Exprs[i]) | |
157 | if cn != nil { | |
158 | tc.n.Exprs[i] = cn | |
159 | continue | |
160 | } | |
161 | ||
162 | return nil, fmt.Errorf( | |
163 | "operand %d should be %s, got %s", | |
164 | i+1, mathType, arg) | |
165 | } | |
166 | } | |
167 | ||
168 | // Modulo doesn't work for floats | |
169 | if mathType == ast.TypeFloat && tc.n.Op == ast.ArithmeticOpMod { | |
170 | return nil, fmt.Errorf("modulo cannot be used with floats") | |
171 | } | |
172 | ||
173 | // Return type | |
174 | v.StackPush(mathType) | |
175 | ||
176 | // Replace our node with a call to the proper function. This isn't | |
177 | // type checked but we already verified types. | |
178 | args := make([]ast.Node, len(tc.n.Exprs)+1) | |
179 | args[0] = &ast.LiteralNode{ | |
180 | Value: tc.n.Op, | |
181 | Typex: ast.TypeInt, | |
182 | Posx: tc.n.Pos(), | |
183 | } | |
184 | copy(args[1:], tc.n.Exprs) | |
185 | return &ast.Call{ | |
186 | Func: mathFunc, | |
187 | Args: args, | |
188 | Posx: tc.n.Pos(), | |
189 | }, nil | |
190 | } | |
191 | ||
192 | func (tc *typeCheckArithmetic) checkComparison(v *TypeCheck, exprs []ast.Type) (ast.Node, error) { | |
193 | if len(exprs) != 2 { | |
194 | // This should never happen, because the parser never produces | |
195 | // nodes that violate this. | |
196 | return nil, fmt.Errorf( | |
197 | "comparison operators must have exactly two operands", | |
198 | ) | |
199 | } | |
200 | ||
201 | // The first operand always dictates the type for a comparison. | |
202 | compareFunc := "" | |
203 | compareType := exprs[0] | |
204 | switch compareType { | |
205 | case ast.TypeBool: | |
206 | compareFunc = "__builtin_BoolCompare" | |
207 | case ast.TypeFloat: | |
208 | compareFunc = "__builtin_FloatCompare" | |
209 | case ast.TypeInt: | |
210 | compareFunc = "__builtin_IntCompare" | |
211 | case ast.TypeString: | |
212 | compareFunc = "__builtin_StringCompare" | |
213 | default: | |
214 | return nil, fmt.Errorf( | |
215 | "comparison operators apply only to bool, float, int, and string", | |
216 | ) | |
217 | } | |
218 | ||
219 | // For non-equality comparisons, we will do implicit conversions to | |
220 | // integer types if possible. In this case, we need to go through and | |
221 | // determine the type of comparison we're doing to enable the implicit | |
222 | // conversion. | |
223 | if tc.n.Op != ast.ArithmeticOpEqual && tc.n.Op != ast.ArithmeticOpNotEqual { | |
224 | compareFunc = "__builtin_IntCompare" | |
225 | compareType = ast.TypeInt | |
226 | for _, expr := range exprs { | |
227 | if expr == ast.TypeFloat { | |
228 | compareFunc = "__builtin_FloatCompare" | |
229 | compareType = ast.TypeFloat | |
230 | break | |
231 | } | |
232 | } | |
233 | } | |
234 | ||
235 | // Verify (and possibly, convert) the args | |
236 | for i, arg := range exprs { | |
237 | if arg != compareType { | |
238 | cn := v.ImplicitConversion(exprs[i], compareType, tc.n.Exprs[i]) | |
239 | if cn != nil { | |
240 | tc.n.Exprs[i] = cn | |
241 | continue | |
242 | } | |
243 | ||
244 | return nil, fmt.Errorf( | |
245 | "operand %d should be %s, got %s", | |
246 | i+1, compareType, arg, | |
247 | ) | |
248 | } | |
249 | } | |
250 | ||
251 | // Only ints and floats can have the <, >, <= and >= operators applied | |
252 | switch tc.n.Op { | |
253 | case ast.ArithmeticOpEqual, ast.ArithmeticOpNotEqual: | |
254 | // anything goes | |
255 | default: | |
256 | switch compareType { | |
257 | case ast.TypeFloat, ast.TypeInt: | |
258 | // fine | |
259 | default: | |
260 | return nil, fmt.Errorf( | |
261 | "<, >, <= and >= may apply only to int and float values", | |
262 | ) | |
263 | } | |
264 | } | |
265 | ||
266 | // Comparison operators always return bool | |
267 | v.StackPush(ast.TypeBool) | |
268 | ||
269 | // Replace our node with a call to the proper function. This isn't | |
270 | // type checked but we already verified types. | |
271 | args := make([]ast.Node, len(tc.n.Exprs)+1) | |
272 | args[0] = &ast.LiteralNode{ | |
273 | Value: tc.n.Op, | |
274 | Typex: ast.TypeInt, | |
275 | Posx: tc.n.Pos(), | |
276 | } | |
277 | copy(args[1:], tc.n.Exprs) | |
278 | return &ast.Call{ | |
279 | Func: compareFunc, | |
280 | Args: args, | |
281 | Posx: tc.n.Pos(), | |
282 | }, nil | |
283 | } | |
284 | ||
285 | func (tc *typeCheckArithmetic) checkLogical(v *TypeCheck, exprs []ast.Type) (ast.Node, error) { | |
286 | for i, t := range exprs { | |
287 | if t != ast.TypeBool { | |
288 | cn := v.ImplicitConversion(t, ast.TypeBool, tc.n.Exprs[i]) | |
289 | if cn == nil { | |
290 | return nil, fmt.Errorf( | |
291 | "logical operators require boolean operands, not %s", | |
292 | t, | |
293 | ) | |
294 | } | |
295 | tc.n.Exprs[i] = cn | |
296 | } | |
297 | } | |
298 | ||
299 | // Return type is always boolean | |
300 | v.StackPush(ast.TypeBool) | |
301 | ||
302 | // Arithmetic nodes are replaced with a call to a built-in function | |
303 | args := make([]ast.Node, len(tc.n.Exprs)+1) | |
304 | args[0] = &ast.LiteralNode{ | |
305 | Value: tc.n.Op, | |
306 | Typex: ast.TypeInt, | |
307 | Posx: tc.n.Pos(), | |
308 | } | |
309 | copy(args[1:], tc.n.Exprs) | |
310 | return &ast.Call{ | |
311 | Func: "__builtin_Logical", | |
312 | Args: args, | |
313 | Posx: tc.n.Pos(), | |
314 | }, nil | |
315 | } | |
316 | ||
317 | type typeCheckCall struct { | |
318 | n *ast.Call | |
319 | } | |
320 | ||
321 | func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) { | |
322 | // Look up the function in the map | |
323 | function, ok := v.Scope.LookupFunc(tc.n.Func) | |
324 | if !ok { | |
325 | return nil, fmt.Errorf("unknown function called: %s", tc.n.Func) | |
326 | } | |
327 | ||
328 | // The arguments are on the stack in reverse order, so pop them off. | |
329 | args := make([]ast.Type, len(tc.n.Args)) | |
330 | for i, _ := range tc.n.Args { | |
331 | args[len(tc.n.Args)-1-i] = v.StackPop() | |
332 | } | |
333 | ||
334 | // Verify the args | |
335 | for i, expected := range function.ArgTypes { | |
336 | if expected == ast.TypeAny { | |
337 | continue | |
338 | } | |
339 | ||
340 | if args[i] == ast.TypeUnknown { | |
341 | v.StackPush(ast.TypeUnknown) | |
342 | return tc.n, nil | |
343 | } | |
344 | ||
345 | if args[i] != expected { | |
346 | cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i]) | |
347 | if cn != nil { | |
348 | tc.n.Args[i] = cn | |
349 | continue | |
350 | } | |
351 | ||
352 | return nil, fmt.Errorf( | |
353 | "%s: argument %d should be %s, got %s", | |
354 | tc.n.Func, i+1, expected.Printable(), args[i].Printable()) | |
355 | } | |
356 | } | |
357 | ||
358 | // If we're variadic, then verify the types there | |
359 | if function.Variadic && function.VariadicType != ast.TypeAny { | |
360 | args = args[len(function.ArgTypes):] | |
361 | for i, t := range args { | |
362 | if t == ast.TypeUnknown { | |
363 | v.StackPush(ast.TypeUnknown) | |
364 | return tc.n, nil | |
365 | } | |
366 | ||
367 | if t != function.VariadicType { | |
368 | realI := i + len(function.ArgTypes) | |
369 | cn := v.ImplicitConversion( | |
370 | t, function.VariadicType, tc.n.Args[realI]) | |
371 | if cn != nil { | |
372 | tc.n.Args[realI] = cn | |
373 | continue | |
374 | } | |
375 | ||
376 | return nil, fmt.Errorf( | |
377 | "%s: argument %d should be %s, got %s", | |
378 | tc.n.Func, realI, | |
379 | function.VariadicType.Printable(), t.Printable()) | |
380 | } | |
381 | } | |
382 | } | |
383 | ||
384 | // Return type | |
385 | v.StackPush(function.ReturnType) | |
386 | ||
387 | return tc.n, nil | |
388 | } | |
389 | ||
390 | type typeCheckConditional struct { | |
391 | n *ast.Conditional | |
392 | } | |
393 | ||
394 | func (tc *typeCheckConditional) TypeCheck(v *TypeCheck) (ast.Node, error) { | |
395 | // On the stack we have the types of the condition, true and false | |
396 | // expressions, but they are in reverse order. | |
397 | falseType := v.StackPop() | |
398 | trueType := v.StackPop() | |
399 | condType := v.StackPop() | |
400 | ||
401 | if condType == ast.TypeUnknown { | |
402 | v.StackPush(ast.TypeUnknown) | |
403 | return tc.n, nil | |
404 | } | |
405 | ||
406 | if condType != ast.TypeBool { | |
407 | cn := v.ImplicitConversion(condType, ast.TypeBool, tc.n.CondExpr) | |
408 | if cn == nil { | |
409 | return nil, fmt.Errorf( | |
410 | "condition must be type bool, not %s", condType.Printable(), | |
411 | ) | |
412 | } | |
413 | tc.n.CondExpr = cn | |
414 | } | |
415 | ||
416 | // The types of the true and false expression must match | |
417 | if trueType != falseType && trueType != ast.TypeUnknown && falseType != ast.TypeUnknown { | |
418 | ||
419 | // Since passing around stringified versions of other types is | |
420 | // common, we pragmatically allow the false expression to dictate | |
421 | // the result type when the true expression is a string. | |
422 | if trueType == ast.TypeString { | |
423 | cn := v.ImplicitConversion(trueType, falseType, tc.n.TrueExpr) | |
424 | if cn == nil { | |
425 | return nil, fmt.Errorf( | |
426 | "true and false expression types must match; have %s and %s", | |
427 | trueType.Printable(), falseType.Printable(), | |
428 | ) | |
429 | } | |
430 | tc.n.TrueExpr = cn | |
431 | trueType = falseType | |
432 | } else { | |
433 | cn := v.ImplicitConversion(falseType, trueType, tc.n.FalseExpr) | |
434 | if cn == nil { | |
435 | return nil, fmt.Errorf( | |
436 | "true and false expression types must match; have %s and %s", | |
437 | trueType.Printable(), falseType.Printable(), | |
438 | ) | |
439 | } | |
440 | tc.n.FalseExpr = cn | |
441 | falseType = trueType | |
442 | } | |
443 | } | |
444 | ||
445 | // Currently list and map types cannot be used, because we cannot | |
446 | // generally assert that their element types are consistent. | |
447 | // Such support might be added later, either by improving the type | |
448 | // system or restricting usage to only variable and literal expressions, | |
449 | // but for now this is simply prohibited because it doesn't seem to | |
450 | // be a common enough case to be worth the complexity. | |
451 | switch trueType { | |
452 | case ast.TypeList: | |
453 | return nil, fmt.Errorf( | |
454 | "conditional operator cannot be used with list values", | |
455 | ) | |
456 | case ast.TypeMap: | |
457 | return nil, fmt.Errorf( | |
458 | "conditional operator cannot be used with map values", | |
459 | ) | |
460 | } | |
461 | ||
462 | // Result type (guaranteed to also match falseType due to the above) | |
463 | if trueType == ast.TypeUnknown { | |
464 | // falseType may also be unknown, but that's okay because two | |
465 | // unknowns means our result is unknown anyway. | |
466 | v.StackPush(falseType) | |
467 | } else { | |
468 | v.StackPush(trueType) | |
469 | } | |
470 | ||
471 | return tc.n, nil | |
472 | } | |
473 | ||
474 | type typeCheckOutput struct { | |
475 | n *ast.Output | |
476 | } | |
477 | ||
478 | func (tc *typeCheckOutput) TypeCheck(v *TypeCheck) (ast.Node, error) { | |
479 | n := tc.n | |
480 | types := make([]ast.Type, len(n.Exprs)) | |
481 | for i, _ := range n.Exprs { | |
482 | types[len(n.Exprs)-1-i] = v.StackPop() | |
483 | } | |
484 | ||
485 | for _, ty := range types { | |
486 | if ty == ast.TypeUnknown { | |
487 | v.StackPush(ast.TypeUnknown) | |
488 | return tc.n, nil | |
489 | } | |
490 | } | |
491 | ||
492 | // If there is only one argument and it is a list, we evaluate to a list | |
493 | if len(types) == 1 { | |
494 | switch t := types[0]; t { | |
495 | case ast.TypeList: | |
496 | fallthrough | |
497 | case ast.TypeMap: | |
498 | v.StackPush(t) | |
499 | return n, nil | |
500 | } | |
501 | } | |
502 | ||
503 | // Otherwise, all concat args must be strings, so validate that | |
504 | resultType := ast.TypeString | |
505 | for i, t := range types { | |
506 | ||
507 | if t == ast.TypeUnknown { | |
508 | resultType = ast.TypeUnknown | |
509 | continue | |
510 | } | |
511 | ||
512 | if t != ast.TypeString { | |
513 | cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i]) | |
514 | if cn != nil { | |
515 | n.Exprs[i] = cn | |
516 | continue | |
517 | } | |
518 | ||
519 | return nil, fmt.Errorf( | |
520 | "output of an HIL expression must be a string, or a single list (argument %d is %s)", i+1, t) | |
521 | } | |
522 | } | |
523 | ||
524 | // This always results in type string, unless there are unknowns | |
525 | v.StackPush(resultType) | |
526 | ||
527 | return n, nil | |
528 | } | |
529 | ||
530 | type typeCheckLiteral struct { | |
531 | n *ast.LiteralNode | |
532 | } | |
533 | ||
534 | func (tc *typeCheckLiteral) TypeCheck(v *TypeCheck) (ast.Node, error) { | |
535 | v.StackPush(tc.n.Typex) | |
536 | return tc.n, nil | |
537 | } | |
538 | ||
539 | type typeCheckVariableAccess struct { | |
540 | n *ast.VariableAccess | |
541 | } | |
542 | ||
543 | func (tc *typeCheckVariableAccess) TypeCheck(v *TypeCheck) (ast.Node, error) { | |
544 | // Look up the variable in the map | |
545 | variable, ok := v.Scope.LookupVar(tc.n.Name) | |
546 | if !ok { | |
547 | return nil, fmt.Errorf( | |
548 | "unknown variable accessed: %s", tc.n.Name) | |
549 | } | |
550 | ||
551 | // Add the type to the stack | |
552 | v.StackPush(variable.Type) | |
553 | ||
554 | return tc.n, nil | |
555 | } | |
556 | ||
557 | type typeCheckIndex struct { | |
558 | n *ast.Index | |
559 | } | |
560 | ||
561 | func (tc *typeCheckIndex) TypeCheck(v *TypeCheck) (ast.Node, error) { | |
562 | keyType := v.StackPop() | |
563 | targetType := v.StackPop() | |
564 | ||
565 | if keyType == ast.TypeUnknown || targetType == ast.TypeUnknown { | |
566 | v.StackPush(ast.TypeUnknown) | |
567 | return tc.n, nil | |
568 | } | |
569 | ||
570 | // Ensure we have a VariableAccess as the target | |
571 | varAccessNode, ok := tc.n.Target.(*ast.VariableAccess) | |
572 | if !ok { | |
573 | return nil, fmt.Errorf( | |
574 | "target of an index must be a VariableAccess node, was %T", tc.n.Target) | |
575 | } | |
576 | ||
577 | // Get the variable | |
578 | variable, ok := v.Scope.LookupVar(varAccessNode.Name) | |
579 | if !ok { | |
580 | return nil, fmt.Errorf( | |
581 | "unknown variable accessed: %s", varAccessNode.Name) | |
582 | } | |
583 | ||
584 | switch targetType { | |
585 | case ast.TypeList: | |
586 | if keyType != ast.TypeInt { | |
587 | tc.n.Key = v.ImplicitConversion(keyType, ast.TypeInt, tc.n.Key) | |
588 | if tc.n.Key == nil { | |
589 | return nil, fmt.Errorf( | |
590 | "key of an index must be an int, was %s", keyType) | |
591 | } | |
592 | } | |
593 | ||
594 | valType, err := ast.VariableListElementTypesAreHomogenous( | |
595 | varAccessNode.Name, variable.Value.([]ast.Variable)) | |
596 | if err != nil { | |
597 | return tc.n, err | |
598 | } | |
599 | ||
600 | v.StackPush(valType) | |
601 | return tc.n, nil | |
602 | case ast.TypeMap: | |
603 | if keyType != ast.TypeString { | |
604 | tc.n.Key = v.ImplicitConversion(keyType, ast.TypeString, tc.n.Key) | |
605 | if tc.n.Key == nil { | |
606 | return nil, fmt.Errorf( | |
607 | "key of an index must be a string, was %s", keyType) | |
608 | } | |
609 | } | |
610 | ||
611 | valType, err := ast.VariableMapValueTypesAreHomogenous( | |
612 | varAccessNode.Name, variable.Value.(map[string]ast.Variable)) | |
613 | if err != nil { | |
614 | return tc.n, err | |
615 | } | |
616 | ||
617 | v.StackPush(valType) | |
618 | return tc.n, nil | |
619 | default: | |
620 | return nil, fmt.Errorf("invalid index operation into non-indexable type: %s", variable.Type) | |
621 | } | |
622 | } | |
623 | ||
624 | func (v *TypeCheck) ImplicitConversion( | |
625 | actual ast.Type, expected ast.Type, n ast.Node) ast.Node { | |
626 | if v.Implicit == nil { | |
627 | return nil | |
628 | } | |
629 | ||
630 | fromMap, ok := v.Implicit[actual] | |
631 | if !ok { | |
632 | return nil | |
633 | } | |
634 | ||
635 | toFunc, ok := fromMap[expected] | |
636 | if !ok { | |
637 | return nil | |
638 | } | |
639 | ||
640 | return &ast.Call{ | |
641 | Func: toFunc, | |
642 | Args: []ast.Node{n}, | |
643 | Posx: n.Pos(), | |
644 | } | |
645 | } | |
646 | ||
647 | func (v *TypeCheck) reset() { | |
648 | v.Stack = nil | |
649 | v.err = nil | |
650 | } | |
651 | ||
652 | func (v *TypeCheck) StackPush(t ast.Type) { | |
653 | v.Stack = append(v.Stack, t) | |
654 | } | |
655 | ||
656 | func (v *TypeCheck) StackPop() ast.Type { | |
657 | var x ast.Type | |
658 | x, v.Stack = v.Stack[len(v.Stack)-1], v.Stack[:len(v.Stack)-1] | |
659 | return x | |
660 | } | |
661 | ||
662 | func (v *TypeCheck) StackPeek() ast.Type { | |
663 | if len(v.Stack) == 0 { | |
664 | return ast.TypeInvalid | |
665 | } | |
666 | ||
667 | return v.Stack[len(v.Stack)-1] | |
668 | } |