@@ -72,18 +72,35 @@ func try(args []cty.Value) (cty.Value, error) {
72
72
var diags hcl.Diagnostics
73
73
for _ , arg := range args {
74
74
closure := customdecode .ExpressionClosureFromVal (arg )
75
- if dependsOnUnknowns (closure .Expression , closure .EvalContext ) {
76
- // We can't safely decide if this expression will succeed yet,
77
- // and so our entire result must be unknown until we have
78
- // more information.
79
- return cty .DynamicVal , nil
80
- }
81
75
82
76
v , moreDiags := closure .Value ()
83
77
diags = append (diags , moreDiags ... )
78
+
84
79
if moreDiags .HasErrors () {
85
- continue // try the next one, if there is one to try
80
+ // If there's an error we know it will always fail and can
81
+ // continue. A more refined value will not remove an error from
82
+ // the expression.
83
+ continue
86
84
}
85
+
86
+ if ! v .IsWhollyKnown () {
87
+ // If there are any unknowns in the value at all, we cannot be
88
+ // certain that the final value will be consistent or have the same
89
+ // type, so wee need to be conservative and return a dynamic value.
90
+
91
+ // There are two different classes of failure that can happen when
92
+ // an expression transitions from unknown to known; an operation on
93
+ // a dynamic value becomes invalid for the type once the type is
94
+ // known, or an index expression on a collection fails once the
95
+ // collection value is known. These changes from a
96
+ // valid-partially-unknown expression to an invalid-known
97
+ // expression can produce inconsistent results by changing which
98
+ // "try" argument is returned, which may be a collection with
99
+ // different previously known values, or a different type entirely
100
+ // ("try" does not require consistent argument types)
101
+ return cty .DynamicVal , nil
102
+ }
103
+
87
104
return v , nil // ignore any accumulated diagnostics if one succeeds
88
105
}
89
106
@@ -111,43 +128,17 @@ func try(args []cty.Value) (cty.Value, error) {
111
128
112
129
func can (arg cty.Value ) (cty.Value , error ) {
113
130
closure := customdecode .ExpressionClosureFromVal (arg )
114
- if dependsOnUnknowns (closure .Expression , closure .EvalContext ) {
115
- // Can't decide yet, then.
116
- return cty .UnknownVal (cty .Bool ), nil
117
- }
118
-
119
- _ , diags := closure .Value ()
131
+ v , diags := closure .Value ()
120
132
if diags .HasErrors () {
121
133
return cty .False , nil
122
134
}
123
- return cty .True , nil
124
- }
125
135
126
- // dependsOnUnknowns returns true if any of the variables that the given
127
- // expression might access are unknown values or contain unknown values.
128
- //
129
- // This is a conservative result that prefers to return true if there's any
130
- // chance that the expression might derive from an unknown value during its
131
- // evaluation; it is likely to produce false-positives for more complex
132
- // expressions involving deep data structures.
133
- func dependsOnUnknowns (expr hcl.Expression , ctx * hcl.EvalContext ) bool {
134
- for _ , traversal := range expr .Variables () {
135
- val , diags := traversal .TraverseAbs (ctx )
136
- if diags .HasErrors () {
137
- // If the traversal returned a definitive error then it must
138
- // not traverse through any unknowns.
139
- continue
140
- }
141
- if ! val .IsWhollyKnown () {
142
- // The value will be unknown if either it refers directly to
143
- // an unknown value or if the traversal moves through an unknown
144
- // collection. We're using IsWhollyKnown, so this also catches
145
- // situations where the traversal refers to a compound data
146
- // structure that contains any unknown values. That's important,
147
- // because during evaluation the expression might evaluate more
148
- // deeply into this structure and encounter the unknowns.
149
- return true
150
- }
136
+ if ! v .IsWhollyKnown () {
137
+ // If the value is not wholly known, we still cannot be certain that
138
+ // the expression was valid. There may be yet index expressions which
139
+ // will fail once values are completely known.
140
+ return cty .UnknownVal (cty .Bool ), nil
151
141
}
152
- return false
142
+
143
+ return cty .True , nil
153
144
}
0 commit comments