@@ -696,64 +696,95 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic
696696 return cty .UnknownVal (resultType ), diags
697697 }
698698 if ! condResult .IsKnown () {
699+ // we use the unmarked values throughout the unknown branch
700+ _ , condResultMarks := condResult .Unmark ()
701+ trueResult , trueResultMarks := trueResult .Unmark ()
702+ falseResult , falseResultMarks := falseResult .Unmark ()
703+
704+ // use a value to merge marks
705+ _ , resMarks := cty .DynamicVal .WithMarks (condResultMarks , trueResultMarks , falseResultMarks ).Unmark ()
706+
707+ trueRange := trueResult .Range ()
708+ falseRange := falseResult .Range ()
709+
710+ // if both branches are known to be null, then the result must still be null
711+ if trueResult .IsNull () && falseResult .IsNull () {
712+ return cty .NullVal (resultType ).WithMarks (resMarks ), diags
713+ }
714+
699715 // We might be able to offer a refined range for the result based on
700716 // the two possible outcomes.
701717 if trueResult .Type () == cty .Number && falseResult .Type () == cty .Number {
702- // This case deals with the common case of (predicate ? 1 : 0) and
703- // significantly decreases the range of the result in that case.
704- if ! (trueResult .IsNull () || falseResult .IsNull ()) {
705- if gt := trueResult .GreaterThan (falseResult ); gt .IsKnown () {
706- b := cty .UnknownVal (cty .Number ).Refine ()
707- if gt .True () {
708- b = b .
709- NumberRangeLowerBound (falseResult , true ).
710- NumberRangeUpperBound (trueResult , true )
711- } else {
712- b = b .
713- NumberRangeLowerBound (trueResult , true ).
714- NumberRangeUpperBound (falseResult , true )
715- }
716- b = b .NotNull () // If neither of the results is null then the result can't be either
717- return b .NewValue ().WithSameMarks (condResult ).WithSameMarks (trueResult ).WithSameMarks (falseResult ), diags
718+ ref := cty .UnknownVal (cty .Number ).Refine ()
719+ if trueRange .DefinitelyNotNull () && falseRange .DefinitelyNotNull () {
720+ ref = ref .NotNull ()
721+ }
722+
723+ falseLo , falseLoInc := falseRange .NumberLowerBound ()
724+ falseHi , falseHiInc := falseRange .NumberUpperBound ()
725+ trueLo , trueLoInc := trueRange .NumberLowerBound ()
726+ trueHi , trueHiInc := trueRange .NumberUpperBound ()
727+
728+ if falseLo .IsKnown () && trueLo .IsKnown () {
729+ lo , loInc := falseLo , falseLoInc
730+ switch {
731+ case trueLo .LessThan (falseLo ).True ():
732+ lo , loInc = trueLo , trueLoInc
733+ case trueLo .Equals (falseLo ).True ():
734+ loInc = trueLoInc || falseLoInc
735+ }
736+
737+ ref = ref .NumberRangeLowerBound (lo , loInc )
738+ }
739+
740+ if falseHi .IsKnown () && trueHi .IsKnown () {
741+ hi , hiInc := falseHi , falseHiInc
742+ switch {
743+ case trueHi .GreaterThan (falseHi ).True ():
744+ hi , hiInc = trueHi , trueHiInc
745+ case trueHi .Equals (falseHi ).True ():
746+ hiInc = trueHiInc || falseHiInc
718747 }
748+ ref = ref .NumberRangeUpperBound (hi , hiInc )
719749 }
750+
751+ return ref .NewValue ().WithMarks (resMarks ), diags
720752 }
753+
721754 if trueResult .Type ().IsCollectionType () && falseResult .Type ().IsCollectionType () {
722755 if trueResult .Type ().Equals (falseResult .Type ()) {
723- if ! (trueResult .IsNull () || falseResult .IsNull ()) {
724- trueLen := trueResult .Length ()
725- falseLen := falseResult .Length ()
726- if gt := trueLen .GreaterThan (falseLen ); gt .IsKnown () {
727- b := cty .UnknownVal (resultType ).Refine ()
728- trueLen , _ := trueLen .AsBigFloat ().Int64 ()
729- falseLen , _ := falseLen .AsBigFloat ().Int64 ()
730- if gt .True () {
731- b = b .
732- CollectionLengthLowerBound (int (falseLen )).
733- CollectionLengthUpperBound (int (trueLen ))
734- } else {
735- b = b .
736- CollectionLengthLowerBound (int (trueLen )).
737- CollectionLengthUpperBound (int (falseLen ))
738- }
739- b = b .NotNull () // If neither of the results is null then the result can't be either
740- return b .NewValue ().WithSameMarks (condResult ).WithSameMarks (trueResult ).WithSameMarks (falseResult ), diags
741- }
756+ ref := cty .UnknownVal (resultType ).Refine ()
757+ if trueRange .DefinitelyNotNull () && falseRange .DefinitelyNotNull () {
758+ ref = ref .NotNull ()
759+ }
760+
761+ falseLo := falseRange .LengthLowerBound ()
762+ falseHi := falseRange .LengthUpperBound ()
763+ trueLo := trueRange .LengthLowerBound ()
764+ trueHi := trueRange .LengthUpperBound ()
765+
766+ lo := falseLo
767+ if trueLo < falseLo {
768+ lo = trueLo
742769 }
770+
771+ hi := falseHi
772+ if trueHi > falseHi {
773+ hi = trueHi
774+ }
775+
776+ ref = ref .CollectionLengthLowerBound (lo ).CollectionLengthUpperBound (hi )
777+ return ref .NewValue ().WithMarks (resMarks ), diags
743778 }
744779 }
745- _ , condResultMarks := condResult .Unmark ()
746- trueResult , trueResultMarks := trueResult .Unmark ()
747- falseResult , falseResultMarks := falseResult .Unmark ()
748780
749- trueRng := trueResult .Range ()
750- falseRng := falseResult .Range ()
751781 ret := cty .UnknownVal (resultType )
752- if trueRng .DefinitelyNotNull () && falseRng .DefinitelyNotNull () {
782+ if trueRange .DefinitelyNotNull () && falseRange .DefinitelyNotNull () {
753783 ret = ret .RefineNotNull ()
754784 }
755- return ret .WithMarks (condResultMarks , trueResultMarks , falseResultMarks ), diags
785+ return ret .WithMarks (resMarks ), diags
756786 }
787+
757788 condResult , err := convert .Convert (condResult , cty .Bool )
758789 if err != nil {
759790 diags = append (diags , & hcl.Diagnostic {
@@ -1244,9 +1275,9 @@ func (e *ObjectConsKeyExpr) UnwrapExpression() Expression {
12441275
12451276// ForExpr represents iteration constructs:
12461277//
1247- // tuple = [for i, v in list: upper(v) if i > 2]
1248- // object = {for k, v in map: k => upper(v)}
1249- // object_of_tuples = {for v in list: v.key: v...}
1278+ // tuple = [for i, v in list: upper(v) if i > 2]
1279+ // object = {for k, v in map: k => upper(v)}
1280+ // object_of_tuples = {for v in list: v.key: v...}
12501281type ForExpr struct {
12511282 KeyVar string // empty if ignoring the key
12521283 ValVar string
@@ -1742,7 +1773,8 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
17421773 if ty .IsListType () && sourceVal .Type ().IsCollectionType () {
17431774 // We can refine the length of an unknown list result based on
17441775 // the source collection's own length.
1745- sourceRng := sourceVal .Range ()
1776+ sv , _ := sourceVal .Unmark ()
1777+ sourceRng := sv .Range ()
17461778 ret = ret .Refine ().
17471779 CollectionLengthLowerBound (sourceRng .LengthLowerBound ()).
17481780 CollectionLengthUpperBound (sourceRng .LengthUpperBound ()).
0 commit comments