SwiftTL: add Resolver.resolveLayeredTypes

Walks layer sections in order, threads a running constructor-name map
with last-wins semantics, and snapshots [SumType] at each layer
boundary. Constructors appearing only in later layers do not leak into
earlier layers' snapshots.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
isaac 2026-04-21 15:40:59 +04:00
parent dba8a67bcb
commit 10054efd81

View file

@ -199,7 +199,114 @@ enum Resolver {
return types.values.sorted(by: { $0.name < $1.name })
}
static func resolveLayeredTypes(
layers: [(layerNumber: Int, constructors: [DescriptionParser.ConstructorDescription])]
) throws -> [(layerNumber: Int, types: [SumType])] {
// Running state: for each constructor name, the target type name and the raw description.
// We keep raw descriptions (not resolved forms) because a later-layer constructor may
// introduce new target-type names, and resolveTypeReference needs the final target-type set.
var liveConstructors: [QualifiedName: (typeName: QualifiedName, description: DescriptionParser.ConstructorDescription)] = [:]
var result: [(layerNumber: Int, types: [SumType])] = []
for (layerNumber, layerConstructors) in layers {
// Apply this layer's constructors to the running map with last-wins semantics.
for constructorDescription in layerConstructors {
switch constructorDescription.type {
case let .type(name):
if !name.value[name.value.startIndex].isUppercase {
throw ResolutionError(text: "Type constructor \(constructorDescription.name) -> \(name): the resulting type name should begin with a capital letter")
}
liveConstructors[constructorDescription.name] = (name, constructorDescription)
case let .generic(name, argumentType):
throw ResolutionError(text: "Type constructor \(constructorDescription.name) can not be used to construct a generic type \(name)<\(argumentType)>")
}
}
// Note: a constructor reassigned to a different target type in a later layer is
// removed from its old type's constructor set. If that drops the old type to zero
// constructors, it vanishes from this snapshot, and any unrelated argument that
// still references the old type via boxedType(...) will fail to resolve here.
// secret_scheme.tl does not exercise this case (same-name constructors always
// target the same type), but the rule applies if a future layered schema does.
// Snapshot: group by target type, resolve.
var constructedTypes: [QualifiedName: [DescriptionParser.ConstructorDescription]] = [:]
var constructorNameToType: [QualifiedName: QualifiedName] = [:]
for (ctorName, entry) in liveConstructors {
constructedTypes[entry.typeName, default: []].append(entry.description)
constructorNameToType[ctorName] = entry.typeName
}
func resolveTypeReference(description: DescriptionParser.TypeReferenceDescription) throws -> TypeReference {
switch description {
case let .type(name):
if let resolvedBuiltinType = resolveBuiltinType(name: name) {
return resolvedBuiltinType
}
if name.value[name.value.startIndex].isUppercase {
if let _ = constructedTypes[name] {
return .boxedType(name)
} else {
throw ResolutionError(text: "Unresolved type \(name) in layer \(layerNumber)")
}
} else {
if let typeName = constructorNameToType[name] {
return .bareConstructor(typeName: typeName, name: name)
} else {
throw ResolutionError(text: "Unresolved type constructor \(name) in layer \(layerNumber)")
}
}
case let .generic(name, argumentType):
if name == "vector" {
return .bareVector(try resolveTypeReference(description: .type(name: argumentType)))
} else if name == "Vector" {
return .boxedVector(try resolveTypeReference(description: .type(name: argumentType)))
} else {
throw ResolutionError(text: "Unresolved generic type \(name) in layer \(layerNumber)")
}
}
}
func resolveArgument(existingArguments: [Argument], description: DescriptionParser.ArgumentDescription) throws -> Argument {
return Argument(
name: description.name,
type: try resolveTypeReference(description: description.type),
condition: try description.condition.flatMap { condition -> Argument.Condition in
if !existingArguments.contains(where: { $0.name == condition.fieldName }) {
throw ResolutionError(text: "Unresolved conditional field reference to \(condition.fieldName) in layer \(layerNumber)")
}
return Argument.Condition(fieldName: condition.fieldName, bitIndex: condition.bitIndex)
}
)
}
var types: [QualifiedName: SumType] = [:]
for (typeName, constructorDescriptions) in constructedTypes {
let type = SumType(name: typeName)
for constructorDescription in constructorDescriptions {
var arguments: [Argument] = []
for argumentDescription in constructorDescription.arguments {
arguments.append(try resolveArgument(existingArguments: arguments, description: argumentDescription))
}
guard let id = constructorDescription.explicitId else {
throw ResolutionError(text: "Constructor \(constructorDescription.name) does not have an id")
}
type.constructors[constructorDescription.name] = SumType.Constructor(
name: constructorDescription.name,
id: id,
arguments: arguments
)
}
types[type.name] = type
}
let sortedTypes = types.values.sorted(by: { $0.name < $1.name })
result.append((layerNumber, sortedTypes))
}
return result
}
static func resolveFunctions(types: [SumType], functionDescriptions: [DescriptionParser.ConstructorDescription]) throws -> [Function] {
var functions: [QualifiedName: Function] = [:]