Skip to content

Switch RecursionId to any #1477

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 12, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 13 additions & 17 deletions internal/checker/relater.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,13 @@ type ErrorOutputContainer struct {

type ErrorReporter func(message *diagnostics.Message, args ...any)

type RecursionIdKind uint32

const (
RecursionIdKindNode RecursionIdKind = iota
RecursionIdKindSymbol
RecursionIdKindType
)

type RecursionId struct {
kind RecursionIdKind
id uint32
value any
}

// This function exists to constrain the types of values that can be used as recursion IDs.
func asRecursionId[T *ast.Node | *ast.Symbol | *Type](value T) RecursionId {
return RecursionId{value: value}
}

type Relation struct {
Expand Down Expand Up @@ -836,35 +832,35 @@ func getRecursionIdentity(t *Type) RecursionId {
// Deferred type references are tracked through their associated AST node. This gives us finer
// granularity than using their associated target because each manifest type reference has a
// unique AST node.
return RecursionId{kind: RecursionIdKindNode, id: uint32(ast.GetNodeId(t.AsTypeReference().node))}
return asRecursionId(t.AsTypeReference().node)
}
if t.symbol != nil && !(t.objectFlags&ObjectFlagsAnonymous != 0 && t.symbol.Flags&ast.SymbolFlagsClass != 0) {
// We track object types that have a symbol by that symbol (representing the origin of the type), but
// exclude the static side of a class since it shares its symbol with the instance side.
return RecursionId{kind: RecursionIdKindSymbol, id: uint32(ast.GetSymbolId(t.symbol))}
return asRecursionId(t.symbol)
}
if isTupleType(t) {
return RecursionId{kind: RecursionIdKindType, id: uint32(t.Target().id)}
return asRecursionId(t.Target())
}
}
if t.flags&TypeFlagsTypeParameter != 0 && t.symbol != nil {
// We use the symbol of the type parameter such that all "fresh" instantiations of that type parameter
// have the same recursion identity.
return RecursionId{kind: RecursionIdKindSymbol, id: uint32(ast.GetSymbolId(t.symbol))}
return asRecursionId(t.symbol)
}
if t.flags&TypeFlagsIndexedAccess != 0 {
// Identity is the leftmost object type in a chain of indexed accesses, eg, in A[P1][P2][P3] it is A.
t = t.AsIndexedAccessType().objectType
for t.flags&TypeFlagsIndexedAccess != 0 {
t = t.AsIndexedAccessType().objectType
}
return RecursionId{kind: RecursionIdKindType, id: uint32(t.id)}
return asRecursionId(t)
}
if t.flags&TypeFlagsConditional != 0 {
// The root object represents the origin of the conditional type
return RecursionId{kind: RecursionIdKindNode, id: uint32(ast.GetNodeId(t.AsConditionalType().root.node.AsNode()))}
return asRecursionId(t.AsConditionalType().root.node.AsNode())
}
return RecursionId{kind: RecursionIdKindType, id: uint32(t.id)}
return asRecursionId(t)
}

func (c *Checker) getBestMatchingType(source *Type, target *Type, isRelatedTo func(source *Type, target *Type) Ternary) *Type {
Expand Down
Loading