@@ -72,18 +72,35 @@ func try(args []cty.Value) (cty.Value, error) {
7272 var diags hcl.Diagnostics
7373 for _ , arg := range args {
7474 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- }
8175
8276 v , moreDiags := closure .Value ()
8377 diags = append (diags , moreDiags ... )
78+
8479 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
8684 }
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+
87104 return v , nil // ignore any accumulated diagnostics if one succeeds
88105 }
89106
@@ -111,43 +128,17 @@ func try(args []cty.Value) (cty.Value, error) {
111128
112129func can (arg cty.Value ) (cty.Value , error ) {
113130 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 ()
120132 if diags .HasErrors () {
121133 return cty .False , nil
122134 }
123- return cty .True , nil
124- }
125135
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
151141 }
152- return false
142+
143+ return cty .True , nil
153144}
0 commit comments