From af387713991b11d8463b7a3330157d5413abcbde Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sat, 10 Oct 2026 22:43:33 -0700 Subject: [PATCH] Compact checker type metadata Each type carries a self-referential interface solely to recover its concrete representation. That costs space in every type and adds GC pointer work even though the representation is fixed at construction. A compact concrete-kind tag saves 16 bytes per type on 64-bit hosts. Keep it separate from mutable semantic flags so casts stay valid as checking discovers more information. --- tsc/internal/checker/checker.go | 58 +++++---- tsc/internal/checker/checker_test.go | 57 +++++++++ tsc/internal/checker/tracer.go | 5 +- tsc/internal/checker/types.go | 181 +++++++++++++++++++++------ tsc/internal/checker/types_test.go | 75 +++++++++++ 5 files changed, 311 insertions(+), 65 deletions(-) create mode 100644 tsc/internal/checker/types_test.go diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index b03cebf741b22..37f2c0c7fb65a 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -25539,14 +25539,13 @@ func isUnaryTupleTypeNode(node *ast.Node) bool { return ast.IsTupleTypeNode(node) && len(node.Elements()) == 1 } -func (c *Checker) newType(flags TypeFlags, objectFlags ObjectFlags, data TypeData) *Type { +func (c *Checker) newType(flags TypeFlags, objectFlags ObjectFlags, t *Type, kind typeKind) *Type { c.TypeCount++ - t := data.AsType() t.flags = flags t.objectFlags = objectFlags &^ (ObjectFlagsCouldContainTypeVariablesComputed | ObjectFlagsCouldContainTypeVariables | ObjectFlagsMembersResolved) t.id = TypeId(c.TypeCount) + t.kind = kind t.checker = c - t.data = data if c.tracer != nil { c.tracer.RecordType(t) } @@ -25560,7 +25559,7 @@ func (c *Checker) newIntrinsicType(flags TypeFlags, intrinsicName string) *Type func (c *Checker) newIntrinsicTypeEx(flags TypeFlags, intrinsicName string, objectFlags ObjectFlags) *Type { data := &IntrinsicType{} data.intrinsicName = intrinsicName - return c.newType(flags, objectFlags, data) + return c.newType(flags, objectFlags, data.AsType(), typeKindIntrinsic) } func (c *Checker) createWideningType(nonWideningType *Type) *Type { @@ -25582,7 +25581,7 @@ func (c *Checker) createUnknownUnionType() *Type { func (c *Checker) newLiteralType(flags TypeFlags, value any, regularType *Type) *Type { data := &LiteralType{} data.value = value - t := c.newType(flags, ObjectFlagsNone, data) + t := c.newType(flags, ObjectFlagsNone, data.AsType(), typeKindLiteral) if regularType != nil { data.regularType = regularType } else { @@ -25594,34 +25593,43 @@ func (c *Checker) newLiteralType(flags TypeFlags, value any, regularType *Type) func (c *Checker) newUniqueESSymbolType(symbol *ast.Symbol, name string) *Type { data := &UniqueESSymbolType{} data.name = name - t := c.newType(TypeFlagsUniqueESSymbol, ObjectFlagsNone, data) + t := c.newType(TypeFlagsUniqueESSymbol, ObjectFlagsNone, data.AsType(), typeKindUniqueESSymbol) t.symbol = symbol return t } func (c *Checker) newObjectType(objectFlags ObjectFlags, symbol *ast.Symbol) *Type { - var data TypeData + var t *Type + var kind typeKind switch { case objectFlags&ObjectFlagsClassOrInterface != 0: - data = &InterfaceType{} + t = (&InterfaceType{}).AsType() + kind = typeKindInterface case objectFlags&ObjectFlagsTuple != 0: - data = &TupleType{} + t = (&TupleType{}).AsType() + kind = typeKindTuple case objectFlags&ObjectFlagsReference != 0: - data = &TypeReference{} + t = (&TypeReference{}).AsType() + kind = typeKindTypeReference case objectFlags&ObjectFlagsMapped != 0: - data = &MappedType{} + t = (&MappedType{}).AsType() + kind = typeKindMapped case objectFlags&ObjectFlagsReverseMapped != 0: - data = &ReverseMappedType{} + t = (&ReverseMappedType{}).AsType() + kind = typeKindReverseMapped case objectFlags&ObjectFlagsEvolvingArray != 0: - data = &EvolvingArrayType{} + t = (&EvolvingArrayType{}).AsType() + kind = typeKindEvolvingArray case objectFlags&ObjectFlagsInstantiationExpressionType != 0: - data = &InstantiationExpressionType{} + t = (&InstantiationExpressionType{}).AsType() + kind = typeKindInstantiationExpression case objectFlags&ObjectFlagsAnonymous != 0: - data = &ObjectType{} + t = (&ObjectType{}).AsType() + kind = typeKindObject default: panic("Unhandled case in newObjectType") } - t := c.newType(TypeFlagsObject, objectFlags, data) + c.newType(TypeFlagsObject, objectFlags, t, kind) t.symbol = symbol return t } @@ -25705,7 +25713,7 @@ func (c *Checker) setStructuredTypeMembers(t *Type, members ast.SymbolTable, cal } func (c *Checker) newTypeParameter(symbol *ast.Symbol) *Type { - t := c.newType(TypeFlagsTypeParameter, ObjectFlagsNone, &TypeParameter{}) + t := c.newType(TypeFlagsTypeParameter, ObjectFlagsNone, (&TypeParameter{}).AsType(), typeKindTypeParameter) t.symbol = symbol return t } @@ -25727,13 +25735,13 @@ func (c *Checker) getPropagatingFlagsOfTypes(types []*Type, excludeKinds TypeFla func (c *Checker) newUnionType(objectFlags ObjectFlags, types []*Type) *Type { data := &UnionType{} data.types = types - return c.newType(TypeFlagsUnion, objectFlags, data) + return c.newType(TypeFlagsUnion, objectFlags, data.AsType(), typeKindUnion) } func (c *Checker) newIntersectionType(objectFlags ObjectFlags, types []*Type) *Type { data := &IntersectionType{} data.types = types - return c.newType(TypeFlagsIntersection, objectFlags, data) + return c.newType(TypeFlagsIntersection, objectFlags, data.AsType(), typeKindIntersection) } func (c *Checker) newIndexedAccessType(objectType *Type, indexType *Type, accessFlags AccessFlags) *Type { @@ -25741,27 +25749,27 @@ func (c *Checker) newIndexedAccessType(objectType *Type, indexType *Type, access data.objectType = objectType data.indexType = indexType data.accessFlags = accessFlags - return c.newType(TypeFlagsIndexedAccess, ObjectFlagsNone, data) + return c.newType(TypeFlagsIndexedAccess, ObjectFlagsNone, data.AsType(), typeKindIndexedAccess) } func (c *Checker) newIndexType(target *Type, indexFlags IndexFlags) *Type { data := &IndexType{} data.target = target data.indexFlags = indexFlags - return c.newType(TypeFlagsIndex, ObjectFlagsNone, data) + return c.newType(TypeFlagsIndex, ObjectFlagsNone, data.AsType(), typeKindIndex) } func (c *Checker) newTemplateLiteralType(texts []string, types []*Type) *Type { data := &TemplateLiteralType{} data.texts = texts data.types = types - return c.newType(TypeFlagsTemplateLiteral, ObjectFlagsNone, data) + return c.newType(TypeFlagsTemplateLiteral, ObjectFlagsNone, data.AsType(), typeKindTemplateLiteral) } func (c *Checker) newStringMappingType(symbol *ast.Symbol, target *Type) *Type { data := &StringMappingType{} data.target = target - t := c.newType(TypeFlagsStringMapping, ObjectFlagsNone, data) + t := c.newType(TypeFlagsStringMapping, ObjectFlagsNone, data.AsType(), typeKindStringMapping) t.symbol = symbol return t } @@ -25773,14 +25781,14 @@ func (c *Checker) newConditionalType(root *ConditionalRoot, mapper *TypeMapper, data.extendsType = c.instantiateType(root.extendsType, mapper) data.mapper = mapper data.combinedMapper = combinedMapper - return c.newType(TypeFlagsConditional, ObjectFlagsNone, data) + return c.newType(TypeFlagsConditional, ObjectFlagsNone, data.AsType(), typeKindConditional) } func (c *Checker) newSubstitutionType(baseType *Type, constraint *Type) *Type { data := &SubstitutionType{} data.baseType = baseType data.constraint = constraint - return c.newType(TypeFlagsSubstitution, ObjectFlagsNone, data) + return c.newType(TypeFlagsSubstitution, ObjectFlagsNone, data.AsType(), typeKindSubstitution) } func (c *Checker) newSignature(flags SignatureFlags, declaration *ast.Node, typeParameters []*Type, thisParameter *ast.Symbol, parameters []*ast.Symbol, resolvedReturnType *Type, resolvedTypePredicate *TypePredicate, minArgumentCount int) *Signature { diff --git a/tsc/internal/checker/checker_test.go b/tsc/internal/checker/checker_test.go index 2ed189904d64b..0ca836c03b1ea 100644 --- a/tsc/internal/checker/checker_test.go +++ b/tsc/internal/checker/checker_test.go @@ -1,7 +1,9 @@ package checker_test import ( + "fmt" "path/filepath" + "strings" "testing" "github.com/microsoft/TypeScript/tsc/internal/ast" @@ -131,3 +133,58 @@ func BenchmarkNewChecker(b *testing.B) { checker.NewChecker(program, nil) } } + +func BenchmarkCheck(b *testing.B) { + fs := bundled.WrapFS(osvfs.FS()) + rootPath := tspath.RootedDirectoryPathFromAbsolute(filepath.Join(repo.TestDataPath(), "fixtures/compiler")) + host := compiler.NewCompilerHost(fs, bundled.LibPath(), nil, nil, nil) + parsed, errors := tsoptions.GetParsedCommandLineOfConfigFile(rootPath.ResolveFile("tsconfig.json"), &core.CompilerOptions{}, nil, fs, nil) + assert.Equal(b, len(errors), 0, "Expected no errors in parsed command line") + program := compiler.NewProgram(compiler.ProgramOptions{ + Config: parsed, + Host: host, + }) + program.BindSourceFiles() + ctx := b.Context() + + b.ReportAllocs() + for b.Loop() { + c, _ := checker.NewChecker(program, nil) + for _, file := range program.GetSourceFiles() { + c.GetDiagnostics(ctx, file) + } + c.GetGlobalDiagnostics() + } +} + +func TestTypeAllocationAcrossCheckerGrowth(t *testing.T) { + t.Parallel() + var content strings.Builder + content.WriteString(`interface Box { value: T } +export type Initial = Box<"first">; +`) + for i := range 1024 { + fmt.Fprintf(&content, "export type T%d = Box<\"value%d\">;\n", i, i) + } + fs := bundled.WrapFS(vfstest.FromMap(map[string]string{ + "/foo.ts": content.String(), + }, tspath.CaseSensitive)) + opts := &core.CompilerOptions{NoLib: core.TSTrue} + program := compiler.NewProgram(compiler.ProgramOptions{ + Config: tsoptions.NewParsedCommandLine(opts, []tspath.RootedFilePath{"/foo.ts"}, nil, "/", fs.CaseSensitivity()), + Host: compiler.NewCompilerHost(fs, bundled.LibPath(), nil, nil, nil), + }) + program.BindSourceFiles() + c, done := program.GetTypeChecker(t.Context()) + defer done() + file := program.GetSourceFile("/foo.ts") + initial := c.GetTypeAtLocation(file.Statements.Nodes[1].AsTypeAliasDeclaration().Type) + for i := range 1024 { + node := file.Statements.Nodes[i+2].AsTypeAliasDeclaration().Type + typ := c.GetTypeAtLocation(node) + assert.Equal(t, c.TypeToString(typ), fmt.Sprintf("T%d", i)) + } + assert.Equal(t, c.GetTypeAtLocation(file.Statements.Nodes[1].AsTypeAliasDeclaration().Type), initial) + assert.Equal(t, c.TypeToString(initial), "Initial") + assert.Equal(t, len(c.GetDiagnostics(t.Context(), file)), 0) +} diff --git a/tsc/internal/checker/tracer.go b/tsc/internal/checker/tracer.go index 0fd4db6937244..0fea57f8d4d70 100644 --- a/tsc/internal/checker/tracer.go +++ b/tsc/internal/checker/tracer.go @@ -112,11 +112,10 @@ func (a *tracedTypeAdapter) IntrinsicName() string { if a.t.flags&TypeFlagsIntrinsic == 0 { return "" } - data, ok := a.t.data.(*IntrinsicType) - if !ok { + if a.t.kind != typeKindIntrinsic { return "" } - return data.intrinsicName + return a.t.AsIntrinsicType().intrinsicName } func (a *tracedTypeAdapter) UnionTypes() []tracing.TracedType { diff --git a/tsc/internal/checker/types.go b/tsc/internal/checker/types.go index 7e4748930262c..a5557f81dd2eb 100644 --- a/tsc/internal/checker/types.go +++ b/tsc/internal/checker/types.go @@ -4,6 +4,7 @@ import ( "math/bits" "slices" "strings" + "unsafe" "github.com/microsoft/TypeScript/tsc/internal/ast" "github.com/microsoft/TypeScript/tsc/internal/collections" @@ -681,14 +682,73 @@ func (a *TypeAlias) TypeArguments() []*Type { // Type +// The concrete kind is separate from mutable semantic flags. All concrete types +// embed Type at offset zero, so a tag replaces the self-referential interface. +// The ordering groups types by their embedded bases for the base casts below. +type typeKind uint8 + +const ( + typeKindIntrinsic typeKind = iota + 1 + typeKindLiteral + typeKindUniqueESSymbol + typeKindTypeParameter + typeKindIndex + typeKindIndexedAccess + typeKindTemplateLiteral + typeKindStringMapping + typeKindSubstitution + typeKindConditional + typeKindObject + typeKindTypeReference + typeKindInterface + typeKindTuple + typeKindInstantiationExpression + typeKindMapped + typeKindReverseMapped + typeKindEvolvingArray + typeKindUnion + typeKindIntersection +) + type Type struct { flags TypeFlags objectFlags ObjectFlags id TypeId + kind typeKind symbol *ast.Symbol alias *TypeAlias checker *Checker - data TypeData // Type specific data +} + +// Enforce the layout required by the pointer conversions, on every architecture. +var ( + _ [0 - unsafe.Offsetof(IntrinsicType{}.Type)]byte + _ [0 - unsafe.Offsetof(LiteralType{}.Type)]byte + _ [0 - unsafe.Offsetof(UniqueESSymbolType{}.Type)]byte + _ [0 - unsafe.Offsetof(TypeParameter{}.Type)]byte + _ [0 - unsafe.Offsetof(IndexType{}.Type)]byte + _ [0 - unsafe.Offsetof(IndexedAccessType{}.Type)]byte + _ [0 - unsafe.Offsetof(TemplateLiteralType{}.Type)]byte + _ [0 - unsafe.Offsetof(StringMappingType{}.Type)]byte + _ [0 - unsafe.Offsetof(SubstitutionType{}.Type)]byte + _ [0 - unsafe.Offsetof(ConditionalType{}.Type)]byte + _ [0 - unsafe.Offsetof(ObjectType{}.Type)]byte + _ [0 - unsafe.Offsetof(TypeReference{}.Type)]byte + _ [0 - unsafe.Offsetof(InterfaceType{}.Type)]byte + _ [0 - unsafe.Offsetof(TupleType{}.Type)]byte + _ [0 - unsafe.Offsetof(InstantiationExpressionType{}.Type)]byte + _ [0 - unsafe.Offsetof(MappedType{}.Type)]byte + _ [0 - unsafe.Offsetof(ReverseMappedType{}.Type)]byte + _ [0 - unsafe.Offsetof(EvolvingArrayType{}.Type)]byte + _ [0 - unsafe.Offsetof(UnionType{}.Type)]byte + _ [0 - unsafe.Offsetof(IntersectionType{}.Type)]byte +) + +func castType[T any](t *Type, kind typeKind) *T { + if t.kind != kind { + panic("Invalid type cast") + } + return (*T)(unsafe.Pointer(t)) } func (t *Type) Id() TypeId { @@ -705,35 +765,94 @@ func (t *Type) ObjectFlags() ObjectFlags { // Casts for concrete struct types -func (t *Type) AsIntrinsicType() *IntrinsicType { return t.data.(*IntrinsicType) } -func (t *Type) AsLiteralType() *LiteralType { return t.data.(*LiteralType) } -func (t *Type) AsUniqueESSymbolType() *UniqueESSymbolType { return t.data.(*UniqueESSymbolType) } -func (t *Type) AsTupleType() *TupleType { return t.data.(*TupleType) } +func (t *Type) AsIntrinsicType() *IntrinsicType { return castType[IntrinsicType](t, typeKindIntrinsic) } +func (t *Type) AsLiteralType() *LiteralType { return castType[LiteralType](t, typeKindLiteral) } +func (t *Type) AsUniqueESSymbolType() *UniqueESSymbolType { + return castType[UniqueESSymbolType](t, typeKindUniqueESSymbol) +} +func (t *Type) AsTupleType() *TupleType { return castType[TupleType](t, typeKindTuple) } func (t *Type) AsInstantiationExpressionType() *InstantiationExpressionType { - return t.data.(*InstantiationExpressionType) -} -func (t *Type) AsMappedType() *MappedType { return t.data.(*MappedType) } -func (t *Type) AsReverseMappedType() *ReverseMappedType { return t.data.(*ReverseMappedType) } -func (t *Type) AsEvolvingArrayType() *EvolvingArrayType { return t.data.(*EvolvingArrayType) } -func (t *Type) AsTypeParameter() *TypeParameter { return t.data.(*TypeParameter) } -func (t *Type) AsUnionType() *UnionType { return t.data.(*UnionType) } -func (t *Type) AsIntersectionType() *IntersectionType { return t.data.(*IntersectionType) } -func (t *Type) AsIndexType() *IndexType { return t.data.(*IndexType) } -func (t *Type) AsIndexedAccessType() *IndexedAccessType { return t.data.(*IndexedAccessType) } -func (t *Type) AsTemplateLiteralType() *TemplateLiteralType { return t.data.(*TemplateLiteralType) } -func (t *Type) AsStringMappingType() *StringMappingType { return t.data.(*StringMappingType) } -func (t *Type) AsSubstitutionType() *SubstitutionType { return t.data.(*SubstitutionType) } -func (t *Type) AsConditionalType() *ConditionalType { return t.data.(*ConditionalType) } + return castType[InstantiationExpressionType](t, typeKindInstantiationExpression) +} +func (t *Type) AsMappedType() *MappedType { return castType[MappedType](t, typeKindMapped) } +func (t *Type) AsReverseMappedType() *ReverseMappedType { + return castType[ReverseMappedType](t, typeKindReverseMapped) +} + +func (t *Type) AsEvolvingArrayType() *EvolvingArrayType { + return castType[EvolvingArrayType](t, typeKindEvolvingArray) +} + +func (t *Type) AsTypeParameter() *TypeParameter { + return castType[TypeParameter](t, typeKindTypeParameter) +} +func (t *Type) AsUnionType() *UnionType { return castType[UnionType](t, typeKindUnion) } +func (t *Type) AsIntersectionType() *IntersectionType { + return castType[IntersectionType](t, typeKindIntersection) +} +func (t *Type) AsIndexType() *IndexType { return castType[IndexType](t, typeKindIndex) } +func (t *Type) AsIndexedAccessType() *IndexedAccessType { + return castType[IndexedAccessType](t, typeKindIndexedAccess) +} + +func (t *Type) AsTemplateLiteralType() *TemplateLiteralType { + return castType[TemplateLiteralType](t, typeKindTemplateLiteral) +} + +func (t *Type) AsStringMappingType() *StringMappingType { + return castType[StringMappingType](t, typeKindStringMapping) +} + +func (t *Type) AsSubstitutionType() *SubstitutionType { + return castType[SubstitutionType](t, typeKindSubstitution) +} + +func (t *Type) AsConditionalType() *ConditionalType { + return castType[ConditionalType](t, typeKindConditional) +} // Casts for embedded struct types -func (t *Type) AsConstrainedType() *ConstrainedType { return t.data.AsConstrainedType() } -func (t *Type) AsStructuredType() *StructuredType { return t.data.AsStructuredType() } -func (t *Type) AsObjectType() *ObjectType { return t.data.AsObjectType() } -func (t *Type) AsTypeReference() *TypeReference { return t.data.AsTypeReference() } -func (t *Type) AsInterfaceType() *InterfaceType { return t.data.AsInterfaceType() } +func (t *Type) AsConstrainedType() *ConstrainedType { + if t.kind >= typeKindTypeParameter && t.kind <= typeKindIntersection { + return (*ConstrainedType)(unsafe.Pointer(t)) + } + return nil +} + +func (t *Type) AsStructuredType() *StructuredType { + if t.kind >= typeKindObject && t.kind <= typeKindIntersection { + return (*StructuredType)(unsafe.Pointer(t)) + } + return nil +} + +func (t *Type) AsObjectType() *ObjectType { + if t.kind >= typeKindObject && t.kind <= typeKindEvolvingArray { + return (*ObjectType)(unsafe.Pointer(t)) + } + return nil +} + +func (t *Type) AsTypeReference() *TypeReference { + if t.kind >= typeKindTypeReference && t.kind <= typeKindTuple { + return (*TypeReference)(unsafe.Pointer(t)) + } + return nil +} + +func (t *Type) AsInterfaceType() *InterfaceType { + if t.kind >= typeKindInterface && t.kind <= typeKindTuple { + return (*InterfaceType)(unsafe.Pointer(t)) + } + return nil +} + func (t *Type) AsUnionOrIntersectionType() *UnionOrIntersectionType { - return t.data.AsUnionOrIntersectionType() + if t.kind >= typeKindUnion && t.kind <= typeKindIntersection { + return (*UnionOrIntersectionType)(unsafe.Pointer(t)) + } + return nil } func (t *Type) Distributed() []*Type { @@ -854,18 +973,6 @@ func (t *Type) IsTupleType() bool { return isTupleType(t) } -// TypeData - -type TypeData interface { - AsType() *Type - AsConstrainedType() *ConstrainedType - AsStructuredType() *StructuredType - AsObjectType() *ObjectType - AsTypeReference() *TypeReference - AsInterfaceType() *InterfaceType - AsUnionOrIntersectionType() *UnionOrIntersectionType -} - // TypeBase type TypeBase struct { diff --git a/tsc/internal/checker/types_test.go b/tsc/internal/checker/types_test.go new file mode 100644 index 0000000000000..8b21b07dc8160 --- /dev/null +++ b/tsc/internal/checker/types_test.go @@ -0,0 +1,75 @@ +package checker + +import ( + "runtime" + "testing" + "unsafe" + + "gotest.tools/v3/assert" +) + +func TestTypeCasts(t *testing.T) { + t.Parallel() + type bases interface { + AsType() *Type + AsConstrainedType() *ConstrainedType + AsStructuredType() *StructuredType + AsObjectType() *ObjectType + AsTypeReference() *TypeReference + AsInterfaceType() *InterfaceType + AsUnionOrIntersectionType() *UnionOrIntersectionType + } + tests := []struct { + kind typeKind + data bases + cast func(*Type) any + }{ + {typeKindIntrinsic, &IntrinsicType{}, func(t *Type) any { return t.AsIntrinsicType() }}, + {typeKindLiteral, &LiteralType{}, func(t *Type) any { return t.AsLiteralType() }}, + {typeKindUniqueESSymbol, &UniqueESSymbolType{}, func(t *Type) any { return t.AsUniqueESSymbolType() }}, + {typeKindTypeParameter, &TypeParameter{}, func(t *Type) any { return t.AsTypeParameter() }}, + {typeKindIndex, &IndexType{}, func(t *Type) any { return t.AsIndexType() }}, + {typeKindIndexedAccess, &IndexedAccessType{}, func(t *Type) any { return t.AsIndexedAccessType() }}, + {typeKindTemplateLiteral, &TemplateLiteralType{}, func(t *Type) any { return t.AsTemplateLiteralType() }}, + {typeKindStringMapping, &StringMappingType{}, func(t *Type) any { return t.AsStringMappingType() }}, + {typeKindSubstitution, &SubstitutionType{}, func(t *Type) any { return t.AsSubstitutionType() }}, + {typeKindConditional, &ConditionalType{}, func(t *Type) any { return t.AsConditionalType() }}, + {typeKindObject, &ObjectType{}, func(t *Type) any { return t.AsObjectType() }}, + {typeKindTypeReference, &TypeReference{}, func(t *Type) any { return t.AsTypeReference() }}, + {typeKindInterface, &InterfaceType{}, func(t *Type) any { return t.AsInterfaceType() }}, + {typeKindTuple, &TupleType{}, func(t *Type) any { return t.AsTupleType() }}, + {typeKindInstantiationExpression, &InstantiationExpressionType{}, func(t *Type) any { return t.AsInstantiationExpressionType() }}, + {typeKindMapped, &MappedType{}, func(t *Type) any { return t.AsMappedType() }}, + {typeKindReverseMapped, &ReverseMappedType{}, func(t *Type) any { return t.AsReverseMappedType() }}, + {typeKindEvolvingArray, &EvolvingArrayType{}, func(t *Type) any { return t.AsEvolvingArrayType() }}, + {typeKindUnion, &UnionType{}, func(t *Type) any { return t.AsUnionType() }}, + {typeKindIntersection, &IntersectionType{}, func(t *Type) any { return t.AsIntersectionType() }}, + } + for _, test := range tests { + c := &Checker{} + typ := c.newType(TypeFlagsAny, ObjectFlagsNone, test.data.AsType(), test.kind) + runtime.GC() + assert.Equal(t, test.cast(typ), any(test.data)) + assert.Equal(t, typ.AsConstrainedType(), test.data.AsConstrainedType()) + assert.Equal(t, typ.AsStructuredType(), test.data.AsStructuredType()) + assert.Equal(t, typ.AsObjectType(), test.data.AsObjectType()) + assert.Equal(t, typ.AsTypeReference(), test.data.AsTypeReference()) + assert.Equal(t, typ.AsInterfaceType(), test.data.AsInterfaceType()) + assert.Equal(t, typ.AsUnionOrIntersectionType(), test.data.AsUnionOrIntersectionType()) + typ.flags = TypeFlagsNever + typ.objectFlags = ObjectFlagsReference + assert.Equal(t, test.cast(typ), any(test.data)) + assert.Assert(t, didPanic(func() { typ.AsLiteralType() }) == (test.kind != typeKindLiteral)) + } +} + +func didPanic(f func()) (panicked bool) { + defer func() { panicked = recover() != nil }() + f() + return false +} + +func TestTypeHeaderSize(t *testing.T) { + t.Parallel() + assert.Equal(t, unsafe.Sizeof(Type{}), 16+3*unsafe.Sizeof((*Checker)(nil))) +}