@@ -696,68 +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
718735 }
736+
737+ ref = ref .NumberRangeLowerBound (lo , loInc )
719738 }
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
747+ }
748+ ref = ref .NumberRangeUpperBound (hi , hiInc )
749+ }
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- // the bounds are not part of the final result value, so
725- // the marks are not needed
726- tr , _ := trueResult .Unmark ()
727- fr , _ := falseResult .Unmark ()
728- trueRange := tr .Range ()
729- falseRange := fr .Range ()
730-
731- if gt := trueResult .Length ().GreaterThan (falseResult .Length ()); gt .IsKnown () {
732- gt , _ := gt .Unmark ()
733- b := cty .UnknownVal (resultType ).Refine ()
734- if gt .True () {
735- b = b .
736- CollectionLengthLowerBound (falseRange .LengthLowerBound ()).
737- CollectionLengthUpperBound (trueRange .LengthUpperBound ())
738- } else {
739- b = b .
740- CollectionLengthLowerBound (trueRange .LengthLowerBound ()).
741- CollectionLengthUpperBound (falseRange .LengthUpperBound ())
742- }
743- b = b .NotNull () // If neither of the results is null then the result can't be either
744- return b .NewValue ().WithSameMarks (condResult ).WithSameMarks (trueResult ).WithSameMarks (falseResult ), diags
745- }
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
746769 }
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
747778 }
748779 }
749- _ , condResultMarks := condResult .Unmark ()
750- trueResult , trueResultMarks := trueResult .Unmark ()
751- falseResult , falseResultMarks := falseResult .Unmark ()
752780
753- trueRng := trueResult .Range ()
754- falseRng := falseResult .Range ()
755781 ret := cty .UnknownVal (resultType )
756- if trueRng .DefinitelyNotNull () && falseRng .DefinitelyNotNull () {
782+ if trueRange .DefinitelyNotNull () && falseRange .DefinitelyNotNull () {
757783 ret = ret .RefineNotNull ()
758784 }
759- return ret .WithMarks (condResultMarks , trueResultMarks , falseResultMarks ), diags
785+ return ret .WithMarks (resMarks ), diags
760786 }
787+
761788 condResult , err := convert .Convert (condResult , cty .Bool )
762789 if err != nil {
763790 diags = append (diags , & hcl.Diagnostic {
0 commit comments