From 4c577a3effe0b7fe4fcd9dc4f5e0f8c935129ff5 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Tue, 20 Feb 2024 00:01:47 +0100 Subject: [PATCH] fix: simplification of union types (#897) ### Summary of Changes Consider the following program: ``` class C { attr a: union } segment s(p: C) { val v = p.a; } ``` Previously, the type of the placeholder `v` was inferred to be `String`, because we incorrectly simplified union types that contained entries that were not fully substituted. Here, we replaced the entry `Int` with `T`, since `Int` is a subtype of `T`. This can change, however, once we substitute `T`, as shown here. Now, we never consider types that are not fully substituted for replacement and compute the correct type `union`. --- .../safe-ds-lang/src/language/typing/model.ts | 199 ++++++++---------- .../language/typing/safe-ds-type-computer.ts | 9 +- .../language/typing/safe-ds-type-factory.ts | 5 - .../tests/language/typing/model.test.ts | 105 --------- .../main.sdstest | 6 +- .../main.sdstest | 6 +- .../main.sdstest | 6 +- .../main.sdstest | 6 +- .../main.sdstest | 6 +- .../main.sdstest | 20 +- 10 files changed, 118 insertions(+), 250 deletions(-) diff --git a/packages/safe-ds-lang/src/language/typing/model.ts b/packages/safe-ds-lang/src/language/typing/model.ts index 8af13d67a..ca61765b0 100644 --- a/packages/safe-ds-lang/src/language/typing/model.ts +++ b/packages/safe-ds-lang/src/language/typing/model.ts @@ -30,6 +30,11 @@ export abstract class Type { */ abstract isExplicitlyNullable: boolean; + /** + * Whether the type does not contain type parameter types anymore. + */ + abstract isFullySubstituted: boolean; + /** * Returns whether the type is equal to another type. */ @@ -60,6 +65,7 @@ export abstract class Type { export class CallableType extends Type { private readonly factory: SafeDsTypeFactory; + override isExplicitlyNullable: boolean = false; constructor( @@ -74,6 +80,10 @@ export class CallableType extends Type { this.factory = services.types.TypeFactory; } + override get isFullySubstituted(): boolean { + return this.inputType.isFullySubstituted && this.outputType.isFullySubstituted; + } + /** * Returns the type of the parameter at the given index. If the index is out of bounds, returns `undefined`. */ @@ -114,7 +124,7 @@ export class CallableType extends Type { } override substituteTypeParameters(substitutions: TypeParameterSubstitutions): CallableType { - if (isEmpty(substitutions)) { + if (isEmpty(substitutions) || this.isFullySubstituted) { return this; } @@ -135,102 +145,21 @@ export class CallableType extends Type { } } -export class IntersectionType extends Type { - private readonly coreTypes: SafeDsCoreTypes; - private readonly factory: SafeDsTypeFactory; - - readonly types: Type[]; - private _isExplicitlyNullable: boolean | undefined; - - constructor(services: SafeDsServices, types: Type[]) { - super(); - - this.coreTypes = services.types.CoreTypes; - this.factory = services.types.TypeFactory; - - this.types = types; - } - - override get isExplicitlyNullable(): boolean { - if (this._isExplicitlyNullable === undefined) { - this._isExplicitlyNullable = this.types.every((it) => it.isExplicitlyNullable); - } - - return this._isExplicitlyNullable; - } - - override equals(other: unknown): boolean { - if (other === this) { - return true; - } else if (!(other instanceof IntersectionType)) { - return false; - } - - return this.types.length === other.types.length && this.types.every((type, i) => type.equals(other.types[i])); - } - - override toString(): string { - return `$intersection<${this.types.join(', ')}>`; - } - - override simplify(): Type { - // Flatten nested intersections - const newTypes = this.types.flatMap((type) => { - const unwrappedType = type.simplify(); - if (unwrappedType instanceof IntersectionType) { - return unwrappedType.types; - } else { - return unwrappedType; - } - }); - - // Remove the outer intersection if there's only one type left - if (newTypes.length === 1) { - return newTypes[0]!; - } - - return this.factory.createIntersectionType(...newTypes); - } - - override substituteTypeParameters(substitutions: TypeParameterSubstitutions): IntersectionType { - if (isEmpty(substitutions)) { - return this; - } - - return this.factory.createIntersectionType( - ...this.types.map((it) => it.substituteTypeParameters(substitutions)), - ); - } - - override withExplicitNullability(isExplicitlyNullable: boolean): Type { - if (isEmpty(this.types)) { - return this.coreTypes.Any.withExplicitNullability(isExplicitlyNullable); - } - - if (this.isExplicitlyNullable && !isExplicitlyNullable) { - return this.factory.createIntersectionType(...this.types.map((it) => it.withExplicitNullability(false))); - } else if (!this.isExplicitlyNullable && isExplicitlyNullable) { - return this.factory.createIntersectionType(...this.types.map((it) => it.withExplicitNullability(true))); - } else { - return this; - } - } -} - export class LiteralType extends Type { private readonly coreTypes: SafeDsCoreTypes; private readonly factory: SafeDsTypeFactory; - readonly constants: Constant[]; private _isExplicitlyNullable: boolean | undefined; + override readonly isFullySubstituted = true; - constructor(services: SafeDsServices, constants: Constant[]) { + constructor( + services: SafeDsServices, + readonly constants: Constant[], + ) { super(); this.coreTypes = services.types.CoreTypes; this.factory = services.types.TypeFactory; - - this.constants = constants; } override get isExplicitlyNullable(): boolean { @@ -304,8 +233,10 @@ export class LiteralType extends Type { export class NamedTupleType extends Type { private readonly factory: SafeDsTypeFactory; + readonly entries: NamedTupleEntry[]; override readonly isExplicitlyNullable = false; + private _isFullySubstituted: boolean | undefined; constructor(services: SafeDsServices, entries: NamedTupleEntry[]) { super(); @@ -314,6 +245,14 @@ export class NamedTupleType extends Type { this.entries = entries; } + override get isFullySubstituted(): boolean { + if (this._isFullySubstituted === undefined) { + this._isFullySubstituted = this.entries.every((it) => it.type.isFullySubstituted); + } + + return this._isFullySubstituted; + } + /** * The length of this tuple. */ @@ -357,7 +296,7 @@ export class NamedTupleType extends Type { } override substituteTypeParameters(substitutions: TypeParameterSubstitutions): NamedTupleType { - if (isEmpty(substitutions)) { + if (isEmpty(substitutions) || this.isFullySubstituted) { return this; } @@ -399,7 +338,7 @@ export class NamedTupleEntry { } substituteTypeParameters(substitutions: TypeParameterSubstitutions): NamedTupleEntry { - if (isEmpty(substitutions)) { + if (isEmpty(substitutions) || this.type.isFullySubstituted) { /* c8 ignore next 2 */ return this; } @@ -433,6 +372,8 @@ export abstract class NamedType extends Type { } export class ClassType extends NamedType { + private _isFullySubstituted: boolean | undefined; + constructor( declaration: SdsClass, readonly substitutions: TypeParameterSubstitutions, @@ -441,6 +382,14 @@ export class ClassType extends NamedType { super(declaration); } + override get isFullySubstituted(): boolean { + if (this._isFullySubstituted === undefined) { + this._isFullySubstituted = stream(this.substitutions.values()).every((it) => it.isFullySubstituted); + } + + return this._isFullySubstituted; + } + getTypeParameterTypeByIndex(index: number): Type { const typeParameter = getTypeParameters(this.declaration)[index]; if (!typeParameter) { @@ -486,7 +435,7 @@ export class ClassType extends NamedType { } override substituteTypeParameters(substitutions: TypeParameterSubstitutions): ClassType { - if (isEmpty(substitutions)) { + if (isEmpty(substitutions) || this.isFullySubstituted) { return this; } @@ -507,6 +456,8 @@ export class ClassType extends NamedType { } export class EnumType extends NamedType { + override readonly isFullySubstituted = true; + constructor( declaration: SdsEnum, override readonly isExplicitlyNullable: boolean, @@ -538,6 +489,8 @@ export class EnumType extends NamedType { } export class EnumVariantType extends NamedType { + override readonly isFullySubstituted = true; + constructor( declaration: SdsEnumVariant, override readonly isExplicitlyNullable: boolean, @@ -569,6 +522,8 @@ export class EnumVariantType extends NamedType { } export class TypeParameterType extends NamedType { + override readonly isFullySubstituted = false; + constructor( declaration: SdsTypeParameter, override readonly isExplicitlyNullable: boolean, @@ -608,12 +563,13 @@ export class TypeParameterType extends NamedType { } /** - * A type that represents an actual class, enum, or enum variant instead of an instance of it. + * A type that represents an actual named type declaration instead of an instance of it. */ export class StaticType extends Type { private readonly factory: SafeDsTypeFactory; override readonly isExplicitlyNullable = false; + override readonly isFullySubstituted = true; constructor( services: SafeDsServices, @@ -664,6 +620,7 @@ export class UnionType extends Type { readonly types: Type[]; private _isExplicitlyNullable: boolean | undefined; + private _isFullySubstituted: boolean | undefined; constructor(services: SafeDsServices, types: Type[]) { super(); @@ -683,6 +640,14 @@ export class UnionType extends Type { return this._isExplicitlyNullable; } + override get isFullySubstituted(): boolean { + if (this._isFullySubstituted === undefined) { + this._isFullySubstituted = this.types.every((it) => it.isFullySubstituted); + } + + return this._isFullySubstituted; + } + override equals(other: unknown): boolean { if (other === this) { return true; @@ -717,6 +682,8 @@ export class UnionType extends Type { // occurrence of duplicate types. It's also makes splicing easier. for (let i = newTypes.length - 1; i >= 0; i--) { const currentType = newTypes[i]!; + const currentTypeIsNothing = currentType.equals(this.coreTypes.Nothing); + const currentTypeIsNothingOrNull = currentType.equals(this.coreTypes.NothingOrNull); for (let j = newTypes.length - 1; j >= 0; j--) { if (i === j) { @@ -725,10 +692,28 @@ export class UnionType extends Type { const otherType = newTypes[j]!; + // Remove identical types + if (currentType.equals(otherType)) { + // Remove the current type + newTypes.splice(i, 1); + break; + } + + // We can always attempt to replace `Nothing` or `Nothing?` with other types, since they are the bottom + // types. But otherwise, we cannot use a type that is not fully substituted as a replacement. After + // substitution, we might lose information about the original type: + // + // Consider the type `union`, where `C` is a class and `T` is a type parameter without an upper + // bound. While `C` is a subtype of `T`, we cannot replace the union type with `T`, since we might later + // substitute `T` with a type that is not a supertype of `C`. + if (!currentTypeIsNothing && !currentTypeIsNothingOrNull && !otherType.isFullySubstituted) { + continue; + } + // Don't merge `Nothing?` into callable types, named tuple types or static types, since that would // create another union type. if ( - currentType.equals(this.coreTypes.NothingOrNull) && + currentTypeIsNothingOrNull && (otherType instanceof CallableType || otherType instanceof NamedTupleType || otherType instanceof StaticType) @@ -741,31 +726,22 @@ export class UnionType extends Type { // Other type always occurs before current type const newConstants = [...otherType.constants, ...currentType.constants]; const newLiteralType = this.factory.createLiteralType(...newConstants).simplify(); + + // Replace the other type with the new literal type newTypes.splice(j, 1, newLiteralType); + // Remove the current type newTypes.splice(i, 1); break; } - // Remove subtypes of other types. Type parameter types are a special case, since there might be a - // subtype relation between `currentType` and `otherType` in both directions. We always keep the type - // parameter type in this case for consistency, since it can be narrower if it has a lower bound. - if (currentType instanceof TypeParameterType) { - const candidateType = currentType.withExplicitNullability( - currentType.isExplicitlyNullable || otherType.isExplicitlyNullable, - ); - - if (this.typeChecker.isSubtypeOf(otherType, candidateType)) { - newTypes.splice(j, 1, candidateType); - newTypes.splice(i, 1); - break; - } - } - + // Remove subtypes of other types const candidateType = otherType.withExplicitNullability( currentType.isExplicitlyNullable || otherType.isExplicitlyNullable, ); - if (this.typeChecker.isSubtypeOf(currentType, candidateType)) { - newTypes.splice(j, 1, candidateType); // Update nullability + if (this.typeChecker.isSupertypeOf(candidateType, currentType)) { + // Replace the other type with the candidate type (updated nullability) + newTypes.splice(j, 1, candidateType); + // Remove the current type newTypes.splice(i, 1); break; } @@ -780,7 +756,7 @@ export class UnionType extends Type { } override substituteTypeParameters(substitutions: TypeParameterSubstitutions): UnionType { - if (isEmpty(substitutions)) { + if (isEmpty(substitutions) || this.isFullySubstituted) { return this; } @@ -803,7 +779,8 @@ export class UnionType extends Type { } class UnknownTypeClass extends Type { - readonly isExplicitlyNullable = false; + override readonly isExplicitlyNullable = false; + override readonly isFullySubstituted = true; override equals(other: unknown): boolean { return other instanceof UnknownTypeClass; diff --git a/packages/safe-ds-lang/src/language/typing/safe-ds-type-computer.ts b/packages/safe-ds-lang/src/language/typing/safe-ds-type-computer.ts index 70a21488a..c285b09ee 100644 --- a/packages/safe-ds-lang/src/language/typing/safe-ds-type-computer.ts +++ b/packages/safe-ds-lang/src/language/typing/safe-ds-type-computer.ts @@ -692,7 +692,7 @@ export class SafeDsTypeComputer { * invalid upper bounds are specified, but are invalid (e.g. because of an unresolved reference or a cycle), * `$unknown` is returned. The result is simplified as much as possible. */ - computeUpperBound(nodeOrType: SdsTypeParameter | TypeParameterType, options: ComputeBoundOptions = {}): Type { + computeUpperBound(nodeOrType: SdsTypeParameter | TypeParameterType, options: ComputeUpperBoundOptions = {}): Type { let type: TypeParameterType; if (nodeOrType instanceof TypeParameterType) { type = nodeOrType; @@ -704,7 +704,7 @@ export class SafeDsTypeComputer { return result.withExplicitNullability(result.isExplicitlyNullable || type.isExplicitlyNullable); } - private doComputeUpperBound(type: TypeParameterType, options: ComputeBoundOptions): Type { + private doComputeUpperBound(type: TypeParameterType, options: ComputeUpperBoundOptions): Type { const upperBound = type.declaration.upperBound; if (!upperBound) { return this.coreTypes.AnyOrNull; @@ -1052,14 +1052,15 @@ export class SafeDsTypeComputer { } private Nothing(isNullable: boolean): Type { + /* c8 ignore next 2 */ return isNullable ? this.coreTypes.NothingOrNull : this.coreTypes.Nothing; } } /** - * Options for {@link computeLowerBound} and {@link computeUpperBound}. + * Options for {@link computeUpperBound}. */ -interface ComputeBoundOptions { +interface ComputeUpperBoundOptions { /** * If `true`, the computation stops at type parameter types and returns them as is. Otherwise, it finds the bounds * for the type parameter types recursively. diff --git a/packages/safe-ds-lang/src/language/typing/safe-ds-type-factory.ts b/packages/safe-ds-lang/src/language/typing/safe-ds-type-factory.ts index 22f19c8fc..eec41eebe 100644 --- a/packages/safe-ds-lang/src/language/typing/safe-ds-type-factory.ts +++ b/packages/safe-ds-lang/src/language/typing/safe-ds-type-factory.ts @@ -4,7 +4,6 @@ import { ClassType, EnumType, EnumVariantType, - IntersectionType, LiteralType, NamedTupleEntry, NamedTupleType, @@ -55,10 +54,6 @@ export class SafeDsTypeFactory { return new EnumVariantType(declaration, isExplicitlyNullable); } - createIntersectionType(...types: Type[]): IntersectionType { - return new IntersectionType(this.services, types); - } - createLiteralType(...constants: Constant[]): LiteralType { return new LiteralType(this.services, constants); } diff --git a/packages/safe-ds-lang/tests/language/typing/model.test.ts b/packages/safe-ds-lang/tests/language/typing/model.test.ts index 29558bfed..e646aed8d 100644 --- a/packages/safe-ds-lang/tests/language/typing/model.test.ts +++ b/packages/safe-ds-lang/tests/language/typing/model.test.ts @@ -72,11 +72,6 @@ describe('type model', async () => { ), valueOfOtherType: () => UnknownType, }, - { - value: () => factory.createIntersectionType(UnknownType), - unequalValueOfSameType: () => factory.createIntersectionType(), - valueOfOtherType: () => UnknownType, - }, { value: () => factory.createLiteralType(new BooleanConstant(true)), unequalValueOfSameType: () => factory.createLiteralType(new IntConstant(1n)), @@ -180,10 +175,6 @@ describe('type model', async () => { ), expectedString: '(p2?: $unknown) -> ()', }, - { - value: factory.createIntersectionType(UnknownType), - expectedString: '$intersection<$unknown>', - }, { value: factory.createLiteralType(new BooleanConstant(true)), expectedString: 'literal', @@ -272,19 +263,6 @@ describe('type model', async () => { ), ), }, - { - type: factory.createIntersectionType( - new ClassType(class1, new Map([[typeParameter2, new TypeParameterType(typeParameter1, false)]]), false), - ), - substitutions: substitutions1, - expectedType: factory.createIntersectionType( - new ClassType( - class1, - new Map([[typeParameter2, factory.createLiteralType(new IntConstant(1n))]]), - false, - ), - ), - }, { type: factory.createLiteralType(new BooleanConstant(true)), substitutions: substitutions1, @@ -377,44 +355,6 @@ describe('type model', async () => { }); }); - const simplifyTests: SimplifyTest[] = [ - { - type: factory.createIntersectionType(), - expectedType: factory.createIntersectionType(), - }, - { - type: factory.createIntersectionType(new ClassType(class1, new Map(), false)), - expectedType: new ClassType(class1, new Map(), false), - }, - { - type: factory.createIntersectionType( - factory.createIntersectionType(new ClassType(class1, new Map(), false)), - ), - expectedType: new ClassType(class1, new Map(), false), - }, - { - type: factory.createIntersectionType( - factory.createIntersectionType( - new ClassType(class1, new Map(), false), - new ClassType(class2, new Map(), false), - ), - factory.createIntersectionType(new EnumType(enum1, false), new EnumVariantType(enumVariant1, false)), - ), - expectedType: factory.createIntersectionType( - new ClassType(class1, new Map(), false), - new ClassType(class2, new Map(), false), - new EnumType(enum1, false), - new EnumVariantType(enumVariant1, false), - ), - }, - ]; - describe.each(simplifyTests)('simplify', ({ type, expectedType }) => { - it(`should simplify type (${type.constructor.name} -- ${type})`, () => { - const actual = type.simplify(); - expectEqualTypes(actual, expectedType); - }); - }); - const withExplicitNullabilityTests: WithExplicitNullabilityTest[] = [ { type: factory.createCallableType( @@ -449,36 +389,6 @@ describe('type model', async () => { factory.createNamedTupleType(), ), }, - { - type: factory.createIntersectionType(), - isNullable: true, - expectedType: coreTypes.AnyOrNull, - }, - { - type: factory.createIntersectionType(), - isNullable: false, - expectedType: coreTypes.Any, - }, - { - type: factory.createIntersectionType(new ClassType(class1, new Map(), false)), - isNullable: true, - expectedType: factory.createIntersectionType(new ClassType(class1, new Map(), true)), - }, - { - type: factory.createIntersectionType(new ClassType(class1, new Map(), false)), - isNullable: false, - expectedType: factory.createIntersectionType(new ClassType(class1, new Map(), false)), - }, - { - type: factory.createIntersectionType(new ClassType(class1, new Map(), true)), - isNullable: true, - expectedType: factory.createIntersectionType(new ClassType(class1, new Map(), true)), - }, - { - type: factory.createIntersectionType(new ClassType(class1, new Map(), true)), - isNullable: false, - expectedType: factory.createIntersectionType(new ClassType(class1, new Map(), false)), - }, { type: factory.createLiteralType(new BooleanConstant(true)), isNullable: true, @@ -708,21 +618,6 @@ interface SubstituteTypeParametersTest { expectedType: Type; } -/** - * Tests for {@link Type.simplify}. - */ -interface SimplifyTest { - /** - * The type to test. - */ - type: Type; - - /** - * The expected result. - */ - expectedType: Type; -} - /** * Tests for {@link Type.withExplicitNullability}. */ diff --git a/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/class type and type parameter type/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/class type and type parameter type/main.sdstest index 462677cca..6b68b8290 100644 --- a/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/class type and type parameter type/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/class type and type parameter type/main.sdstest @@ -12,11 +12,11 @@ class Test + // $TEST$ serialization List p1: Any = »[c, unbounded]«, - // $TEST$ serialization List + // $TEST$ serialization List p2: Any = »[c, unboundedOrNull]«, - // $TEST$ serialization List + // $TEST$ serialization List p3: Any = »[c, boundedByC]«, // $TEST$ serialization List p4: Any = »[c, boundedByD]«, diff --git a/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/enum type and type parameter type/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/enum type and type parameter type/main.sdstest index bd98fa2f3..14568f31e 100644 --- a/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/enum type and type parameter type/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/enum type and type parameter type/main.sdstest @@ -22,11 +22,11 @@ class Test< boundedByOtherEnum: BoundedByOtherEnum, boundedByOtherEnumOrNull: BoundedByOtherEnumOrNull, - // $TEST$ serialization List + // $TEST$ serialization List p1: Any = »[e, unbounded]«, - // $TEST$ serialization List + // $TEST$ serialization List p2: Any = »[e, unboundedOrNull]«, - // $TEST$ serialization List + // $TEST$ serialization List p3: Any = »[e, boundedByEnum]«, // $TEST$ serialization List p4: Any = »[e, boundedByEnumVariant]«, diff --git a/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/enum variant type and type parameter type/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/enum variant type and type parameter type/main.sdstest index 4fc207332..625c24ef8 100644 --- a/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/enum variant type and type parameter type/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/enum variant type and type parameter type/main.sdstest @@ -19,11 +19,11 @@ class D< boundedByOtherEnumVariant: BoundedByOtherEnumVariant, boundedByOtherEnumVariantOrNull: BoundedByOtherEnumVariantOrNull, - // $TEST$ serialization List + // $TEST$ serialization List p1: Any = »[v, unbounded]«, - // $TEST$ serialization List + // $TEST$ serialization List p2: Any = »[v, unboundedOrNull]«, - // $TEST$ serialization List + // $TEST$ serialization List p3: Any = »[v, boundedByEnumVariant]«, // $TEST$ serialization List p4: Any = »[v, boundedByOtherEnumVariant]«, diff --git a/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/literal type and type parameter type/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/literal type and type parameter type/main.sdstest index c883c0738..59380141c 100644 --- a/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/literal type and type parameter type/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/literal type and type parameter type/main.sdstest @@ -12,11 +12,11 @@ class Test< boundedByString: BoundedByString, boundedByStringOrNull: BoundedByStringOrNull, - // $TEST$ serialization List + // $TEST$ serialization List p1: Any = »[1, unbounded]«, - // $TEST$ serialization List + // $TEST$ serialization List p2: Any = »[1, unboundedOrNull]«, - // $TEST$ serialization List + // $TEST$ serialization List p3: Any = »[1, boundedByInt]«, // $TEST$ serialization List p4: Any = »[1, boundedByString]«, diff --git a/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/type parameter type and type parameter type/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/type parameter type and type parameter type/main.sdstest index 0dbef9256..7a1d97b65 100644 --- a/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/type parameter type and type parameter type/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/lowest common supertype/type parameter type and type parameter type/main.sdstest @@ -13,11 +13,11 @@ class Test a1: Any = »[nothingToAnyOrNull, nothingToAnyOrNull]«, - // $TEST$ serialization List + // $TEST$ serialization List a2: Any = »[nothingToAnyOrNull, nothingToD]«, - // $TEST$ serialization List + // $TEST$ serialization List a3: Any = »[nothingToAnyOrNull, nothingToE]«, - // $TEST$ serialization List + // $TEST$ serialization List a4: Any = »[nothingToAnyOrNull, nothingToF]«, // $TEST$ serialization List diff --git a/packages/safe-ds-lang/tests/resources/typing/simplification/remove unneeded entries from union types/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/simplification/remove unneeded entries from union types/main.sdstest index 06d603687..f26435ca3 100644 --- a/packages/safe-ds-lang/tests/resources/typing/simplification/remove unneeded entries from union types/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/simplification/remove unneeded entries from union types/main.sdstest @@ -83,14 +83,14 @@ class TestsInvolvingTypeParameters( // $TEST$ serialization Unbounded? a4: »union«, - // $TEST$ serialization Unbounded + // $TEST$ serialization union a5: »union«, - // $TEST$ serialization Unbounded + // $TEST$ serialization union a6: »union«, - // $TEST$ serialization Unbounded? + // $TEST$ serialization Any? a7: »union«, - // $TEST$ serialization Unbounded? + // $TEST$ serialization Any? a8: »union«, @@ -104,19 +104,19 @@ class TestsInvolvingTypeParameters( // $TEST$ serialization UpperBound? b4: »union«, - // $TEST$ serialization UpperBound + // $TEST$ serialization union b5: »union«, - // $TEST$ serialization UpperBound + // $TEST$ serialization union b6: »union«, - // $TEST$ serialization UpperBound + // $TEST$ serialization Number b7: »union«, - // $TEST$ serialization UpperBound + // $TEST$ serialization Number b8: »union«, - // $TEST$ serialization UpperBound? + // $TEST$ serialization Number? b9: »union«, - // $TEST$ serialization UpperBound? + // $TEST$ serialization Number? b10: »union«, // $TEST$ serialization Any?