[codegen/pcl] Allocate fewer type instances. (#7548)
When converting a `schema.InputType` to a `model.Type`, calculate the resolved form of the type in the schema type system rather than the model type system. The results are semantically identical, but the number of type objects that are allocated is much smaller b/c `model.NewOutputType` no longer allocates. This deserves a little more explanation. In order to prevent nested outputs and/or promises, `model.NewOutputType` calculates the resolved form of its argument prior to allocating a new `OutputType` value. Calculating the resolved form of the argument is a no-op if the argument is already fully resolved. Therefore, passing in a fully-resolved schema type prevents `model.NewOutputType` from calulating the resolved form, and `model.NewOutputType` will only allocate the `OutputType` itself instead of the `OutputType` and the resolved form of any eventuals present in its argument. This has a _very important_ knock-on benefit: the schema -> model type translator ensures that given a `schema.Type` instance `T` it will always return the same `model.Type` instance `U`. This termendously speeds up type equality checks for complex types, as they will now be referentially identical. This change alone gives a significant speedup in azure-native code generation.
This commit is contained in:
parent
c6062ea1d5
commit
80d35758d5
|
@ -47,7 +47,7 @@ type binder struct {
|
||||||
options bindOptions
|
options bindOptions
|
||||||
|
|
||||||
referencedPackages map[string]*schema.Package
|
referencedPackages map[string]*schema.Package
|
||||||
typeSchemas map[model.Type]schema.Type
|
schemaTypes map[schema.Type]model.Type
|
||||||
|
|
||||||
tokens syntax.TokenMap
|
tokens syntax.TokenMap
|
||||||
nodes []Node
|
nodes []Node
|
||||||
|
@ -117,7 +117,7 @@ func BindProgram(files []*syntax.File, opts ...BindOption) (*Program, hcl.Diagno
|
||||||
options: options,
|
options: options,
|
||||||
tokens: syntax.NewTokenMapForFiles(files),
|
tokens: syntax.NewTokenMapForFiles(files),
|
||||||
referencedPackages: map[string]*schema.Package{},
|
referencedPackages: map[string]*schema.Package{},
|
||||||
typeSchemas: map[model.Type]schema.Type{},
|
schemaTypes: map[schema.Type]model.Type{},
|
||||||
root: model.NewRootScope(syntax.None),
|
root: model.NewRootScope(syntax.None),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,34 +146,29 @@ func (b *binder) loadReferencedPackageSchemas(n Node) error {
|
||||||
|
|
||||||
// schemaTypeToType converts a schema.Type to a model Type.
|
// schemaTypeToType converts a schema.Type to a model Type.
|
||||||
func (b *binder) schemaTypeToType(src schema.Type) (result model.Type) {
|
func (b *binder) schemaTypeToType(src schema.Type) (result model.Type) {
|
||||||
return b.schemaTypeToTypeImpl(src, map[schema.Type]model.Type{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *binder) schemaTypeToTypeImpl(src schema.Type, seen map[schema.Type]model.Type) (result model.Type) {
|
|
||||||
defer func() {
|
|
||||||
b.typeSchemas[result] = src
|
|
||||||
}()
|
|
||||||
|
|
||||||
if already, ok := seen[src]; ok {
|
|
||||||
return already
|
|
||||||
}
|
|
||||||
|
|
||||||
switch src := src.(type) {
|
switch src := src.(type) {
|
||||||
case *schema.ArrayType:
|
case *schema.ArrayType:
|
||||||
return model.NewListType(b.schemaTypeToTypeImpl(src.ElementType, seen))
|
return model.NewListType(b.schemaTypeToType(src.ElementType))
|
||||||
case *schema.MapType:
|
case *schema.MapType:
|
||||||
return model.NewMapType(b.schemaTypeToTypeImpl(src.ElementType, seen))
|
return model.NewMapType(b.schemaTypeToType(src.ElementType))
|
||||||
|
case *schema.EnumType:
|
||||||
|
// TODO(codegen): make this a union of constant types.
|
||||||
|
return b.schemaTypeToType(src.ElementType)
|
||||||
case *schema.ObjectType:
|
case *schema.ObjectType:
|
||||||
|
if t, ok := b.schemaTypes[src]; ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
properties := map[string]model.Type{}
|
properties := map[string]model.Type{}
|
||||||
objType := model.NewObjectType(properties, src)
|
objType := model.NewObjectType(properties, src)
|
||||||
seen[src] = objType
|
b.schemaTypes[src] = objType
|
||||||
for _, prop := range src.Properties {
|
for _, prop := range src.Properties {
|
||||||
typ := prop.Type
|
typ := prop.Type
|
||||||
if b.options.allowMissingProperties {
|
if b.options.allowMissingProperties {
|
||||||
typ = &schema.OptionalType{ElementType: typ}
|
typ = &schema.OptionalType{ElementType: typ}
|
||||||
}
|
}
|
||||||
|
|
||||||
t := b.schemaTypeToTypeImpl(typ, seen)
|
t := b.schemaTypeToType(typ)
|
||||||
if prop.ConstValue != nil {
|
if prop.ConstValue != nil {
|
||||||
var value cty.Value
|
var value cty.Value
|
||||||
switch v := prop.ConstValue.(type) {
|
switch v := prop.ConstValue.(type) {
|
||||||
|
@ -200,20 +195,21 @@ func (b *binder) schemaTypeToTypeImpl(src schema.Type, seen map[schema.Type]mode
|
||||||
}
|
}
|
||||||
|
|
||||||
if src.UnderlyingType != nil {
|
if src.UnderlyingType != nil {
|
||||||
underlyingType := b.schemaTypeToTypeImpl(src.UnderlyingType, seen)
|
underlyingType := b.schemaTypeToType(src.UnderlyingType)
|
||||||
return model.NewUnionType(t, underlyingType)
|
return model.NewUnionType(t, underlyingType)
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
case *schema.InputType:
|
case *schema.InputType:
|
||||||
elementType := b.schemaTypeToTypeImpl(src.ElementType, seen)
|
elementType := b.schemaTypeToType(src.ElementType)
|
||||||
return model.NewUnionTypeAnnotated([]model.Type{elementType, model.NewOutputType(elementType)}, src)
|
resolvedElementType := b.schemaTypeToType(codegen.ResolvedType(src.ElementType))
|
||||||
|
return model.NewUnionTypeAnnotated([]model.Type{elementType, model.NewOutputType(resolvedElementType)}, src)
|
||||||
case *schema.OptionalType:
|
case *schema.OptionalType:
|
||||||
elementType := b.schemaTypeToTypeImpl(src.ElementType, seen)
|
elementType := b.schemaTypeToType(src.ElementType)
|
||||||
return model.NewOptionalType(elementType)
|
return model.NewOptionalType(elementType)
|
||||||
case *schema.UnionType:
|
case *schema.UnionType:
|
||||||
types := make([]model.Type, len(src.ElementTypes))
|
types := make([]model.Type, len(src.ElementTypes))
|
||||||
for i, src := range src.ElementTypes {
|
for i, src := range src.ElementTypes {
|
||||||
types[i] = b.schemaTypeToTypeImpl(src, seen)
|
types[i] = b.schemaTypeToType(src)
|
||||||
}
|
}
|
||||||
if src.Discriminator != "" {
|
if src.Discriminator != "" {
|
||||||
return model.NewUnionTypeAnnotated(types, src)
|
return model.NewUnionTypeAnnotated(types, src)
|
||||||
|
|
Loading…
Reference in a new issue