]>
Commit | Line | Data |
---|---|---|
15c0b25d AP |
1 | package cty |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | ) | |
6 | ||
7 | // anyUnknown is a helper to easily check if a set of values contains any | |
8 | // unknowns, for operations that short-circuit to return unknown in that case. | |
9 | func anyUnknown(values ...Value) bool { | |
10 | for _, val := range values { | |
11 | if val.v == unknown { | |
12 | return true | |
13 | } | |
14 | } | |
15 | return false | |
16 | } | |
17 | ||
18 | // typeCheck tests whether all of the given values belong to the given type. | |
19 | // If the given types are a mixture of the given type and the dynamic | |
20 | // pseudo-type then a short-circuit dynamic value is returned. If the given | |
21 | // values are all of the correct type but at least one is unknown then | |
22 | // a short-circuit unknown value is returned. If any other types appear then | |
23 | // an error is returned. Otherwise (finally!) the result is nil, nil. | |
24 | func typeCheck(required Type, ret Type, values ...Value) (shortCircuit *Value, err error) { | |
25 | hasDynamic := false | |
26 | hasUnknown := false | |
27 | ||
28 | for i, val := range values { | |
29 | if val.ty == DynamicPseudoType { | |
30 | hasDynamic = true | |
31 | continue | |
32 | } | |
33 | ||
34 | if !val.Type().Equals(required) { | |
35 | return nil, fmt.Errorf( | |
36 | "type mismatch: want %s but value %d is %s", | |
37 | required.FriendlyName(), | |
38 | i, val.ty.FriendlyName(), | |
39 | ) | |
40 | } | |
41 | ||
42 | if val.v == unknown { | |
43 | hasUnknown = true | |
44 | } | |
45 | } | |
46 | ||
47 | if hasDynamic { | |
48 | return &DynamicVal, nil | |
49 | } | |
50 | ||
51 | if hasUnknown { | |
52 | ret := UnknownVal(ret) | |
53 | return &ret, nil | |
54 | } | |
55 | ||
56 | return nil, nil | |
57 | } | |
58 | ||
59 | // mustTypeCheck is a wrapper around typeCheck that immediately panics if | |
60 | // any error is returned. | |
61 | func mustTypeCheck(required Type, ret Type, values ...Value) *Value { | |
62 | shortCircuit, err := typeCheck(required, ret, values...) | |
63 | if err != nil { | |
64 | panic(err) | |
65 | } | |
66 | return shortCircuit | |
67 | } | |
68 | ||
69 | // shortCircuitForceType takes the return value from mustTypeCheck and | |
70 | // replaces it with an unknown of the given type if the original value was | |
71 | // DynamicVal. | |
72 | // | |
73 | // This is useful for operations that are specified to always return a | |
74 | // particular type, since then a dynamic result can safely be "upgrade" to | |
75 | // a strongly-typed unknown, which then allows subsequent operations to | |
76 | // be actually type-checked. | |
77 | // | |
78 | // It is safe to use this only if the operation in question is defined as | |
79 | // returning either a value of the given type or panicking, since we know | |
80 | // then that subsequent operations won't run if the operation panics. | |
81 | // | |
82 | // If the given short-circuit value is *not* DynamicVal then it must be | |
83 | // of the given type, or this function will panic. | |
84 | func forceShortCircuitType(shortCircuit *Value, ty Type) *Value { | |
85 | if shortCircuit == nil { | |
86 | return nil | |
87 | } | |
88 | ||
89 | if shortCircuit.ty == DynamicPseudoType { | |
90 | ret := UnknownVal(ty) | |
91 | return &ret | |
92 | } | |
93 | ||
94 | if !shortCircuit.ty.Equals(ty) { | |
95 | panic("forceShortCircuitType got value of wrong type") | |
96 | } | |
97 | ||
98 | return shortCircuit | |
99 | } |