diff --git a/cty/convert/conversion_collection.go b/cty/convert/conversion_collection.go index ea23bf61..dc8dcb92 100644 --- a/cty/convert/conversion_collection.go +++ b/cty/convert/conversion_collection.go @@ -265,6 +265,7 @@ func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) co // element conversions in elemConvs return func(val cty.Value, path cty.Path) (cty.Value, error) { elems := make([]cty.Value, 0, len(elemConvs)) + elemTys := make([]cty.Type, 0, len(elems)) elemPath := append(path.Copy(), nil) i := int64(0) it := val.ElementIterator() @@ -284,10 +285,15 @@ func conversionTupleToList(tupleType cty.Type, listEty cty.Type, unsafe bool) co } } elems = append(elems, val) + elemTys = append(elemTys, val.Type()) i++ } + elems, err := conversionUnifyListElements(elems, elemPath, unsafe) + if err != nil { + return cty.NilVal, err + } return cty.ListVal(elems), nil } } @@ -441,6 +447,7 @@ func conversionUnifyCollectionElements(elems map[string]cty.Value, path cty.Path } unifiedType, _ := unify(elemTypes, unsafe) if unifiedType == cty.NilType { + return nil, path.NewErrorf("collection elements cannot be unified") } unifiedElems := make(map[string]cty.Value) @@ -486,3 +493,37 @@ func conversionCheckMapElementTypes(elems map[string]cty.Value, path cty.Path) e return nil } + +func conversionUnifyListElements(elems []cty.Value, path cty.Path, unsafe bool) ([]cty.Value, error) { + elemTypes := make([]cty.Type, len(elems)) + for i, elem := range elems { + elemTypes[i] = elem.Type() + } + unifiedType, _ := unify(elemTypes, unsafe) + if unifiedType == cty.NilType { + return nil, path.NewErrorf("collection elements cannot be unified") + } + + ret := make([]cty.Value, len(elems)) + elemPath := append(path.Copy(), nil) + + for i, elem := range elems { + if elem.Type().Equals(unifiedType) { + ret[i] = elem + continue + } + conv := getConversion(elem.Type(), unifiedType, unsafe) + if conv == nil { + } + elemPath[len(elemPath)-1] = cty.IndexStep{ + Key: cty.NumberIntVal(int64(i)), + } + val, err := conv(elem, elemPath) + if err != nil { + return nil, err + } + ret[i] = val + } + + return ret, nil +} diff --git a/cty/convert/public_test.go b/cty/convert/public_test.go index 4300aefa..e085ad4a 100644 --- a/cty/convert/public_test.go +++ b/cty/convert/public_test.go @@ -649,6 +649,34 @@ func TestConvert(t *testing.T) { "b": cty.MapValEmpty(cty.String), }), }, + // https://github.com/hashicorp/terraform/issues/21588: + { + Value: cty.TupleVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "a": cty.EmptyObjectVal, + "b": cty.NumberIntVal(2), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{"var1": cty.StringVal("val1")}), + "b": cty.StringVal("2"), + }), + }), + Type: cty.List(cty.Object(map[string]cty.Type{ + "a": cty.DynamicPseudoType, + "b": cty.String, + })), + Want: cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapValEmpty(cty.String), + "b": cty.StringVal("2"), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.MapVal(map[string]cty.Value{"var1": cty.StringVal("val1")}), + "b": cty.StringVal("2"), + }), + }), + WantError: false, + }, } for _, test := range tests {