From 8a35558ac9db8aac181ac3f9b80966d59656f805 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 5 Feb 2024 17:17:58 +0100 Subject: [PATCH] feat: handle type parameters (part 1) (#856) Closes partially #23 ### Summary of Changes * New kind of type `TypeParameterType` to represent usages of type parameter references as types. * Store substitutions for type parameters in `ClassType` and take them into account for serialization and equality checks. * Compute element types for list literals. * Compute key/value types for map literals. * Compute type of indexed accesses on lists/maps. * Type checking for the indexed passed to an indexed access on maps (it already worked for lists). * Consider type parameter values when checking the assignability of class types. ### Deferred to future PRs There are additional features needed to fully handle type parameters, including * Inference of type parameter values for calls. * Substitution of type parameters for types of class members (e.g. for member access on an attribute). * Finding the lowest common superclass of class types with type parameters. These features will be added later in separate PRs. --- .../lsp/safe-ds-node-info-provider.ts | 12 +- .../scoping/safe-ds-scope-computation.ts | 1 + .../safe-ds-lang/src/language/typing/model.ts | 167 +- .../typing/safe-ds-class-hierarchy.ts | 8 +- .../src/language/typing/safe-ds-core-types.ts | 40 +- .../language/typing/safe-ds-type-checker.ts | 82 +- .../language/typing/safe-ds-type-computer.ts | 173 +- .../experimentalLanguageFeatures.ts | 74 - .../other/expressions/indexedAccess.ts | 4 +- .../language/validation/safe-ds-validator.ts | 6 - .../src/language/validation/types.ts | 21 +- .../builtins/safeds/lang/coreClasses.sdsstub | 4 +- .../tests/helpers/testDescription.ts | 2 +- .../safe-ds-document-symbol-provider.test.ts | 2 + .../language/partialEvaluation/model.test.ts | 14 +- .../tests/language/purity/model.test.ts | 14 +- .../tests/language/typing/model.test.ts | 309 +++- .../canBeTypeOfConstantParameter.test.ts | 7 +- .../type checker/isAssignableTo.test.ts | 1537 ++++++++++------- .../lowestCommonSupertype.test.ts | 8 + .../type computer/streamSupertypes.test.ts | 119 ++ .../generation/expressions/maps/input.sdstest | 14 +- .../output/tests/generator/maps/gen_input.py | 8 +- .../tests/generator/maps/gen_input.py.map | 2 +- .../in global classes/main.sdstest | 5 +- .../in nested classes/main.sdstest | 7 +- .../block lambda results/main.sdstest | 2 +- .../assignees/placeholders/main.sdstest | 2 +- .../typing/assignees/yields/main.sdstest | 2 +- .../declarations/annotations/main.sdstest | 2 +- .../declarations/attributes/main.sdstest | 4 +- .../declarations/functions/main.sdstest | 2 +- .../parameters/of annotations/main.sdstest | 2 +- .../that are isolated/main.sdstest | 2 +- .../that are passed as arguments/main.sdstest | 8 +- .../main.sdstest | 4 +- .../that are yielded/main.sdstest | 4 +- .../parameters/of callable types/main.sdstest | 2 +- .../parameters/of classes/main.sdstest | 2 +- .../parameters/of enum variants/main.sdstest | 2 +- .../that are isolated/main.sdstest | 2 +- .../that are passed as arguments/main.sdstest | 8 +- .../main.sdstest | 4 +- .../that are yielded/main.sdstest | 4 +- .../parameters/of functions/main.sdstest | 2 +- .../parameters/of segments/main.sdstest | 2 +- .../declarations/pipelines/main.sdstest | 2 +- .../typing/declarations/segments/main.sdstest | 2 +- .../declarations/type parameters/main.sdstest | 5 + .../that are isolated/main.sdstest | 4 +- .../that are passed as arguments/main.sdstest | 12 +- .../main.sdstest | 10 +- .../that are yielded/main.sdstest | 4 +- .../with manifest types/main.sdstest | 2 +- .../calls/of annotations/main.sdstest | 2 +- .../expressions/calls/of classes/main.sdstest | 2 +- .../calls/of enum variants/main.sdstest | 12 +- .../calls/of non-callable/main.sdstest | 2 +- .../expressions/calls/unresolved/main.sdstest | 2 +- .../that are isolated/main.sdstest | 4 +- .../that are passed as arguments/main.sdstest | 10 +- .../main.sdstest | 4 +- .../that are yielded/main.sdstest | 4 +- .../expressions/indexed accesses/main.sdstest | 16 +- .../typing/expressions/lists/main.sdstest | 18 +- .../typing/expressions/maps/main.sdstest | 21 +- .../member accesses/unresolved/main.sdstest | 4 +- .../operations/arithmetic/main.sdstest | 38 + .../references/unresolved/main.sdstest | 2 +- .../typing/types/callable types/main.sdstest | 2 +- .../typing/types/member types/main.sdstest | 4 +- .../typing/types/named types/main.sdstest | 4 +- .../named types/with type parameters.sdstest | 36 + .../typing/types/type arguments/main.sdstest | 4 +- .../indexed access/main.sdstest | 15 - .../type argument lists/main.sdstest | 11 - .../type parameter lists/main.sdstest | 8 - .../main.sdstest | 3 +- .../indexed access on list/main.sdstest | 22 +- .../indexed access on map/main.sdstest | 27 + .../indexed access receiver/main.sdstest | 2 +- .../checking/infix operations/main.sdstest | 40 +- .../checking/prefix operations/main.sdstest | 4 +- 83 files changed, 2091 insertions(+), 975 deletions(-) create mode 100644 packages/safe-ds-lang/tests/language/typing/type computer/streamSupertypes.test.ts create mode 100644 packages/safe-ds-lang/tests/resources/typing/declarations/type parameters/main.sdstest create mode 100644 packages/safe-ds-lang/tests/resources/typing/types/named types/with type parameters.sdstest delete mode 100644 packages/safe-ds-lang/tests/resources/validation/experimental language feature/indexed access/main.sdstest delete mode 100644 packages/safe-ds-lang/tests/resources/validation/experimental language feature/type argument lists/main.sdstest delete mode 100644 packages/safe-ds-lang/tests/resources/validation/experimental language feature/type parameter lists/main.sdstest create mode 100644 packages/safe-ds-lang/tests/resources/validation/types/checking/indexed access on map/main.sdstest diff --git a/packages/safe-ds-lang/src/language/lsp/safe-ds-node-info-provider.ts b/packages/safe-ds-lang/src/language/lsp/safe-ds-node-info-provider.ts index c5116bbf5..8d6ca2cea 100644 --- a/packages/safe-ds-lang/src/language/lsp/safe-ds-node-info-provider.ts +++ b/packages/safe-ds-lang/src/language/lsp/safe-ds-node-info-provider.ts @@ -1,7 +1,7 @@ import { AstNode } from 'langium'; import { SymbolTag } from 'vscode-languageserver'; import type { SafeDsAnnotations } from '../builtins/safe-ds-annotations.js'; -import { isSdsAnnotatedObject, isSdsFunction, isSdsSegment } from '../generated/ast.js'; +import { isSdsAnnotatedObject, isSdsAttribute, isSdsFunction, isSdsSegment } from '../generated/ast.js'; import type { SafeDsServices } from '../safe-ds-module.js'; import { SafeDsTypeComputer } from '../typing/safe-ds-type-computer.js'; @@ -19,11 +19,13 @@ export class SafeDsNodeInfoProvider { * hierarchies. */ getDetails(node: AstNode): string | undefined { - if (isSdsFunction(node) || isSdsSegment(node)) { - const type = this.typeComputer.computeType(node); - return type?.toString(); + if (isSdsAttribute(node)) { + return `: ${this.typeComputer.computeType(node)}`; + } else if (isSdsFunction(node) || isSdsSegment(node)) { + return this.typeComputer.computeType(node)?.toString(); + } else { + return undefined; } - return undefined; } /** diff --git a/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-computation.ts b/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-computation.ts index 96732c298..3dd486a11 100644 --- a/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-computation.ts +++ b/packages/safe-ds-lang/src/language/scoping/safe-ds-scope-computation.ts @@ -122,6 +122,7 @@ export class SafeDsScopeComputation extends DefaultScopeComputation { if (isSdsClass(containingDeclaration)) { this.addToScopesIfKeyIsDefined(scopes, containingDeclaration.parameterList, description); + this.addToScopesIfKeyIsDefined(scopes, containingDeclaration.parentTypeList, description); this.addToScopesIfKeyIsDefined(scopes, containingDeclaration.constraintList, description); this.addToScopesIfKeyIsDefined(scopes, containingDeclaration.body, description); } else if (isSdsFunction(containingDeclaration)) { diff --git a/packages/safe-ds-lang/src/language/typing/model.ts b/packages/safe-ds-lang/src/language/typing/model.ts index d71cff8fb..7043f7b42 100644 --- a/packages/safe-ds-lang/src/language/typing/model.ts +++ b/packages/safe-ds-lang/src/language/typing/model.ts @@ -7,9 +7,13 @@ import { SdsEnum, SdsEnumVariant, SdsParameter, + SdsTypeParameter, } from '../generated/ast.js'; -import { Parameter } from '../helpers/nodeProperties.js'; +import { getTypeParameters, Parameter } from '../helpers/nodeProperties.js'; import { Constant, NullConstant } from '../partialEvaluation/model.js'; +import { stream } from 'langium'; + +export type TypeParameterSubstitutions = Map; /** * The type of an AST node. @@ -30,6 +34,11 @@ export abstract class Type { */ abstract toString(): string; + /** + * Returns a copy of this type with the given type parameters substituted. + */ + abstract substituteTypeParameters(substitutions: TypeParameterSubstitutions): Type; + /** * Removes any unnecessary containers from the type. */ @@ -83,6 +92,19 @@ export class CallableType extends Type { return `(${inputTypeString}) -> ${this.outputType}`; } + override substituteTypeParameters(substitutions: TypeParameterSubstitutions): CallableType { + if (isEmpty(substitutions)) { + return this; + } + + return new CallableType( + this.callable, + this.parameter, + this.inputType.substituteTypeParameters(substitutions), + this.outputType.substituteTypeParameters(substitutions), + ); + } + override unwrap(): CallableType { return new CallableType( this.callable, @@ -129,6 +151,10 @@ export class LiteralType extends Type { return `literal<${this.constants.join(', ')}>`; } + override substituteTypeParameters(_substitutions: TypeParameterSubstitutions): Type { + return this; + } + override unwrap(): LiteralType { return this; } @@ -185,6 +211,14 @@ export class NamedTupleType extends Type { return `(${this.entries.join(', ')})`; } + override substituteTypeParameters(substitutions: TypeParameterSubstitutions): NamedTupleType { + if (isEmpty(substitutions)) { + return this; + } + + return new NamedTupleType(...this.entries.map((it) => it.substituteTypeParameters(substitutions))); + } + /** * If this only has one entry, returns its type. Otherwise, returns this. */ @@ -228,6 +262,15 @@ export class NamedTupleEntry { return `${this.name}: ${this.type}`; } + substituteTypeParameters(substitutions: TypeParameterSubstitutions): NamedTupleEntry { + if (isEmpty(substitutions)) { + /* c8 ignore next 2 */ + return this; + } + + return new NamedTupleEntry(this.declaration, this.name, this.type.substituteTypeParameters(substitutions)); + } + unwrap(): NamedTupleEntry { return new NamedTupleEntry(this.declaration, this.name, this.type.unwrap()); } @@ -256,11 +299,21 @@ export abstract class NamedType extends Type { export class ClassType extends NamedType { constructor( declaration: SdsClass, + readonly substitutions: TypeParameterSubstitutions, override readonly isNullable: boolean, ) { super(declaration); } + getTypeParameterTypeByIndex(index: number): Type { + const typeParameter = getTypeParameters(this.declaration)[index]; + if (!typeParameter) { + return UnknownType; + } + + return this.substitutions.get(typeParameter) ?? UnknownType; + } + override equals(other: unknown): boolean { if (other === this) { return true; @@ -268,11 +321,43 @@ export class ClassType extends NamedType { return false; } - return other.declaration === this.declaration && other.isNullable === this.isNullable; + return ( + other.declaration === this.declaration && + other.isNullable === this.isNullable && + substitutionsAreEqual(other.substitutions, this.substitutions) + ); + } + + override toString(): string { + let result = this.declaration.name; + + if (this.substitutions.size > 0) { + result += `<${Array.from(this.substitutions.values()) + .map((value) => value.toString()) + .join(', ')}>`; + } + + if (this.isNullable) { + result += '?'; + } + + return result; + } + + override substituteTypeParameters(substitutions: TypeParameterSubstitutions): ClassType { + if (isEmpty(substitutions)) { + return this; + } + + const newSubstitutions = new Map( + stream(this.substitutions).map(([key, value]) => [key, value.substituteTypeParameters(substitutions)]), + ); + + return new ClassType(this.declaration, newSubstitutions, this.isNullable); } override updateNullability(isNullable: boolean): ClassType { - return new ClassType(this.declaration, isNullable); + return new ClassType(this.declaration, this.substitutions, isNullable); } } @@ -294,6 +379,10 @@ export class EnumType extends NamedType { return other.declaration === this.declaration && other.isNullable === this.isNullable; } + override substituteTypeParameters(_substitutions: TypeParameterSubstitutions): Type { + return this; + } + override updateNullability(isNullable: boolean): EnumType { return new EnumType(this.declaration, isNullable); } @@ -317,11 +406,42 @@ export class EnumVariantType extends NamedType { return other.declaration === this.declaration && other.isNullable === this.isNullable; } + override substituteTypeParameters(_substitutions: TypeParameterSubstitutions): Type { + return this; + } + override updateNullability(isNullable: boolean): EnumVariantType { return new EnumVariantType(this.declaration, isNullable); } } +export class TypeParameterType extends NamedType { + constructor( + declaration: SdsTypeParameter, + override readonly isNullable: boolean, + ) { + super(declaration); + } + + override equals(other: unknown): boolean { + if (other === this) { + return true; + } else if (!(other instanceof TypeParameterType)) { + return false; + } + + return other.declaration === this.declaration && other.isNullable === this.isNullable; + } + + override substituteTypeParameters(substitutions: TypeParameterSubstitutions): Type { + return substitutions.get(this.declaration) ?? this; + } + + override updateNullability(isNullable: boolean): TypeParameterType { + return new TypeParameterType(this.declaration, isNullable); + } +} + /** * A type that represents an actual class, enum, or enum variant instead of an instance of it. */ @@ -346,6 +466,12 @@ export class StaticType extends Type { return `$type<${this.instanceType}>`; } + override substituteTypeParameters(_substitutions: TypeParameterSubstitutions): StaticType { + // The substitutions are only meaningful for instances of a declaration, not for the declaration itself. Hence, + // we don't substitute anything here. + return this; + } + override unwrap(): Type { return this; } @@ -387,6 +513,14 @@ export class UnionType extends Type { return `union<${this.possibleTypes.join(', ')}>`; } + override substituteTypeParameters(substitutions: TypeParameterSubstitutions): UnionType { + if (isEmpty(substitutions)) { + return this; + } + + return new UnionType(...this.possibleTypes.map((it) => it.substituteTypeParameters(substitutions))); + } + override unwrap(): Type { if (this.possibleTypes.length === 1) { return this.possibleTypes[0]!.unwrap(); @@ -418,7 +552,11 @@ class UnknownTypeClass extends Type { } override toString(): string { - return '?'; + return '$unknown'; + } + + override substituteTypeParameters(_substitutions: TypeParameterSubstitutions): Type { + return this; } override unwrap(): Type { @@ -431,3 +569,24 @@ class UnknownTypeClass extends Type { } export const UnknownType = new UnknownTypeClass(); + +// ------------------------------------------------------------------------------------------------- +// Helpers +// ------------------------------------------------------------------------------------------------- + +const substitutionsAreEqual = ( + a: Map | undefined, + b: Map | undefined, +): boolean => { + if (a?.size !== b?.size) { + return false; + } + + const aEntries = Array.from(a?.entries() ?? []); + const bEntries = Array.from(b?.entries() ?? []); + + return aEntries.every(([aEntry, aValue], i) => { + const [bEntry, bValue] = bEntries[i]!; + return aEntry === bEntry && aValue.equals(bValue); + }); +}; diff --git a/packages/safe-ds-lang/src/language/typing/safe-ds-class-hierarchy.ts b/packages/safe-ds-lang/src/language/typing/safe-ds-class-hierarchy.ts index d6494a48d..7b03b68ff 100644 --- a/packages/safe-ds-lang/src/language/typing/safe-ds-class-hierarchy.ts +++ b/packages/safe-ds-lang/src/language/typing/safe-ds-class-hierarchy.ts @@ -41,13 +41,13 @@ export class SafeDsClassHierarchy { return stream(this.superclassesGenerator(node)); } - private *superclassesGenerator(node: SdsClass | undefined): Generator { + private *superclassesGenerator(node: SdsClass): Generator { const visited = new Set(); - let current = this.parentClassOrUndefined(node); + let current = this.parentClass(node); while (current && !visited.has(current)) { yield current; visited.add(current); - current = this.parentClassOrUndefined(current); + current = this.parentClass(current); } const anyClass = this.builtinClasses.Any; @@ -68,7 +68,7 @@ export class SafeDsClassHierarchy { * Returns the parent class of the given class, or undefined if there is no parent class. Only the first parent * type is considered, i.e. multiple inheritance is not supported. */ - private parentClassOrUndefined(node: SdsClass | undefined): SdsClass | undefined { + private parentClass(node: SdsClass | undefined): SdsClass | undefined { const [firstParentType] = getParentTypes(node); if (isSdsNamedType(firstParentType)) { const declaration = firstParentType.declaration?.ref; diff --git a/packages/safe-ds-lang/src/language/typing/safe-ds-core-types.ts b/packages/safe-ds-lang/src/language/typing/safe-ds-core-types.ts index e6dec7671..0ae219bd1 100644 --- a/packages/safe-ds-lang/src/language/typing/safe-ds-core-types.ts +++ b/packages/safe-ds-lang/src/language/typing/safe-ds-core-types.ts @@ -2,7 +2,8 @@ import { WorkspaceCache } from 'langium'; import { SafeDsClasses } from '../builtins/safe-ds-classes.js'; import { SdsClass } from '../generated/ast.js'; import { SafeDsServices } from '../safe-ds-module.js'; -import { ClassType, Type, UnknownType } from './model.js'; +import { ClassType, Type, TypeParameterSubstitutions, UnknownType } from './model.js'; +import { getTypeParameters } from '../helpers/nodeProperties.js'; export class SafeDsCoreTypes { private readonly builtinClasses: SafeDsClasses; @@ -33,12 +34,34 @@ export class SafeDsCoreTypes { return this.createCoreType(this.builtinClasses.Int); } - get List(): Type { - return this.createCoreType(this.builtinClasses.List); + List(elementType: Type): Type { + const list = this.builtinClasses.List; + const elementTypeParameter = getTypeParameters(list)[0]; + + if (!list || !elementTypeParameter) { + /* c8 ignore next 2 */ + return UnknownType; + } + + let substitutions = new Map([[elementTypeParameter, elementType]]); + return new ClassType(list, substitutions, false); } - get Map(): Type { - return this.createCoreType(this.builtinClasses.Map); + Map(keyType: Type, valueType: Type): Type { + const map = this.builtinClasses.Map; + const keyTypeParameter = getTypeParameters(map)[0]; + const valueTypeParameter = getTypeParameters(map)[1]; + + if (!map || !keyTypeParameter || !valueTypeParameter) { + /* c8 ignore next 2 */ + return UnknownType; + } + + const substitutions = new Map([ + [keyTypeParameter, keyType], + [valueTypeParameter, valueType], + ]); + return new ClassType(map, substitutions, false); } get Nothing(): Type { @@ -58,13 +81,14 @@ export class SafeDsCoreTypes { } private createCoreType(coreClass: SdsClass | undefined, isNullable: boolean = false): Type { - /* c8 ignore start */ if (!coreClass) { + /* c8 ignore next 2 */ return UnknownType; } - /* c8 ignore stop */ const key = `${coreClass.name}~${isNullable}`; - return this.cache.get(key, () => new ClassType(coreClass, isNullable)); + return this.cache.get(key, () => new ClassType(coreClass, NO_SUBSTITUTIONS, isNullable)); } } + +const NO_SUBSTITUTIONS: TypeParameterSubstitutions = new Map(); diff --git a/packages/safe-ds-lang/src/language/typing/safe-ds-type-checker.ts b/packages/safe-ds-lang/src/language/typing/safe-ds-type-checker.ts index 9bb5ed2ba..b570d7b82 100644 --- a/packages/safe-ds-lang/src/language/typing/safe-ds-type-checker.ts +++ b/packages/safe-ds-lang/src/language/typing/safe-ds-type-checker.ts @@ -1,7 +1,14 @@ -import { getContainerOfType } from 'langium'; +import { getContainerOfType, stream } from 'langium'; import type { SafeDsClasses } from '../builtins/safe-ds-classes.js'; import { isSdsEnum, type SdsAbstractResult, SdsDeclaration } from '../generated/ast.js'; -import { Enum, EnumVariant, getParameters, Parameter } from '../helpers/nodeProperties.js'; +import { + Enum, + EnumVariant, + getParameters, + getTypeParameters, + Parameter, + TypeParameter, +} from '../helpers/nodeProperties.js'; import { Constant } from '../partialEvaluation/model.js'; import { SafeDsServices } from '../safe-ds-module.js'; import { @@ -14,12 +21,14 @@ import { NamedTupleType, StaticType, Type, + TypeParameterType, UnionType, UnknownType, } from './model.js'; import { SafeDsClassHierarchy } from './safe-ds-class-hierarchy.js'; import { SafeDsCoreTypes } from './safe-ds-core-types.js'; import type { SafeDsTypeComputer } from './safe-ds-type-computer.js'; +import { isEmpty } from '../../helpers/collections.js'; export class SafeDsTypeChecker { private readonly builtinClasses: SafeDsClasses; @@ -41,9 +50,15 @@ export class SafeDsTypeChecker { /** * Checks whether {@link type} is assignable {@link other}. */ - isAssignableTo = (type: Type, other: Type): boolean => { + isAssignableTo = (type: Type, other: Type, options?: IsAssignableToOptions): boolean => { + const ignoreTypeParameters = options?.ignoreTypeParameters ?? false; + if (type === UnknownType || other === UnknownType) { return false; + } else if (type instanceof TypeParameterType || other instanceof TypeParameterType) { + /* c8 ignore next 3 */ + // TODO(LR): This must be updated when we work on type parameter constraints. + return true; } else if (other instanceof UnionType) { return other.possibleTypes.some((it) => this.isAssignableTo(type, it)); } @@ -51,7 +66,7 @@ export class SafeDsTypeChecker { if (type instanceof CallableType) { return this.callableTypeIsAssignableTo(type, other); } else if (type instanceof ClassType) { - return this.classTypeIsAssignableTo(type, other); + return this.classTypeIsAssignableTo(type, other, ignoreTypeParameters); } else if (type instanceof EnumType) { return this.enumTypeIsAssignableTo(type, other); } else if (type instanceof EnumVariantType) { @@ -128,13 +143,46 @@ export class SafeDsTypeChecker { } } - private classTypeIsAssignableTo(type: ClassType, other: Type): boolean { + private classTypeIsAssignableTo(type: ClassType, other: Type, ignoreTypeParameters: boolean): boolean { if (type.isNullable && !other.isNullable) { return false; } if (other instanceof ClassType) { - return this.classHierarchy.isEqualToOrSubclassOf(type.declaration, other.declaration); + if (type.declaration === this.builtinClasses.Nothing) { + return true; + } else if (!this.classHierarchy.isEqualToOrSubclassOf(type.declaration, other.declaration)) { + return false; + } + + // We are done already if we ignore type parameters or if the other type has no type parameters + const typeParameters = getTypeParameters(other.declaration); + if (ignoreTypeParameters || isEmpty(typeParameters)) { + return true; + } + + // Get the parent type that refers to the same class as `other` + const candidate = stream([type], this.typeComputer().streamSupertypes(type)).find( + (it) => it.declaration === other.declaration, + ); + if (!candidate) { + /* c8 ignore next 2 */ + return false; + } + + // Check type parameters + return typeParameters.every((it) => { + const candidateType = candidate.substitutions.get(it) ?? UnknownType; + const otherType = other.substitutions.get(it) ?? UnknownType; + + if (TypeParameter.isInvariant(it)) { + return candidateType !== UnknownType && candidateType.equals(otherType); + } else if (TypeParameter.isCovariant(it)) { + return this.isAssignableTo(candidateType, otherType); + } else { + return this.isAssignableTo(otherType, candidateType); + } + }); } else { return false; } @@ -260,7 +308,7 @@ export class SafeDsTypeChecker { } // ----------------------------------------------------------------------------------------------------------------- - // canBeTypeOfConstantParameter + // Other // ----------------------------------------------------------------------------------------------------------------- /** @@ -285,4 +333,24 @@ export class SafeDsTypeChecker { return type instanceof LiteralType || type === UnknownType; } }; + + /** + * Checks whether {@link type} some kind of list (with any element type). + */ + isList(type: Type): type is ClassType { + return this.isAssignableTo(type, this.coreTypes.List(UnknownType), { ignoreTypeParameters: true }); + } + + /** + * Checks whether {@link type} some kind of map (with any key/value types). + */ + isMap(type: Type): type is ClassType { + return this.isAssignableTo(type, this.coreTypes.Map(UnknownType, UnknownType), { + ignoreTypeParameters: true, + }); + } +} + +interface IsAssignableToOptions { + ignoreTypeParameters?: boolean; } 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 26e99cec5..1cb591e23 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 @@ -1,4 +1,13 @@ -import { AstNode, AstNodeLocator, getContainerOfType, getDocument, stream, WorkspaceCache } from 'langium'; +import { + AstNode, + AstNodeLocator, + EMPTY_STREAM, + getContainerOfType, + getDocument, + Stream, + stream, + WorkspaceCache, +} from 'langium'; import { isEmpty } from '../../helpers/collections.js'; import { isSdsAnnotation, @@ -48,6 +57,7 @@ import { type SdsBlockLambda, SdsCall, SdsCallableType, + SdsClass, SdsDeclaration, SdsExpression, type SdsExpressionLambda, @@ -56,18 +66,23 @@ import { SdsInfixOperation, SdsLiteralType, SdsMemberAccess, + SdsNamedType, SdsParameter, SdsPrefixOperation, SdsReference, SdsSegment, SdsType, + SdsTypeArgument, + SdsTypeParameter, } from '../generated/ast.js'; import { getAssignees, getLiterals, getParameters, + getParentTypes, getResults, getTypeArguments, + getTypeParameters, streamBlockLambdaResults, } from '../helpers/nodeProperties.js'; import { SafeDsNodeMapper } from '../helpers/safe-ds-node-mapper.js'; @@ -93,6 +108,8 @@ import { NamedType, StaticType, Type, + TypeParameterSubstitutions, + TypeParameterType, UnionType, UnknownType, } from './model.js'; @@ -128,11 +145,13 @@ export class SafeDsTypeComputer { /** * Computes the type of the given node. */ - computeType(node: AstNode | undefined): Type { + computeType(node: AstNode | undefined, substitutions: TypeParameterSubstitutions = NO_SUBSTITUTIONS): Type { if (!node) { return UnknownType; } - return this.nodeTypeCache.get(this.getNodeId(node), () => this.doComputeType(node).unwrap()); + + const unsubstitutedType = this.nodeTypeCache.get(this.getNodeId(node), () => this.doComputeType(node).unwrap()); + return unsubstitutedType.substituteTypeParameters(substitutions); } private getNodeId(node: AstNode) { @@ -187,7 +206,7 @@ export class SafeDsTypeComputer { } else if (isSdsAttribute(node)) { return this.computeType(node.type); } else if (isSdsClass(node)) { - return new ClassType(node, false); + return new ClassType(node, NO_SUBSTITUTIONS, false); } else if (isSdsEnum(node)) { return new EnumType(node, false); } else if (isSdsEnumVariant(node)) { @@ -205,7 +224,7 @@ export class SafeDsTypeComputer { } else if (isSdsSegment(node)) { return this.computeTypeOfCallableWithManifestTypes(node); } else if (isSdsTypeParameter(node)) { - return UnknownType; + return new TypeParameterType(node, false); } /* c8 ignore start */ else { return UnknownType; } /* c8 ignore stop */ @@ -301,9 +320,21 @@ export class SafeDsTypeComputer { // Terminal cases if (isSdsList(node)) { - return this.coreTypes.List; + const elementType = this.lowestCommonSupertype(...node.elements.map((it) => this.computeType(it))); + return this.coreTypes.List(elementType); } else if (isSdsMap(node)) { - return this.coreTypes.Map; + let keyType = this.lowestCommonSupertype(...node.entries.map((it) => this.computeType(it.key))); + + // Keeping literal types for keys is too strict: We would otherwise infer the key type of `{"a": 1, "b": 2}` + // as `Literal<"a", "b">`. But then we would be unable to pass an unknown `String` as the key in an indexed + // access. Where possible, we already validate the existence of keys in indexed accesses using the partial + // evaluator. + if (keyType instanceof LiteralType) { + keyType = this.computeClassTypeForLiteralType(keyType); + } + + const valueType = this.lowestCommonSupertype(...node.entries.map((it) => this.computeType(it.value))); + return this.coreTypes.Map(keyType, valueType); } else if (isSdsTemplateString(node)) { return this.coreTypes.String; } @@ -430,9 +461,10 @@ export class SafeDsTypeComputer { private computeTypeOfIndexedAccess(node: SdsIndexedAccess): Type { const receiverType = this.computeType(node.receiver); - if (receiverType.equals(this.coreTypes.List) || receiverType.equals(this.coreTypes.Map)) { - // TODO: access type arguments - return this.coreTypes.AnyOrNull; + if (this.typeChecker.isList(receiverType)) { + return receiverType.getTypeParameterTypeByIndex(0); + } else if (this.typeChecker.isMap(receiverType)) { + return receiverType.getTypeParameterTypeByIndex(1); } else { return UnknownType; } @@ -442,7 +474,10 @@ export class SafeDsTypeComputer { const leftOperandType = this.computeType(node.leftOperand); const rightOperandType = this.computeType(node.rightOperand); - if (leftOperandType.equals(this.coreTypes.Int) && rightOperandType.equals(this.coreTypes.Int)) { + if ( + this.typeChecker.isAssignableTo(leftOperandType, this.coreTypes.Int) && + this.typeChecker.isAssignableTo(rightOperandType, this.coreTypes.Int) + ) { return this.coreTypes.Int; } else { return this.coreTypes.Float; @@ -476,9 +511,9 @@ export class SafeDsTypeComputer { } private computeTypeOfArithmeticPrefixOperation(node: SdsPrefixOperation): Type { - const leftOperandType = this.computeType(node.operand); + const operandType = this.computeType(node.operand); - if (leftOperandType.equals(this.coreTypes.Int)) { + if (this.typeChecker.isAssignableTo(operandType, this.coreTypes.Int)) { return this.coreTypes.Int; } else { return this.coreTypes.Float; @@ -504,7 +539,7 @@ export class SafeDsTypeComputer { } else if (isSdsMemberType(node)) { return this.computeType(node.member); } else if (isSdsNamedType(node)) { - return this.computeType(node.declaration?.ref).updateNullability(node.isNullable); + return this.computeTypeOfNamedType(node); } else if (isSdsUnionType(node)) { const typeArguments = getTypeArguments(node.typeArgumentList); return new UnionType(...typeArguments.map((typeArgument) => this.computeType(typeArgument.value))); @@ -522,6 +557,43 @@ export class SafeDsTypeComputer { } /* c8 ignore stop */ } + private computeTypeOfNamedType(node: SdsNamedType) { + const unparameterizedType = this.computeType(node.declaration?.ref).updateNullability(node.isNullable); + if (!(unparameterizedType instanceof ClassType)) { + return unparameterizedType; + } + + const substitutions = this.getTypeParameterSubstitutionsForNamedType(node, unparameterizedType.declaration); + return new ClassType(unparameterizedType.declaration, substitutions, node.isNullable); + } + + private getTypeParameterSubstitutionsForNamedType(node: SdsNamedType, clazz: SdsClass): TypeParameterSubstitutions { + const typeParameters = getTypeParameters(clazz); + if (isEmpty(typeParameters)) { + return NO_SUBSTITUTIONS; + } + + // Map type parameters to the first type argument that sets it + const typeArgumentsByTypeParameters = new Map(); + for (const typeArgument of getTypeArguments(node)) { + const typeParameter = this.nodeMapper.typeArgumentToTypeParameter(typeArgument); + if (typeParameter && !typeArgumentsByTypeParameters.has(typeParameter)) { + typeArgumentsByTypeParameters.set(typeParameter, typeArgument); + } + } + + // Compute substitutions (ordered by the position of the type parameters) + const result = new Map(); + + for (const typeParameter of typeParameters) { + const typeArgument = typeArgumentsByTypeParameters.get(typeParameter); + const type = this.computeType(typeArgument?.value ?? typeParameter.defaultValue); + result.set(typeParameter, type); + } + + return result; + } + // ----------------------------------------------------------------------------------------------------------------- // Compute class types for literal types and their constants // ----------------------------------------------------------------------------------------------------------------- @@ -580,7 +652,7 @@ export class SafeDsTypeComputer { // Class-based types if (!isEmpty(groupedTypes.classTypes) || !isEmpty(groupedTypes.constants)) { if (!isEmpty(groupedTypes.enumTypes) || !isEmpty(groupedTypes.enumVariantTypes)) { - return this.getAny(isNullable); + return this.Any(isNullable); } else { return this.lowestCommonSupertypeForClassBasedTypes( groupedTypes.classTypes, @@ -624,7 +696,11 @@ export class SafeDsTypeComputer { }; for (const type of types) { - if (type instanceof ClassType) { + if (type.equals(this.coreTypes.Nothing)) { + // Do nothing + } else if (type.equals(this.coreTypes.NothingOrNull)) { + result.constants.push(NullConstant); + } else if (type instanceof ClassType) { result.classTypes.push(type); } else if (type instanceof EnumType) { result.enumTypes.push(type); @@ -661,13 +737,17 @@ export class SafeDsTypeComputer { const other = [...classTypes.slice(1), literalType]; for (const candidateClass of candidateClasses) { - const candidateType = new ClassType(candidateClass, isNullable); + // TODO: handle type parameters + const candidateType = new ClassType(candidateClass, NO_SUBSTITUTIONS, isNullable); + // TODO: We need to check first without type parameters + // Then check with type parameters and whether we can find a common supertype for them, respecting variance + // If we can't, try the next candidate if (this.isCommonSupertype(candidateType, other)) { return candidateType; } } /* c8 ignore next */ - return this.getAny(isNullable); + return this.Any(isNullable); } private lowestCommonSupertypeForEnumBasedTypes( @@ -698,16 +778,67 @@ export class SafeDsTypeComputer { } } - return this.getAny(isNullable); + return this.Any(isNullable); } private isCommonSupertype(candidate: Type, otherTypes: Type[]): boolean { return otherTypes.every((it) => this.typeChecker.isAssignableTo(it, candidate)); } - private getAny(isNullable: boolean): Type { + private Any(isNullable: boolean): Type { return isNullable ? this.coreTypes.AnyOrNull : this.coreTypes.Any; } + + // ----------------------------------------------------------------------------------------------------------------- + // Stream supertypes + // ----------------------------------------------------------------------------------------------------------------- + + /** + * Returns a stream of all declared super types of the given type. Direct ancestors are returned first, followed by + * their ancestors and so on. The given type is never included in the stream. + * + * Compared to `ClassHierarchy.streamSuperTypes`, this method cannot be used to detect cycles in the inheritance + * hierarchy. However, it can handle type parameters on parent types and substitute them accordingly. + */ + streamSupertypes(type: ClassType | undefined): Stream { + if (!type) { + return EMPTY_STREAM; + } + + return stream(this.supertypesGenerator(type)); + } + + private *supertypesGenerator(type: ClassType): Generator { + // Compared to `ClassHierarchy.superclassesGenerator`, we already include the given type in the list of visited + // elements, since this method here is not used to detect cycles. + const visited = new Set([type.declaration]); + let current = this.parentClassType(type); + while (current && !visited.has(current.declaration)) { + yield current; + visited.add(current.declaration); + current = this.parentClassType(current); + } + + const Any = this.coreTypes.Any; + if (Any instanceof ClassType && !visited.has(Any.declaration)) { + yield Any; + } + } + + /** + * Tries to evaluate the first parent type of the class to a `ClassType` and returns it if successful. Type + * parameters get substituted. If there is no parent type or the parent type is not a class, `undefined` is + * returned. + */ + private parentClassType(type: ClassType | undefined): ClassType | undefined { + const [firstParentTypeNode] = getParentTypes(type?.declaration); + const firstParentType = this.computeType(firstParentTypeNode, type?.substitutions); + + if (firstParentType instanceof ClassType) { + return firstParentType; + } + return undefined; + } } interface GroupTypesResult { @@ -717,3 +848,5 @@ interface GroupTypesResult { enumVariantTypes: EnumVariantType[]; hasTypeWithUnknownSupertype: boolean; } + +const NO_SUBSTITUTIONS: TypeParameterSubstitutions = new Map(); diff --git a/packages/safe-ds-lang/src/language/validation/experimentalLanguageFeatures.ts b/packages/safe-ds-lang/src/language/validation/experimentalLanguageFeatures.ts index cac316a03..e137b0ce3 100644 --- a/packages/safe-ds-lang/src/language/validation/experimentalLanguageFeatures.ts +++ b/packages/safe-ds-lang/src/language/validation/experimentalLanguageFeatures.ts @@ -1,15 +1,10 @@ import { hasContainerOfType, ValidationAcceptor } from 'langium'; import { - isSdsIndexedAccess, isSdsMap, - isSdsTypeArgumentList, isSdsUnionType, type SdsConstraintList, - type SdsIndexedAccess, type SdsLiteralType, type SdsMap, - type SdsTypeArgumentList, - type SdsTypeParameterList, type SdsUnionType, } from '../generated/ast.js'; import { SafeDsServices } from '../safe-ds-module.js'; @@ -33,27 +28,6 @@ export const constraintListsShouldBeUsedWithCaution = (services: SafeDsServices) }; }; -export const indexedAccessesShouldBeUsedWithCaution = (services: SafeDsServices) => { - const settingsProvider = services.workspace.SettingsProvider; - - return async (node: SdsIndexedAccess, accept: ValidationAcceptor) => { - if (!(await settingsProvider.shouldValidateExperimentalLanguageFeature())) { - /* c8 ignore next 2 */ - return; - } - - // There's already a warning on the container - if (hasContainerOfType(node.$container, isSdsIndexedAccess)) { - return; - } - - accept('warning', 'Indexed accesses are experimental and may change without prior notice.', { - node, - code: CODE_EXPERIMENTAL_LANGUAGE_FEATURE, - }); - }; -}; - export const literalTypesShouldBeUsedWithCaution = (services: SafeDsServices) => { const settingsProvider = services.workspace.SettingsProvider; @@ -92,54 +66,6 @@ export const mapsShouldBeUsedWithCaution = (services: SafeDsServices) => { }; }; -export const typeArgumentListsShouldBeUsedWithCaution = (services: SafeDsServices) => { - const settingsProvider = services.workspace.SettingsProvider; - - return async (node: SdsTypeArgumentList, accept: ValidationAcceptor) => { - if (!(await settingsProvider.shouldValidateExperimentalLanguageFeature())) { - /* c8 ignore next 2 */ - return; - } - - // There's already a warning on the container - if ( - hasContainerOfType(node.$container, isSdsTypeArgumentList) || - hasContainerOfType(node.$container, isSdsUnionType) - ) { - return; - } - - accept( - 'warning', - 'Type argument lists & type arguments are experimental and may change without prior notice.', - { - node, - code: CODE_EXPERIMENTAL_LANGUAGE_FEATURE, - }, - ); - }; -}; - -export const typeParameterListsShouldBeUsedWithCaution = (services: SafeDsServices) => { - const settingsProvider = services.workspace.SettingsProvider; - - return async (node: SdsTypeParameterList, accept: ValidationAcceptor) => { - if (!(await settingsProvider.shouldValidateExperimentalLanguageFeature())) { - /* c8 ignore next 2 */ - return; - } - - accept( - 'warning', - 'Type parameter lists & type parameters are experimental and may change without prior notice.', - { - node, - code: CODE_EXPERIMENTAL_LANGUAGE_FEATURE, - }, - ); - }; -}; - export const unionTypesShouldBeUsedWithCaution = (services: SafeDsServices) => { const settingsProvider = services.workspace.SettingsProvider; diff --git a/packages/safe-ds-lang/src/language/validation/other/expressions/indexedAccess.ts b/packages/safe-ds-lang/src/language/validation/other/expressions/indexedAccess.ts index a01e0485e..678d9958e 100644 --- a/packages/safe-ds-lang/src/language/validation/other/expressions/indexedAccess.ts +++ b/packages/safe-ds-lang/src/language/validation/other/expressions/indexedAccess.ts @@ -6,8 +6,8 @@ import { EvaluatedList, EvaluatedMap, IntConstant } from '../../../partialEvalua export const CODE_INDEXED_ACCESS_INVALID_INDEX = 'indexed-access/invalid-index'; export const indexedAccessIndexMustBeValid = (services: SafeDsServices) => { - const coreTypes = services.types.CoreTypes; const partialEvaluator = services.evaluation.PartialEvaluator; + const typeChecker = services.types.TypeChecker; const typeComputer = services.types.TypeComputer; return (node: SdsIndexedAccess, accept: ValidationAcceptor): void => { @@ -46,7 +46,7 @@ export const indexedAccessIndexMustBeValid = (services: SafeDsServices) => { } const receiverType = typeComputer.computeType(node.receiver); - if (receiverType.equals(coreTypes.List)) { + if (typeChecker.isList(receiverType)) { if (indexValue.value < 0) { accept('error', `List index '${indexValue}' is out of bounds.`, { node, diff --git a/packages/safe-ds-lang/src/language/validation/safe-ds-validator.ts b/packages/safe-ds-lang/src/language/validation/safe-ds-validator.ts index bc4dcc493..0e2743727 100644 --- a/packages/safe-ds-lang/src/language/validation/safe-ds-validator.ts +++ b/packages/safe-ds-lang/src/language/validation/safe-ds-validator.ts @@ -27,11 +27,8 @@ import { singleUseAnnotationsMustNotBeRepeated } from './builtins/repeatable.js' import { annotationCallMustHaveCorrectTarget, targetShouldNotHaveDuplicateEntries } from './builtins/target.js'; import { constraintListsShouldBeUsedWithCaution, - indexedAccessesShouldBeUsedWithCaution, literalTypesShouldBeUsedWithCaution, mapsShouldBeUsedWithCaution, - typeArgumentListsShouldBeUsedWithCaution, - typeParameterListsShouldBeUsedWithCaution, unionTypesShouldBeUsedWithCaution, } from './experimentalLanguageFeatures.js'; import { @@ -276,7 +273,6 @@ export const registerValidationChecks = function (services: SafeDsServices) { indexedAccessIndexMustBeValid(services), indexedAccessIndexMustHaveCorrectType(services), indexedAccessReceiverMustBeListOrMap(services), - indexedAccessesShouldBeUsedWithCaution(services), ], SdsInfixOperation: [ divisionDivisorMustNotBeZero(services), @@ -350,13 +346,11 @@ export const registerValidationChecks = function (services: SafeDsServices) { ], SdsStatement: [statementMustDoSomething(services)], SdsTemplateString: [templateStringMustHaveExpressionBetweenTwoStringParts], - SdsTypeArgumentList: [typeArgumentListsShouldBeUsedWithCaution(services)], SdsTypeCast: [typeCastExpressionMustHaveUnknownType(services)], SdsTypeParameter: [typeParameterMustHaveSufficientContext, typeParameterMustBeUsedInCorrectPosition(services)], SdsTypeParameterConstraint: [typeParameterConstraintLeftOperandMustBeOwnTypeParameter], SdsTypeParameterList: [ typeParameterListMustNotHaveRequiredTypeParametersAfterOptionalTypeParameters, - typeParameterListsShouldBeUsedWithCaution(services), typeParameterListShouldNotBeEmpty(services), ], SdsUnionType: [ diff --git a/packages/safe-ds-lang/src/language/validation/types.ts b/packages/safe-ds-lang/src/language/validation/types.ts index f53f638db..925706cf4 100644 --- a/packages/safe-ds-lang/src/language/validation/types.ts +++ b/packages/safe-ds-lang/src/language/validation/types.ts @@ -95,18 +95,13 @@ export const callReceiverMustBeCallable = (services: SafeDsServices) => { }; export const indexedAccessReceiverMustBeListOrMap = (services: SafeDsServices) => { - const coreTypes = services.types.CoreTypes; const typeChecker = services.types.TypeChecker; const typeComputer = services.types.TypeComputer; return (node: SdsIndexedAccess, accept: ValidationAcceptor): void => { const receiverType = typeComputer.computeType(node.receiver); - if ( - node.receiver && - !typeChecker.isAssignableTo(receiverType, coreTypes.List) && - !typeChecker.isAssignableTo(receiverType, coreTypes.Map) - ) { - accept('error', `Expected type '${coreTypes.List}' or '${coreTypes.Map}' but got '${receiverType}'.`, { + if (node.receiver && !typeChecker.isList(receiverType) && !typeChecker.isMap(receiverType)) { + accept('error', `Expected type 'List' or 'Map' but got '${receiverType}'.`, { node: node.receiver, code: CODE_TYPE_MISMATCH, }); @@ -121,7 +116,7 @@ export const indexedAccessIndexMustHaveCorrectType = (services: SafeDsServices) return (node: SdsIndexedAccess, accept: ValidationAcceptor): void => { const receiverType = typeComputer.computeType(node.receiver); - if (typeChecker.isAssignableTo(receiverType, coreTypes.List)) { + if (typeChecker.isList(receiverType)) { const indexType = typeComputer.computeType(node.index); if (!typeChecker.isAssignableTo(indexType, coreTypes.Int)) { accept('error', `Expected type '${coreTypes.Int}' but got '${indexType}'.`, { @@ -130,6 +125,16 @@ export const indexedAccessIndexMustHaveCorrectType = (services: SafeDsServices) code: CODE_TYPE_MISMATCH, }); } + } else if (typeChecker.isMap(receiverType)) { + const keyType = receiverType.getTypeParameterTypeByIndex(0); + const indexType = typeComputer.computeType(node.index); + if (!typeChecker.isAssignableTo(indexType, keyType)) { + accept('error', `Expected type '${keyType}' but got '${indexType}'.`, { + node, + property: 'index', + code: CODE_TYPE_MISMATCH, + }); + } } }; }; diff --git a/packages/safe-ds-lang/src/resources/builtins/safeds/lang/coreClasses.sdsstub b/packages/safe-ds-lang/src/resources/builtins/safeds/lang/coreClasses.sdsstub index f8a1f5773..6e428584a 100644 --- a/packages/safe-ds-lang/src/resources/builtins/safeds/lang/coreClasses.sdsstub +++ b/packages/safe-ds-lang/src/resources/builtins/safeds/lang/coreClasses.sdsstub @@ -41,12 +41,12 @@ class Float sub Number /** * A list of elements. */ -class List +class List /** * A map of keys to values. */ -class Map +class Map /** * Some text. diff --git a/packages/safe-ds-lang/tests/helpers/testDescription.ts b/packages/safe-ds-lang/tests/helpers/testDescription.ts index e036d94b9..0e7c5d55a 100644 --- a/packages/safe-ds-lang/tests/helpers/testDescription.ts +++ b/packages/safe-ds-lang/tests/helpers/testDescription.ts @@ -49,7 +49,7 @@ export interface EqualsTest { /** * Produces a value of a different type. */ - valueOfOtherType: () => T; + valueOfOtherType?: () => T; } /** diff --git a/packages/safe-ds-lang/tests/language/lsp/safe-ds-document-symbol-provider.test.ts b/packages/safe-ds-lang/tests/language/lsp/safe-ds-document-symbol-provider.test.ts index 22ce50ff0..4efdbf37b 100644 --- a/packages/safe-ds-lang/tests/language/lsp/safe-ds-document-symbol-provider.test.ts +++ b/packages/safe-ds-lang/tests/language/lsp/safe-ds-document-symbol-provider.test.ts @@ -35,10 +35,12 @@ describe('SafeDsSemanticTokenProvider', async () => { { name: 'a', kind: SymbolKind.Property, + detail: ': Int', }, { name: 'b', kind: SymbolKind.Property, + detail: ': (p: Int) -> (r: Int)', }, ], }, diff --git a/packages/safe-ds-lang/tests/language/partialEvaluation/model.test.ts b/packages/safe-ds-lang/tests/language/partialEvaluation/model.test.ts index addbc3757..dd536fac3 100644 --- a/packages/safe-ds-lang/tests/language/partialEvaluation/model.test.ts +++ b/packages/safe-ds-lang/tests/language/partialEvaluation/model.test.ts @@ -143,12 +143,6 @@ describe('partial evaluation model', async () => { expect(nodeInstance.equals(nodeInstance)).toBeTruthy(); }); - it(`should return false if the other node is an instance of another class (${ - value().constructor.name - })`, () => { - expect(value().equals(valueOfOtherType())).toBeFalsy(); - }); - it(`should return true if both nodes have the same values (${value().constructor.name})`, () => { expect(value().equals(value())).toBeTruthy(); }); @@ -158,6 +152,14 @@ describe('partial evaluation model', async () => { expect(value().equals(unequalValueOfSameType())).toBeFalsy(); }); } + + if (valueOfOtherType) { + it(`should return false if the other node is an instance of another class (${ + value().constructor.name + })`, () => { + expect(value().equals(valueOfOtherType())).toBeFalsy(); + }); + } }); const toStringTests: ToStringTest[] = [ diff --git a/packages/safe-ds-lang/tests/language/purity/model.test.ts b/packages/safe-ds-lang/tests/language/purity/model.test.ts index 46240ba02..9b2bf9851 100644 --- a/packages/safe-ds-lang/tests/language/purity/model.test.ts +++ b/packages/safe-ds-lang/tests/language/purity/model.test.ts @@ -54,12 +54,6 @@ describe('purity model', async () => { expect(typeInstance.equals(typeInstance)).toBeTruthy(); }); - it(`should return false if the other type is an instance of another class (${ - value().constructor.name - })`, () => { - expect(value().equals(valueOfOtherType())).toBeFalsy(); - }); - it(`should return true if both types have the same values (${value().constructor.name})`, () => { expect(value().equals(value())).toBeTruthy(); }); @@ -69,6 +63,14 @@ describe('purity model', async () => { expect(value().equals(unequalValueOfSameType())).toBeFalsy(); }); } + + if (valueOfOtherType) { + it(`should return false if the other type is an instance of another class (${ + value().constructor.name + })`, () => { + expect(value().equals(valueOfOtherType())).toBeFalsy(); + }); + } }); const toStringTests: ToStringTest[] = [ 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 8c6738d5d..d72158885 100644 --- a/packages/safe-ds-lang/tests/language/typing/model.test.ts +++ b/packages/safe-ds-lang/tests/language/typing/model.test.ts @@ -1,8 +1,12 @@ import { NodeFileSystem } from 'langium/node'; import { describe, expect, it } from 'vitest'; import { isSdsClass, isSdsEnum, isSdsEnumVariant, isSdsFunction } from '../../../src/language/generated/ast.js'; -import { getParameters, getResults } from '../../../src/language/helpers/nodeProperties.js'; -import { createSafeDsServicesWithBuiltins } from '../../../src/language/index.js'; +import { + createSafeDsServicesWithBuiltins, + getParameters, + getResults, + getTypeParameters, +} from '../../../src/language/index.js'; import { BooleanConstant, IntConstant, NullConstant } from '../../../src/language/partialEvaluation/model.js'; import { CallableType, @@ -14,6 +18,8 @@ import { NamedTupleType, StaticType, Type, + TypeParameterSubstitutions, + TypeParameterType, UnionType, UnknownType, } from '../../../src/language/typing/model.js'; @@ -26,7 +32,7 @@ const code = ` fun f2(), class C1 - class C2 + class C2 enum MyEnum1 { MyEnumVariant1 @@ -41,6 +47,8 @@ const result = getResults(callable1.resultList)[0]!; const callable2 = await getNodeOfType(services, code, isSdsFunction, 1); const class1 = await getNodeOfType(services, code, isSdsClass, 0); const class2 = await getNodeOfType(services, code, isSdsClass, 1); +const typeParameter1 = getTypeParameters(class2)[0]!; +const typeParameter2 = getTypeParameters(class2)[1]!; const enum1 = await getNodeOfType(services, code, isSdsEnum, 0); const enum2 = await getNodeOfType(services, code, isSdsEnum, 1); const enumVariant1 = await getNodeOfType(services, code, isSdsEnumVariant, 0); @@ -71,10 +79,22 @@ describe('type model', async () => { valueOfOtherType: () => UnknownType, }, { - value: () => new ClassType(class1, true), - unequalValueOfSameType: () => new ClassType(class2, true), + value: () => new ClassType(class1, new Map(), true), + unequalValueOfSameType: () => new ClassType(class2, new Map(), true), valueOfOtherType: () => UnknownType, }, + { + value: () => new ClassType(class1, new Map(), false), + unequalValueOfSameType: () => new ClassType(class1, new Map(), true), + }, + { + value: () => new ClassType(class2, new Map([[typeParameter1, UnknownType]]), true), + unequalValueOfSameType: () => new ClassType(class2, new Map([[typeParameter1, new UnionType()]]), true), + }, + { + value: () => new ClassType(class2, new Map(), true), + unequalValueOfSameType: () => new ClassType(class2, new Map([[typeParameter1, new UnionType()]]), true), + }, { value: () => new EnumType(enum1, true), unequalValueOfSameType: () => new EnumType(enum2, true), @@ -86,8 +106,13 @@ describe('type model', async () => { valueOfOtherType: () => UnknownType, }, { - value: () => new StaticType(new ClassType(class1, false)), - unequalValueOfSameType: () => new StaticType(new ClassType(class2, false)), + value: () => new TypeParameterType(typeParameter1, true), + unequalValueOfSameType: () => new TypeParameterType(typeParameter2, true), + valueOfOtherType: () => UnknownType, + }, + { + value: () => new StaticType(new ClassType(class1, new Map(), false)), + unequalValueOfSameType: () => new StaticType(new ClassType(class2, new Map(), false)), valueOfOtherType: () => UnknownType, }, { @@ -106,12 +131,6 @@ describe('type model', async () => { expect(typeInstance.equals(typeInstance)).toBeTruthy(); }); - it(`should return false if the other type is an instance of another class (${ - value().constructor.name - })`, () => { - expect(value().equals(valueOfOtherType())).toBeFalsy(); - }); - it(`should return true if both types have the same values (${value().constructor.name})`, () => { expect(value().equals(value())).toBeTruthy(); }); @@ -121,6 +140,14 @@ describe('type model', async () => { expect(value().equals(unequalValueOfSameType())).toBeFalsy(); }); } + + if (valueOfOtherType) { + it(`should return false if the other type is an instance of another class (${ + value().constructor.name + })`, () => { + expect(value().equals(valueOfOtherType())).toBeFalsy(); + }); + } }); const toStringTests: ToStringTest[] = [ @@ -131,7 +158,7 @@ describe('type model', async () => { new NamedTupleType(new NamedTupleEntry(parameter1, 'p1', UnknownType)), new NamedTupleType(), ), - expectedString: '(p1: ?) -> ()', + expectedString: '(p1: $unknown) -> ()', }, { value: new CallableType( @@ -140,7 +167,7 @@ describe('type model', async () => { new NamedTupleType(new NamedTupleEntry(parameter2, 'p2', UnknownType)), new NamedTupleType(), ), - expectedString: '(p2?: ?) -> ()', + expectedString: '(p2?: $unknown) -> ()', }, { value: new LiteralType(new BooleanConstant(true)), @@ -148,31 +175,55 @@ describe('type model', async () => { }, { value: new NamedTupleType(new NamedTupleEntry(parameter1, 'p1', UnknownType)), - expectedString: '(p1: ?)', + expectedString: '(p1: $unknown)', }, { - value: new ClassType(class1, true), + value: new ClassType(class1, new Map(), false), + expectedString: 'C1', + }, + { + value: new ClassType(class1, new Map(), true), expectedString: 'C1?', }, + { + value: new ClassType(class2, new Map([[typeParameter1, new UnionType()]]), true), + expectedString: 'C2>?', + }, + { + value: new EnumType(enum1, false), + expectedString: 'MyEnum1', + }, { value: new EnumType(enum1, true), expectedString: 'MyEnum1?', }, + { + value: new EnumVariantType(enumVariant1, false), + expectedString: 'MyEnumVariant1', + }, { value: new EnumVariantType(enumVariant1, true), expectedString: 'MyEnumVariant1?', }, { - value: new StaticType(new ClassType(class1, false)), + value: new TypeParameterType(typeParameter1, false), + expectedString: 'K', + }, + { + value: new TypeParameterType(typeParameter1, true), + expectedString: 'K?', + }, + { + value: new StaticType(new ClassType(class1, new Map(), false)), expectedString: '$type', }, { value: new UnionType(UnknownType), - expectedString: 'union', + expectedString: 'union<$unknown>', }, { value: UnknownType, - expectedString: '?', + expectedString: '$unknown', }, ]; describe.each(toStringTests)('toString', ({ value, expectedString }) => { @@ -181,6 +232,104 @@ describe('type model', async () => { }); }); + const substitutions1 = new Map([[typeParameter1, new LiteralType(new IntConstant(1n))]]); + const substituteTypeParametersTest: SubstituteTypeParametersTest[] = [ + { + type: new CallableType( + callable1, + undefined, + new NamedTupleType(new NamedTupleEntry(parameter1, 'p1', new TypeParameterType(typeParameter1, false))), + new NamedTupleType(new NamedTupleEntry(result, 'r', new TypeParameterType(typeParameter1, false))), + ), + substitutions: substitutions1, + expectedType: new CallableType( + callable1, + undefined, + new NamedTupleType(new NamedTupleEntry(parameter1, 'p1', new LiteralType(new IntConstant(1n)))), + new NamedTupleType(new NamedTupleEntry(result, 'r', new LiteralType(new IntConstant(1n)))), + ), + }, + { + type: new LiteralType(new BooleanConstant(true)), + substitutions: substitutions1, + expectedType: new LiteralType(new BooleanConstant(true)), + }, + { + type: new NamedTupleType( + new NamedTupleEntry(parameter1, 'p1', new TypeParameterType(typeParameter1, false)), + ), + substitutions: substitutions1, + expectedType: new NamedTupleType( + new NamedTupleEntry(parameter1, 'p1', new LiteralType(new IntConstant(1n))), + ), + }, + { + type: new ClassType( + class1, + new Map([[typeParameter2, new TypeParameterType(typeParameter1, false)]]), + false, + ), + substitutions: substitutions1, + expectedType: new ClassType( + class1, + new Map([[typeParameter2, new LiteralType(new IntConstant(1n))]]), + false, + ), + }, + { + type: new EnumType(enum1, false), + substitutions: substitutions1, + expectedType: new EnumType(enum1, false), + }, + { + type: new EnumVariantType(enumVariant1, false), + substitutions: substitutions1, + expectedType: new EnumVariantType(enumVariant1, false), + }, + { + type: new TypeParameterType(typeParameter1, false), + substitutions: substitutions1, + expectedType: new LiteralType(new IntConstant(1n)), + }, + { + type: new TypeParameterType(typeParameter2, false), + substitutions: substitutions1, + expectedType: new TypeParameterType(typeParameter2, false), + }, + { + type: new StaticType( + new ClassType(class1, new Map([[typeParameter1, new TypeParameterType(typeParameter2, false)]]), false), + ), + substitutions: substitutions1, + expectedType: new StaticType( + new ClassType(class1, new Map([[typeParameter1, new TypeParameterType(typeParameter2, false)]]), false), + ), + }, + { + type: new UnionType( + new ClassType(class1, new Map([[typeParameter2, new TypeParameterType(typeParameter1, false)]]), false), + ), + substitutions: substitutions1, + expectedType: new UnionType( + new ClassType(class1, new Map([[typeParameter2, new LiteralType(new IntConstant(1n))]]), false), + ), + }, + { + type: UnknownType, + substitutions: substitutions1, + expectedType: UnknownType, + }, + ]; + describe.each(substituteTypeParametersTest)('substituteTypeParameters', ({ type, substitutions, expectedType }) => { + it(`should return the expected value (${type.constructor.name} -- ${type})`, () => { + expect(type.substituteTypeParameters(substitutions).equals(expectedType)).toBeTruthy(); + }); + + it(`should not change if no substitutions are passed (${type.constructor.name} -- ${type})`, () => { + expect(type.substituteTypeParameters(new Map()).equals(type)).toBeTruthy(); + }); + }); + const unwrapTests: UnwrapTest[] = [ { type: new CallableType(callable1, undefined, new NamedTupleType(), new NamedTupleType()), @@ -219,8 +368,8 @@ describe('type model', async () => { ), }, { - type: new ClassType(class1, false), - expectedType: new ClassType(class1, false), + type: new ClassType(class1, new Map(), false), + expectedType: new ClassType(class1, new Map(), false), }, { type: new EnumType(enum1, false), @@ -231,20 +380,24 @@ describe('type model', async () => { expectedType: new EnumVariantType(enumVariant1, false), }, { - type: new StaticType(new ClassType(class1, false)), - expectedType: new StaticType(new ClassType(class1, false)), + type: new TypeParameterType(typeParameter1, false), + expectedType: new TypeParameterType(typeParameter1, false), + }, + { + type: new StaticType(new ClassType(class1, new Map(), false)), + expectedType: new StaticType(new ClassType(class1, new Map(), false)), }, { type: new UnionType(), expectedType: new UnionType(), }, { - type: new UnionType(new ClassType(class1, false)), - expectedType: new ClassType(class1, false), + type: new UnionType(new ClassType(class1, new Map(), false)), + expectedType: new ClassType(class1, new Map(), false), }, { - type: new UnionType(new UnionType(new ClassType(class1, false))), - expectedType: new ClassType(class1, false), + type: new UnionType(new UnionType(new ClassType(class1, new Map(), false))), + expectedType: new ClassType(class1, new Map(), false), }, { type: UnknownType, @@ -257,7 +410,7 @@ describe('type model', async () => { }); }); - const updateNullabilityTest: UpdateNullabilityTest[] = [ + const updateNullabilityTests: UpdateNullabilityTest[] = [ { type: new CallableType(callable1, undefined, new NamedTupleType(), new NamedTupleType()), isNullable: true, @@ -302,14 +455,14 @@ describe('type model', async () => { expectedType: new NamedTupleType(), }, { - type: new ClassType(class1, false), + type: new ClassType(class1, new Map(), false), isNullable: true, - expectedType: new ClassType(class1, true), + expectedType: new ClassType(class1, new Map(), true), }, { - type: new ClassType(class1, true), + type: new ClassType(class1, new Map(), true), isNullable: false, - expectedType: new ClassType(class1, false), + expectedType: new ClassType(class1, new Map(), false), }, { type: new EnumType(enum1, false), @@ -332,14 +485,27 @@ describe('type model', async () => { expectedType: new EnumVariantType(enumVariant1, false), }, { - type: new StaticType(new ClassType(class1, false)), + type: new StaticType(new ClassType(class1, new Map(), false)), + isNullable: true, + expectedType: new UnionType( + new StaticType(new ClassType(class1, new Map(), false)), + new LiteralType(NullConstant), + ), + }, + { + type: new StaticType(new ClassType(class1, new Map(), false)), + isNullable: false, + expectedType: new StaticType(new ClassType(class1, new Map(), false)), + }, + { + type: new TypeParameterType(typeParameter1, false), isNullable: true, - expectedType: new UnionType(new StaticType(new ClassType(class1, false)), new LiteralType(NullConstant)), + expectedType: new TypeParameterType(typeParameter1, true), }, { - type: new StaticType(new ClassType(class1, false)), + type: new TypeParameterType(typeParameter1, true), isNullable: false, - expectedType: new StaticType(new ClassType(class1, false)), + expectedType: new TypeParameterType(typeParameter1, false), }, { type: new UnionType(), @@ -352,24 +518,24 @@ describe('type model', async () => { expectedType: new UnionType(), }, { - type: new UnionType(new ClassType(class1, false)), + type: new UnionType(new ClassType(class1, new Map(), false)), isNullable: true, - expectedType: new UnionType(new ClassType(class1, true)), + expectedType: new UnionType(new ClassType(class1, new Map(), true)), }, { - type: new UnionType(new ClassType(class1, false)), + type: new UnionType(new ClassType(class1, new Map(), false)), isNullable: false, - expectedType: new UnionType(new ClassType(class1, false)), + expectedType: new UnionType(new ClassType(class1, new Map(), false)), }, { - type: new UnionType(new ClassType(class1, true)), + type: new UnionType(new ClassType(class1, new Map(), true)), isNullable: true, - expectedType: new UnionType(new ClassType(class1, true)), + expectedType: new UnionType(new ClassType(class1, new Map(), true)), }, { - type: new UnionType(new ClassType(class1, true)), + type: new UnionType(new ClassType(class1, new Map(), true)), isNullable: false, - expectedType: new UnionType(new ClassType(class1, false)), + expectedType: new UnionType(new ClassType(class1, new Map(), false)), }, { type: UnknownType, @@ -382,7 +548,7 @@ describe('type model', async () => { expectedType: UnknownType, }, ]; - describe.each(updateNullabilityTest)('updateNullability', ({ type, isNullable, expectedType }) => { + describe.each(updateNullabilityTests)('updateNullability', ({ type, isNullable, expectedType }) => { it(`should return the expected value (${type.constructor.name} -- ${type})`, () => { expect(type.updateNullability(isNullable).equals(expectedType)).toBeTruthy(); }); @@ -400,11 +566,13 @@ describe('type model', async () => { type: new CallableType( callable1, undefined, - new NamedTupleType(new NamedTupleEntry(parameter1, 'p1', new ClassType(class1, false))), + new NamedTupleType( + new NamedTupleEntry(parameter1, 'p1', new ClassType(class1, new Map(), false)), + ), new NamedTupleType(), ), index: 0, - expectedType: new ClassType(class1, false), + expectedType: new ClassType(class1, new Map(), false), }, ])('should return the type of the parameter at the given index (%#)', ({ type, index, expectedType }) => { expect(type.getParameterTypeByIndex(index).equals(expectedType)).toBeTruthy(); @@ -412,6 +580,25 @@ describe('type model', async () => { }); }); + describe('ClassType', () => { + describe('getTypeParameterTypeByIndex', () => { + it.each([ + { + type: new ClassType(class1, new Map(), false), + index: 0, + expectedType: UnknownType, + }, + { + type: new ClassType(class2, new Map([[typeParameter1, new UnionType()]]), false), + index: 0, + expectedType: new UnionType(), + }, + ])('should return the type of the parameter at the given index (%#)', ({ type, index, expectedType }) => { + expect(type.getTypeParameterTypeByIndex(index).equals(expectedType)).toBeTruthy(); + }); + }); + }); + describe('NamedTupleType', () => { describe('getTypeOfEntryByIndex', () => { it.each([ @@ -421,9 +608,11 @@ describe('type model', async () => { expectedType: UnknownType, }, { - type: new NamedTupleType(new NamedTupleEntry(parameter1, 'p1', new ClassType(class1, false))), + type: new NamedTupleType( + new NamedTupleEntry(parameter1, 'p1', new ClassType(class1, new Map(), false)), + ), index: 0, - expectedType: new ClassType(class1, false), + expectedType: new ClassType(class1, new Map(), false), }, ])('should return the entry at the given index (%#)', ({ type, index, expectedType }) => { expect(type.getTypeOfEntryByIndex(index).equals(expectedType)).toBeTruthy(); @@ -432,6 +621,26 @@ describe('type model', async () => { }); }); +/** + * Tests for {@link Type.substituteTypeParameters}. + */ +interface SubstituteTypeParametersTest { + /** + * The type to test. + */ + type: Type; + + /** + * The type parameter substitutions to apply. + */ + substitutions: TypeParameterSubstitutions; + + /** + * The expected result. + */ + expectedType: Type; +} + /** * Tests for {@link Type.unwrap}. */ diff --git a/packages/safe-ds-lang/tests/language/typing/type checker/canBeTypeOfConstantParameter.test.ts b/packages/safe-ds-lang/tests/language/typing/type checker/canBeTypeOfConstantParameter.test.ts index 06f272cb2..c2280c2ed 100644 --- a/packages/safe-ds-lang/tests/language/typing/type checker/canBeTypeOfConstantParameter.test.ts +++ b/packages/safe-ds-lang/tests/language/typing/type checker/canBeTypeOfConstantParameter.test.ts @@ -1,8 +1,7 @@ import { NodeFileSystem } from 'langium/node'; import { describe, expect, it } from 'vitest'; import { isSdsClass, isSdsEnum, isSdsModule } from '../../../../src/language/generated/ast.js'; -import { getEnumVariants, getModuleMembers } from '../../../../src/language/helpers/nodeProperties.js'; -import { createSafeDsServicesWithBuiltins } from '../../../../src/language/index.js'; +import { createSafeDsServicesWithBuiltins, getEnumVariants, getModuleMembers } from '../../../../src/language/index.js'; import { ClassType, EnumType, @@ -63,11 +62,11 @@ describe('SafeDsTypeChecker', async () => { expected: true, }, { - type: coreTypes.List, + type: coreTypes.List(coreTypes.Int), expected: true, }, { - type: coreTypes.Map, + type: coreTypes.Map(coreTypes.String, coreTypes.Int), expected: true, }, { diff --git a/packages/safe-ds-lang/tests/language/typing/type checker/isAssignableTo.test.ts b/packages/safe-ds-lang/tests/language/typing/type checker/isAssignableTo.test.ts index 38eff1831..55b5074a6 100644 --- a/packages/safe-ds-lang/tests/language/typing/type checker/isAssignableTo.test.ts +++ b/packages/safe-ds-lang/tests/language/typing/type checker/isAssignableTo.test.ts @@ -2,14 +2,16 @@ import { streamAllContents } from 'langium'; import { NodeFileSystem } from 'langium/node'; import { describe, expect, it } from 'vitest'; import { + isSdsAttribute, isSdsClass, isSdsEnum, isSdsEnumVariant, isSdsFunction, isSdsModule, + SdsDeclaration, } from '../../../../src/language/generated/ast.js'; -import { getModuleMembers } from '../../../../src/language/helpers/nodeProperties.js'; -import { createSafeDsServicesWithBuiltins } from '../../../../src/language/index.js'; +import { createSafeDsServicesWithBuiltins, getClassMembers, getModuleMembers } from '../../../../src/language/index.js'; + import { BooleanConstant, FloatConstant, @@ -36,611 +38,942 @@ const coreTypes = services.types.CoreTypes; const typeChecker = services.types.TypeChecker; const typeComputer = services.types.TypeComputer; -const code = ` - fun func1() -> () - fun func2(p: Int = 0) -> () - fun func3(p: Int) -> () - fun func4(r: Int) -> () - fun func5(p: Any) -> () - fun func6(p: String) -> () - fun func7() -> (r: Int) - fun func8() -> (s: Int) - fun func9() -> (r: Any) - fun func10() -> (r: String) - fun func11() -> (r: Class1) - fun func12() -> (r: Enum1) - - class Class1(p: Int) - class Class2() sub Class1 - class Class3 - - enum Enum1 { - Variant1(p: Int) - Variant2 - } - enum Enum2 -`; -const module = await getNodeOfType(services, code, isSdsModule); -const functions = getModuleMembers(module).filter(isSdsFunction); -const callableType1 = typeComputer.computeType(functions[0]); -const callableType2 = typeComputer.computeType(functions[1]); -const callableType3 = typeComputer.computeType(functions[2]); -const callableType4 = typeComputer.computeType(functions[3]); -const callableType5 = typeComputer.computeType(functions[4]); -const callableType6 = typeComputer.computeType(functions[5]); -const callableType7 = typeComputer.computeType(functions[6]); -const callableType8 = typeComputer.computeType(functions[7]); -const callableType9 = typeComputer.computeType(functions[8]); -const callableType10 = typeComputer.computeType(functions[9]); -const callableType11 = typeComputer.computeType(functions[10]); -const callableType12 = typeComputer.computeType(functions[11]); - -const classes = getModuleMembers(module).filter(isSdsClass); -const class1 = classes[0]; -const class2 = classes[1]; -const class3 = classes[2]; -const classType1 = typeComputer.computeType(class1) as ClassType; -const classType2 = typeComputer.computeType(class2) as ClassType; -const classType3 = typeComputer.computeType(class3) as ClassType; - -const enums = getModuleMembers(module).filter(isSdsEnum); -const enum1 = enums[0]; -const enum2 = enums[1]; -const enumType1 = typeComputer.computeType(enum1) as EnumType; -const enumType2 = typeComputer.computeType(enum2) as EnumType; - -const enumVariants = streamAllContents(module).filter(isSdsEnumVariant).toArray(); -const enumVariant1 = enumVariants[0]; -const enumVariant2 = enumVariants[1]; -const enumVariantType1 = typeComputer.computeType(enumVariant1) as EnumVariantType; -const enumVariantType2 = typeComputer.computeType(enumVariant2) as EnumVariantType; - describe('SafeDsTypeChecker', async () => { - const testCases: IsAssignableToTest[] = [ - { - type1: callableType1, - type2: callableType1, - expected: true, - }, - { - type1: callableType2, - type2: callableType1, - expected: true, - }, - { - type1: callableType1, - type2: callableType2, - expected: false, - }, - { - type1: callableType2, - type2: callableType3, - expected: true, - }, - { - type1: callableType3, - type2: callableType2, - expected: false, - }, - { - type1: callableType3, - type2: callableType1, - expected: false, - }, - { - type1: callableType3, - type2: callableType4, - expected: false, - }, - { - type1: callableType3, - type2: callableType5, - expected: false, - }, - { - type1: callableType5, - type2: callableType3, - expected: true, - }, - { - type1: callableType6, - type2: callableType3, - expected: false, - }, - { - type1: callableType7, - type2: callableType1, - expected: true, - }, - { - type1: callableType1, - type2: callableType7, - expected: false, - }, - { - type1: callableType8, - type2: callableType7, - expected: true, - }, - { - type1: callableType9, - type2: callableType7, - expected: false, - }, - { - type1: callableType7, - type2: callableType9, - expected: true, - }, - { - type1: callableType10, - type2: callableType7, - expected: false, - }, - // Callable type to class type - { - type1: callableType1, - type2: coreTypes.Any, - expected: true, - }, - { - type1: callableType1, - type2: coreTypes.AnyOrNull, - expected: true, - }, - // Callable type to other - { - type1: callableType1, - type2: enumType1, - expected: false, - }, - // Class type to class type - { - type1: classType1, - type2: classType1, - expected: true, - }, - { - type1: classType2, - type2: classType1, - expected: true, - }, - { - type1: classType1, - type2: classType3, - expected: false, - }, - { - type1: classType1, - type2: coreTypes.Any, - expected: true, - }, - { - type1: classType2.updateNullability(true), - type2: classType1, - expected: false, - }, - { - type1: classType2.updateNullability(true), - type2: classType1.updateNullability(true), - expected: true, - }, - // Class type to union type - { - type1: classType1, - type2: new UnionType(classType1), - expected: true, - }, - { - type1: classType1, - type2: new UnionType(classType3), - expected: false, - }, - // Class type to other - { - type1: classType1, - type2: enumType1, - expected: false, - }, - // Enum type to class type - { - type1: enumType1, - type2: classType1, - expected: false, - }, - { - type1: enumType1, - type2: coreTypes.Any, - expected: true, - }, - { - type1: enumType1.updateNullability(true), - type2: coreTypes.Any, - expected: false, - }, - { - type1: enumType1.updateNullability(true), - type2: coreTypes.AnyOrNull, - expected: true, - }, - // Enum type to enum type - { - type1: enumType1, - type2: enumType1, - expected: true, - }, - { - type1: enumType1, - type2: enumType2, - expected: false, - }, - { - type1: enumType1.updateNullability(true), - type2: enumType1, - expected: false, - }, - { - type1: enumType1.updateNullability(true), - type2: enumType1.updateNullability(true), - expected: true, - }, - // Enum type to union type - { - type1: enumType1, - type2: new UnionType(enumType1), - expected: true, - }, - { - type1: enumType1, - type2: new UnionType(enumType2), - expected: false, - }, - // Enum type to other - { - type1: enumType1, - type2: new LiteralType(), - expected: false, - }, - // Enum variant type to class type - { - type1: enumVariantType1, - type2: classType1, - expected: false, - }, - { - type1: enumVariantType1, - type2: coreTypes.Any, - expected: true, - }, - { - type1: enumVariantType1.updateNullability(true), - type2: coreTypes.Any, - expected: false, - }, - { - type1: enumVariantType1.updateNullability(true), - type2: coreTypes.AnyOrNull, - expected: true, - }, - // Enum variant type to enum type - { - type1: enumVariantType1, - type2: enumType1, - expected: true, - }, - { - type1: enumVariantType1, - type2: enumType2, - expected: false, - }, - { - type1: enumVariantType1.updateNullability(true), - type2: enumType1, - expected: false, - }, - { - type1: enumVariantType1.updateNullability(true), - type2: enumType1.updateNullability(true), - expected: true, - }, - // Enum variant type to enum variant type - { - type1: enumVariantType1, - type2: enumVariantType1, - expected: true, - }, - { - type1: enumVariantType1, - type2: enumVariantType2, - expected: false, - }, - { - type1: enumVariantType1.updateNullability(true), - type2: enumVariantType1, - expected: false, - }, - { - type1: enumVariantType1.updateNullability(true), - type2: enumVariantType1.updateNullability(true), - expected: true, - }, - // Enum variant type to union type - { - type1: enumVariantType1, - type2: new UnionType(enumType1), - expected: true, - }, - { - type1: enumVariantType1, - type2: new UnionType(enumType2), - expected: false, - }, - // Enum variant type to other - { - type1: enumVariantType1, - type2: new LiteralType(), - expected: false, - }, - // Literal type to class type - { - type1: new LiteralType(), - type2: classType1, - expected: true, - }, - { - type1: new LiteralType(new BooleanConstant(true)), - type2: coreTypes.Boolean, - expected: true, - }, - { - type1: new LiteralType(new FloatConstant(1.5)), - type2: coreTypes.Float, - expected: true, - }, - { - type1: new LiteralType(new IntConstant(1n)), - type2: coreTypes.Int, - expected: true, - }, - { - type1: new LiteralType(NullConstant), - type2: coreTypes.NothingOrNull, - expected: true, - }, - { - type1: new LiteralType(new StringConstant('')), - type2: coreTypes.String, - expected: true, - }, - { - type1: new LiteralType(new IntConstant(1n)), - type2: coreTypes.Any, - expected: true, - }, - { - type1: new LiteralType(new IntConstant(1n)), - type2: coreTypes.String, - expected: false, - }, - { - type1: new LiteralType(new IntConstant(1n), NullConstant), - type2: coreTypes.Int.updateNullability(true), - expected: true, - }, - { - type1: new LiteralType(new IntConstant(1n), NullConstant), - type2: coreTypes.Int, - expected: false, - }, - { - type1: new LiteralType(new IntConstant(1n), new StringConstant('')), - type2: coreTypes.Int, - expected: false, - }, - { - type1: new LiteralType(new IntConstant(1n), new StringConstant('')), - type2: coreTypes.String, - expected: false, - }, - { - type1: new LiteralType(new IntConstant(1n), new StringConstant('')), - type2: coreTypes.Any, - expected: true, - }, - { - type1: new LiteralType(new IntConstant(1n), new StringConstant(''), NullConstant), - type2: coreTypes.AnyOrNull, - expected: true, - }, - // Literal type to literal type - { - type1: new LiteralType(), - type2: new LiteralType(), - expected: true, - }, - { - type1: new LiteralType(new BooleanConstant(true)), - type2: new LiteralType(new BooleanConstant(true)), - expected: true, - }, - { - type1: new LiteralType(new BooleanConstant(true)), - type2: new LiteralType(new BooleanConstant(false)), - expected: false, - }, - { - type1: new LiteralType(new BooleanConstant(true)), - type2: new LiteralType(new FloatConstant(1.5)), - expected: false, - }, - { - type1: new LiteralType(new BooleanConstant(true), NullConstant), - type2: new LiteralType(new BooleanConstant(true), NullConstant), - expected: true, - }, - { - type1: new LiteralType(new BooleanConstant(true), NullConstant), - type2: new LiteralType(new BooleanConstant(true)), - expected: false, - }, - // Literal type to union type - { - type1: new LiteralType(new IntConstant(1n)), - type2: new UnionType(coreTypes.Any), - expected: true, - }, - { - type1: new LiteralType(new IntConstant(1n)), - type2: new UnionType(coreTypes.String), - expected: false, - }, - // Literal type to other - { - type1: new LiteralType(new IntConstant(1n)), - type2: enumType1, - expected: false, - }, - // Named tuple type to named tuple type - { - type1: new NamedTupleType(), - type2: new NamedTupleType(), - expected: true, - }, - { - type1: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Int)), - type2: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Int)), - expected: true, - }, - { - type1: new NamedTupleType(new NamedTupleEntry(class1, 'a', coreTypes.Int)), - type2: new NamedTupleType(new NamedTupleEntry(class2, 'a', coreTypes.Int)), - expected: true, - }, - { - type1: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Int)), - type2: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Any)), - expected: true, - }, - { - type1: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Any)), - type2: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Int)), - expected: false, - }, - { - type1: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Int)), - type2: new NamedTupleType(new NamedTupleEntry(undefined, 'b', coreTypes.Int)), - expected: false, - }, - // Named tuple type to other - { - type1: new NamedTupleType(), - type2: enumType1, - expected: false, - }, - // Static type to callable type - { - type1: new StaticType(classType1), - type2: callableType1, - expected: false, - }, - { - type1: new StaticType(classType1), - type2: callableType3, - expected: true, - }, - { - type1: new StaticType(classType2), - type2: callableType11, - expected: true, - }, - { - type1: new StaticType(classType3), - type2: callableType1, - expected: false, - }, - { - type1: new StaticType(enumType1), - type2: callableType1, - expected: false, - }, - { - type1: new StaticType(enumVariantType1), - type2: callableType1, - expected: false, - }, - { - type1: new StaticType(enumVariantType1), - type2: callableType3, - expected: true, - }, - { - type1: new StaticType(enumVariantType2), - type2: callableType12, - expected: true, - }, - // Static type to static type - { - type1: new StaticType(classType1), - type2: new StaticType(classType1), - expected: true, - }, - { - type1: new StaticType(classType1), - type2: new StaticType(classType2), - expected: false, - }, - // Static type to other - { - type1: new StaticType(classType1), - type2: enumType1, - expected: false, - }, - // Union type to X - { - type1: new UnionType(), - type2: classType1, - expected: true, - }, - { - type1: new UnionType(classType1), - type2: classType1, - expected: true, - }, - { - type1: new UnionType(classType1, classType2), - type2: classType1, - expected: true, - }, - { - type1: new UnionType(classType1, classType3), - type2: classType1, - expected: false, - }, - { - type1: new UnionType(classType1.updateNullability(true)), - type2: classType1, - expected: false, - }, - { - type1: new UnionType(classType1.updateNullability(true)), - type2: classType1.updateNullability(true), - expected: true, - }, - // Unknown to X - { - type1: UnknownType, - type2: UnknownType, - expected: false, - }, - ]; - - describe.each(testCases)('isAssignableTo', ({ type1, type2, expected }) => { - it(`should check whether ${type1} is assignable to ${type2}`, () => { - expect(typeChecker.isAssignableTo(type1, type2)).toBe(expected); + describe('basic', async () => { + const code = ` + fun func1() -> () + fun func2(p: Int = 0) -> () + fun func3(p: Int) -> () + fun func4(r: Int) -> () + fun func5(p: Any) -> () + fun func6(p: String) -> () + fun func7() -> (r: Int) + fun func8() -> (s: Int) + fun func9() -> (r: Any) + fun func10() -> (r: String) + fun func11() -> (r: Class1) + fun func12() -> (r: Enum1) + + class Class1(p: Int) + class Class2() sub Class1 + class Class3 + + class Class4 + + enum Enum1 { + Variant1(p: Int) + Variant2 + } + enum Enum2 + `; + const module = await getNodeOfType(services, code, isSdsModule); + const functions = getModuleMembers(module).filter(isSdsFunction); + const callableType1 = typeComputer.computeType(functions[0]); + const callableType2 = typeComputer.computeType(functions[1]); + const callableType3 = typeComputer.computeType(functions[2]); + const callableType4 = typeComputer.computeType(functions[3]); + const callableType5 = typeComputer.computeType(functions[4]); + const callableType6 = typeComputer.computeType(functions[5]); + const callableType7 = typeComputer.computeType(functions[6]); + const callableType8 = typeComputer.computeType(functions[7]); + const callableType9 = typeComputer.computeType(functions[8]); + const callableType10 = typeComputer.computeType(functions[9]); + const callableType11 = typeComputer.computeType(functions[10]); + const callableType12 = typeComputer.computeType(functions[11]); + + const classes = getModuleMembers(module).filter(isSdsClass); + const class1 = classes[0]; + const class2 = classes[1]; + const class3 = classes[2]; + const classType1 = typeComputer.computeType(class1) as ClassType; + const classType2 = typeComputer.computeType(class2) as ClassType; + const classType3 = typeComputer.computeType(class3) as ClassType; + + const enums = getModuleMembers(module).filter(isSdsEnum); + const enum1 = enums[0]; + const enum2 = enums[1]; + const enumType1 = typeComputer.computeType(enum1) as EnumType; + const enumType2 = typeComputer.computeType(enum2) as EnumType; + + const enumVariants = streamAllContents(module).filter(isSdsEnumVariant).toArray(); + const enumVariant1 = enumVariants[0]; + const enumVariant2 = enumVariants[1]; + const enumVariantType1 = typeComputer.computeType(enumVariant1) as EnumVariantType; + const enumVariantType2 = typeComputer.computeType(enumVariant2) as EnumVariantType; + + const testCases: IsAssignableToTest[] = [ + { + type1: callableType1, + type2: callableType1, + expected: true, + }, + { + type1: callableType2, + type2: callableType1, + expected: true, + }, + { + type1: callableType1, + type2: callableType2, + expected: false, + }, + { + type1: callableType2, + type2: callableType3, + expected: true, + }, + { + type1: callableType3, + type2: callableType2, + expected: false, + }, + { + type1: callableType3, + type2: callableType1, + expected: false, + }, + { + type1: callableType3, + type2: callableType4, + expected: false, + }, + { + type1: callableType3, + type2: callableType5, + expected: false, + }, + { + type1: callableType5, + type2: callableType3, + expected: true, + }, + { + type1: callableType6, + type2: callableType3, + expected: false, + }, + { + type1: callableType7, + type2: callableType1, + expected: true, + }, + { + type1: callableType1, + type2: callableType7, + expected: false, + }, + { + type1: callableType8, + type2: callableType7, + expected: true, + }, + { + type1: callableType9, + type2: callableType7, + expected: false, + }, + { + type1: callableType7, + type2: callableType9, + expected: true, + }, + { + type1: callableType10, + type2: callableType7, + expected: false, + }, + // Callable type to class type + { + type1: callableType1, + type2: coreTypes.Any, + expected: true, + }, + { + type1: callableType1, + type2: coreTypes.AnyOrNull, + expected: true, + }, + // Callable type to other + { + type1: callableType1, + type2: enumType1, + expected: false, + }, + // Class type to class type + { + type1: classType1, + type2: classType1, + expected: true, + }, + { + type1: classType2, + type2: classType1, + expected: true, + }, + { + type1: classType1, + type2: classType3, + expected: false, + }, + { + type1: classType1, + type2: coreTypes.Any, + expected: true, + }, + { + type1: classType2.updateNullability(true), + type2: classType1, + expected: false, + }, + { + type1: classType2.updateNullability(true), + type2: classType1.updateNullability(true), + expected: true, + }, + // Class type to union type + { + type1: classType1, + type2: new UnionType(classType1), + expected: true, + }, + { + type1: classType1, + type2: new UnionType(classType3), + expected: false, + }, + // Class type to other + { + type1: classType1, + type2: enumType1, + expected: false, + }, + // Enum type to class type + { + type1: enumType1, + type2: classType1, + expected: false, + }, + { + type1: enumType1, + type2: coreTypes.Any, + expected: true, + }, + { + type1: enumType1.updateNullability(true), + type2: coreTypes.Any, + expected: false, + }, + { + type1: enumType1.updateNullability(true), + type2: coreTypes.AnyOrNull, + expected: true, + }, + // Enum type to enum type + { + type1: enumType1, + type2: enumType1, + expected: true, + }, + { + type1: enumType1, + type2: enumType2, + expected: false, + }, + { + type1: enumType1.updateNullability(true), + type2: enumType1, + expected: false, + }, + { + type1: enumType1.updateNullability(true), + type2: enumType1.updateNullability(true), + expected: true, + }, + // Enum type to union type + { + type1: enumType1, + type2: new UnionType(enumType1), + expected: true, + }, + { + type1: enumType1, + type2: new UnionType(enumType2), + expected: false, + }, + // Enum type to other + { + type1: enumType1, + type2: new LiteralType(), + expected: false, + }, + // Enum variant type to class type + { + type1: enumVariantType1, + type2: classType1, + expected: false, + }, + { + type1: enumVariantType1, + type2: coreTypes.Any, + expected: true, + }, + { + type1: enumVariantType1.updateNullability(true), + type2: coreTypes.Any, + expected: false, + }, + { + type1: enumVariantType1.updateNullability(true), + type2: coreTypes.AnyOrNull, + expected: true, + }, + // Enum variant type to enum type + { + type1: enumVariantType1, + type2: enumType1, + expected: true, + }, + { + type1: enumVariantType1, + type2: enumType2, + expected: false, + }, + { + type1: enumVariantType1.updateNullability(true), + type2: enumType1, + expected: false, + }, + { + type1: enumVariantType1.updateNullability(true), + type2: enumType1.updateNullability(true), + expected: true, + }, + // Enum variant type to enum variant type + { + type1: enumVariantType1, + type2: enumVariantType1, + expected: true, + }, + { + type1: enumVariantType1, + type2: enumVariantType2, + expected: false, + }, + { + type1: enumVariantType1.updateNullability(true), + type2: enumVariantType1, + expected: false, + }, + { + type1: enumVariantType1.updateNullability(true), + type2: enumVariantType1.updateNullability(true), + expected: true, + }, + // Enum variant type to union type + { + type1: enumVariantType1, + type2: new UnionType(enumType1), + expected: true, + }, + { + type1: enumVariantType1, + type2: new UnionType(enumType2), + expected: false, + }, + // Enum variant type to other + { + type1: enumVariantType1, + type2: new LiteralType(), + expected: false, + }, + // Literal type to class type + { + type1: new LiteralType(), + type2: classType1, + expected: true, + }, + { + type1: new LiteralType(new BooleanConstant(true)), + type2: coreTypes.Boolean, + expected: true, + }, + { + type1: new LiteralType(new FloatConstant(1.5)), + type2: coreTypes.Float, + expected: true, + }, + { + type1: new LiteralType(new IntConstant(1n)), + type2: coreTypes.Int, + expected: true, + }, + { + type1: new LiteralType(NullConstant), + type2: coreTypes.NothingOrNull, + expected: true, + }, + { + type1: new LiteralType(new StringConstant('')), + type2: coreTypes.String, + expected: true, + }, + { + type1: new LiteralType(new IntConstant(1n)), + type2: coreTypes.Any, + expected: true, + }, + { + type1: new LiteralType(new IntConstant(1n)), + type2: coreTypes.String, + expected: false, + }, + { + type1: new LiteralType(new IntConstant(1n), NullConstant), + type2: coreTypes.Int.updateNullability(true), + expected: true, + }, + { + type1: new LiteralType(new IntConstant(1n), NullConstant), + type2: coreTypes.Int, + expected: false, + }, + { + type1: new LiteralType(new IntConstant(1n), new StringConstant('')), + type2: coreTypes.Int, + expected: false, + }, + { + type1: new LiteralType(new IntConstant(1n), new StringConstant('')), + type2: coreTypes.String, + expected: false, + }, + { + type1: new LiteralType(new IntConstant(1n), new StringConstant('')), + type2: coreTypes.Any, + expected: true, + }, + { + type1: new LiteralType(new IntConstant(1n), new StringConstant(''), NullConstant), + type2: coreTypes.AnyOrNull, + expected: true, + }, + // Literal type to literal type + { + type1: new LiteralType(), + type2: new LiteralType(), + expected: true, + }, + { + type1: new LiteralType(new BooleanConstant(true)), + type2: new LiteralType(new BooleanConstant(true)), + expected: true, + }, + { + type1: new LiteralType(new BooleanConstant(true)), + type2: new LiteralType(new BooleanConstant(false)), + expected: false, + }, + { + type1: new LiteralType(new BooleanConstant(true)), + type2: new LiteralType(new FloatConstant(1.5)), + expected: false, + }, + { + type1: new LiteralType(new BooleanConstant(true), NullConstant), + type2: new LiteralType(new BooleanConstant(true), NullConstant), + expected: true, + }, + { + type1: new LiteralType(new BooleanConstant(true), NullConstant), + type2: new LiteralType(new BooleanConstant(true)), + expected: false, + }, + // Literal type to union type + { + type1: new LiteralType(new IntConstant(1n)), + type2: new UnionType(coreTypes.Any), + expected: true, + }, + { + type1: new LiteralType(new IntConstant(1n)), + type2: new UnionType(coreTypes.String), + expected: false, + }, + // Literal type to other + { + type1: new LiteralType(new IntConstant(1n)), + type2: enumType1, + expected: false, + }, + // Named tuple type to named tuple type + { + type1: new NamedTupleType(), + type2: new NamedTupleType(), + expected: true, + }, + { + type1: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Int)), + type2: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Int)), + expected: true, + }, + { + type1: new NamedTupleType(new NamedTupleEntry(class1, 'a', coreTypes.Int)), + type2: new NamedTupleType(new NamedTupleEntry(class2, 'a', coreTypes.Int)), + expected: true, + }, + { + type1: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Int)), + type2: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Any)), + expected: true, + }, + { + type1: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Any)), + type2: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Int)), + expected: false, + }, + { + type1: new NamedTupleType(new NamedTupleEntry(undefined, 'a', coreTypes.Int)), + type2: new NamedTupleType(new NamedTupleEntry(undefined, 'b', coreTypes.Int)), + expected: false, + }, + // Named tuple type to other + { + type1: new NamedTupleType(), + type2: enumType1, + expected: false, + }, + // Static type to callable type + { + type1: new StaticType(classType1), + type2: callableType1, + expected: false, + }, + { + type1: new StaticType(classType1), + type2: callableType3, + expected: true, + }, + { + type1: new StaticType(classType2), + type2: callableType11, + expected: true, + }, + { + type1: new StaticType(classType3), + type2: callableType1, + expected: false, + }, + { + type1: new StaticType(enumType1), + type2: callableType1, + expected: false, + }, + { + type1: new StaticType(enumVariantType1), + type2: callableType1, + expected: false, + }, + { + type1: new StaticType(enumVariantType1), + type2: callableType3, + expected: true, + }, + { + type1: new StaticType(enumVariantType2), + type2: callableType12, + expected: true, + }, + // Static type to static type + { + type1: new StaticType(classType1), + type2: new StaticType(classType1), + expected: true, + }, + { + type1: new StaticType(classType1), + type2: new StaticType(classType2), + expected: false, + }, + // Static type to other + { + type1: new StaticType(classType1), + type2: enumType1, + expected: false, + }, + // Union type to X + { + type1: new UnionType(), + type2: classType1, + expected: true, + }, + { + type1: new UnionType(classType1), + type2: classType1, + expected: true, + }, + { + type1: new UnionType(classType1, classType2), + type2: classType1, + expected: true, + }, + { + type1: new UnionType(classType1, classType3), + type2: classType1, + expected: false, + }, + { + type1: new UnionType(classType1.updateNullability(true)), + type2: classType1, + expected: false, + }, + { + type1: new UnionType(classType1.updateNullability(true)), + type2: classType1.updateNullability(true), + expected: true, + }, + // Unknown to X + { + type1: UnknownType, + type2: UnknownType, + expected: false, + }, + ]; + + describe.each(testCases)('isAssignableTo', ({ type1, type2, expected }) => { + it(`should check whether ${type1} is assignable to ${type2}`, () => { + expect(typeChecker.isAssignableTo(type1, type2)).toBe(expected); + }); + }); + }); + + describe('class types with type parameters', async () => { + const code = ` + class TestClass { + attr any: MyAny + + attr baseClassInvariantAny: BaseClassInvariant + attr baseClassCovariantAny: BaseClassCovariant + attr baseClassContravariantAny: BaseClassContravariant + + attr baseClassInvariantNumber: BaseClassInvariant + attr baseClassCovariantNumber: BaseClassCovariant + attr baseClassContravariantNumber: BaseClassContravariant + + attr baseClassInvariantInt: BaseClassInvariant + attr baseClassCovariantInt: BaseClassCovariant + attr baseClassContravariantInt: BaseClassContravariant + + attr subclassParameterizedInvariantAny: SubclassParameterizedInvariant + attr subclassParameterizedCovariantAny: SubclassParameterizedCovariant + attr subclassParameterizedContravariantAny: SubclassParameterizedContravariant + + attr subclassParameterizedInvariantNumber: SubclassParameterizedInvariant + attr subclassParameterizedCovariantNumber: SubclassParameterizedCovariant + attr subclassParameterizedContravariantNumber: SubclassParameterizedContravariant + + attr subclassParameterizedInvariantInt: SubclassParameterizedInvariant + attr subclassParameterizedCovariantInt: SubclassParameterizedCovariant + attr subclassParameterizedContravariantInt: SubclassParameterizedContravariant + + attr subclassFixedInvariant: SubclassFixedInvariant + attr subclassFixedCovariant: SubclassFixedCovariant + attr subclassFixedContravariant: SubclassFixedContravariant + } + + class MyAny + class MyNumber sub MyAny + class MyInt sub MyNumber + + class BaseClassInvariant sub MyAny + class BaseClassCovariant sub MyAny + class BaseClassContravariant sub MyAny + + class SubclassParameterizedInvariant sub BaseClassInvariant + class SubclassParameterizedCovariant sub BaseClassCovariant + class SubclassParameterizedContravariant sub BaseClassContravariant + + class SubclassFixedInvariant sub BaseClassInvariant + class SubclassFixedCovariant sub BaseClassCovariant + class SubclassFixedContravariant sub BaseClassContravariant + `; + const module = await getNodeOfType(services, code, isSdsModule); + const classes = getModuleMembers(module).filter(isSdsClass); + const attributes = getClassMembers(classes[0]).filter(isSdsAttribute); + + const computeTypeOfAttributeWithName = computeTypeOfDeclarationWithName(attributes); + + const any = computeTypeOfAttributeWithName('any'); + + const baseClassInvariantAny = computeTypeOfAttributeWithName('baseClassInvariantAny'); + const baseClassCovariantAny = computeTypeOfAttributeWithName('baseClassCovariantAny'); + const baseClassContravariantAny = computeTypeOfAttributeWithName('baseClassContravariantAny'); + + const baseClassInvariantNumber = computeTypeOfAttributeWithName('baseClassInvariantNumber'); + const baseClassCovariantNumber = computeTypeOfAttributeWithName('baseClassCovariantNumber'); + const baseClassContravariantNumber = computeTypeOfAttributeWithName('baseClassContravariantNumber'); + + const baseClassInvariantInt = computeTypeOfAttributeWithName('baseClassInvariantInt'); + const baseClassCovariantInt = computeTypeOfAttributeWithName('baseClassCovariantInt'); + const baseClassContravariantInt = computeTypeOfAttributeWithName('baseClassContravariantInt'); + + const subclassParameterizedInvariantAny = computeTypeOfAttributeWithName('subclassParameterizedInvariantAny'); + const subclassParameterizedCovariantAny = computeTypeOfAttributeWithName('subclassParameterizedCovariantAny'); + const subclassParameterizedContravariantAny = computeTypeOfAttributeWithName( + 'subclassParameterizedContravariantAny', + ); + + const subclassParameterizedInvariantNumber = computeTypeOfAttributeWithName( + 'subclassParameterizedInvariantNumber', + ); + const subclassParameterizedCovariantNumber = computeTypeOfAttributeWithName( + 'subclassParameterizedCovariantNumber', + ); + const subclassParameterizedContravariantNumber = computeTypeOfAttributeWithName( + 'subclassParameterizedContravariantNumber', + ); + + const subclassParameterizedInvariantInt = computeTypeOfAttributeWithName('subclassParameterizedInvariantInt'); + const subclassParameterizedCovariantInt = computeTypeOfAttributeWithName('subclassParameterizedCovariantInt'); + const subclassParameterizedContravariantInt = computeTypeOfAttributeWithName( + 'subclassParameterizedContravariantInt', + ); + + const subclassFixedInvariant = computeTypeOfAttributeWithName('subclassFixedInvariant'); + const subclassFixedCovariant = computeTypeOfAttributeWithName('subclassFixedCovariant'); + const subclassFixedContravariant = computeTypeOfAttributeWithName('subclassFixedContravariant'); + + const testCases: IsAssignableToTest[] = [ + // Compare to MyAny + { + type1: baseClassInvariantAny, + type2: any, + expected: true, + }, + { + type1: subclassParameterizedCovariantAny, + type2: any, + expected: true, + }, + { + type1: subclassFixedContravariant, + type2: any, + expected: true, + }, + + // Compare to BaseClassInvariant + { + type1: subclassParameterizedInvariantAny, + type2: baseClassInvariantAny, + expected: true, + }, + { + type1: subclassParameterizedInvariantNumber, + type2: baseClassInvariantAny, + expected: false, + }, + { + type1: subclassParameterizedInvariantInt, + type2: baseClassInvariantAny, + expected: false, + }, + { + type1: subclassFixedInvariant, + type2: baseClassInvariantAny, + expected: false, + }, + + // Compare to BaseClassCovariant + { + type1: subclassParameterizedCovariantAny, + type2: baseClassCovariantAny, + expected: true, + }, + { + type1: subclassParameterizedCovariantNumber, + type2: baseClassCovariantAny, + expected: true, + }, + { + type1: subclassParameterizedCovariantInt, + type2: baseClassCovariantAny, + expected: true, + }, + { + type1: subclassFixedCovariant, + type2: baseClassCovariantAny, + expected: true, + }, + + // Compare to BaseClassContravariant + { + type1: subclassParameterizedContravariantAny, + type2: baseClassContravariantAny, + expected: true, + }, + { + type1: subclassParameterizedContravariantNumber, + type2: baseClassContravariantAny, + expected: false, + }, + { + type1: subclassParameterizedContravariantInt, + type2: baseClassContravariantAny, + expected: false, + }, + { + type1: subclassFixedContravariant, + type2: baseClassContravariantAny, + expected: false, + }, + + // Compare to BaseClassInvariant + { + type1: subclassParameterizedInvariantAny, + type2: baseClassInvariantNumber, + expected: false, + }, + { + type1: subclassParameterizedInvariantNumber, + type2: baseClassInvariantNumber, + expected: true, + }, + { + type1: subclassParameterizedInvariantInt, + type2: baseClassInvariantNumber, + expected: false, + }, + { + type1: subclassFixedInvariant, + type2: baseClassInvariantNumber, + expected: true, + }, + + // Compare to BaseClassCovariant + { + type1: subclassParameterizedCovariantAny, + type2: baseClassCovariantNumber, + expected: false, + }, + { + type1: subclassParameterizedCovariantNumber, + type2: baseClassCovariantNumber, + expected: true, + }, + { + type1: subclassParameterizedCovariantInt, + type2: baseClassCovariantNumber, + expected: true, + }, + { + type1: subclassFixedCovariant, + type2: baseClassCovariantNumber, + expected: true, + }, + + // Compare to BaseClassContravariant + { + type1: subclassParameterizedContravariantAny, + type2: baseClassContravariantNumber, + expected: true, + }, + { + type1: subclassParameterizedContravariantNumber, + type2: baseClassContravariantNumber, + expected: true, + }, + { + type1: subclassParameterizedContravariantInt, + type2: baseClassContravariantNumber, + expected: false, + }, + { + type1: subclassFixedContravariant, + type2: baseClassContravariantNumber, + expected: true, + }, + + // Compare to BaseClassInvariant + { + type1: subclassParameterizedInvariantAny, + type2: baseClassInvariantInt, + expected: false, + }, + { + type1: subclassParameterizedInvariantNumber, + type2: baseClassInvariantInt, + expected: false, + }, + { + type1: subclassParameterizedInvariantInt, + type2: baseClassInvariantInt, + expected: true, + }, + { + type1: subclassFixedInvariant, + type2: baseClassInvariantInt, + expected: false, + }, + + // Compare to BaseClassCovariant + { + type1: subclassParameterizedCovariantAny, + type2: baseClassCovariantInt, + expected: false, + }, + { + type1: subclassParameterizedCovariantNumber, + type2: baseClassCovariantInt, + expected: false, + }, + { + type1: subclassParameterizedCovariantInt, + type2: baseClassCovariantInt, + expected: true, + }, + { + type1: subclassFixedCovariant, + type2: baseClassCovariantInt, + expected: false, + }, + + // Compare to BaseClassContravariant + { + type1: subclassParameterizedContravariantAny, + type2: baseClassContravariantInt, + expected: true, + }, + { + type1: subclassParameterizedContravariantNumber, + type2: baseClassContravariantInt, + expected: true, + }, + { + type1: subclassParameterizedContravariantInt, + type2: baseClassContravariantInt, + expected: true, + }, + { + type1: subclassFixedContravariant, + type2: baseClassContravariantInt, + expected: true, + }, + ]; + + describe.each(testCases)('isAssignableTo', ({ type1, type2, expected }) => { + it(`should check whether ${type1} is assignable to ${type2}`, () => { + expect(typeChecker.isAssignableTo(type1, type2)).toBe(expected); + }); }); }); }); +const computeTypeOfDeclarationWithName = (declarations: T[]) => { + return (name: string): Type => { + const result = declarations.find((declaration) => declaration.name === name); + return typeComputer.computeType(result); + }; +}; + /** * A test case for {@link SafeDsTypeChecker.isAssignableTo}. */ diff --git a/packages/safe-ds-lang/tests/language/typing/type computer/lowestCommonSupertype.test.ts b/packages/safe-ds-lang/tests/language/typing/type computer/lowestCommonSupertype.test.ts index 6332a2f5a..5ce2f2cc4 100644 --- a/packages/safe-ds-lang/tests/language/typing/type computer/lowestCommonSupertype.test.ts +++ b/packages/safe-ds-lang/tests/language/typing/type computer/lowestCommonSupertype.test.ts @@ -204,6 +204,14 @@ const tests: LowestCommonSupertypeTest[] = [ types: [coreTypes.String, new LiteralType(new IntConstant(1n), NullConstant)], expected: coreTypes.AnyOrNull, }, + { + types: [coreTypes.Nothing, new LiteralType(new IntConstant(1n))], + expected: new LiteralType(new IntConstant(1n)), + }, + { + types: [coreTypes.NothingOrNull, new LiteralType(new IntConstant(1n))], + expected: new LiteralType(NullConstant, new IntConstant(1n)), + }, // Enum type & enum type { types: [enumType1, enumType1], diff --git a/packages/safe-ds-lang/tests/language/typing/type computer/streamSupertypes.test.ts b/packages/safe-ds-lang/tests/language/typing/type computer/streamSupertypes.test.ts new file mode 100644 index 000000000..ba7ee2e2d --- /dev/null +++ b/packages/safe-ds-lang/tests/language/typing/type computer/streamSupertypes.test.ts @@ -0,0 +1,119 @@ +import { NodeFileSystem } from 'langium/node'; +import { describe, expect, it } from 'vitest'; +import { createSafeDsServicesWithBuiltins } from '../../../../src/language/index.js'; +import { ClassType } from '../../../../src/language/typing/model.js'; +import { isSdsAttribute } from '../../../../src/language/generated/ast.js'; +import { getNodeOfType } from '../../../helpers/nodeFinder.js'; +import { AssertionError } from 'assert'; + +const services = (await createSafeDsServicesWithBuiltins(NodeFileSystem)).SafeDs; +const typeComputer = services.types.TypeComputer; + +describe('streamSupertypes', async () => { + const supertypesAsStrings = (type: ClassType | undefined) => + typeComputer + .streamSupertypes(type) + .map((clazz) => clazz.toString()) + .toArray(); + + it('should return an empty stream if passed undefined', () => { + expect(supertypesAsStrings(undefined)).toStrictEqual([]); + }); + + const testCases = [ + { + testName: 'should return "Any" if the class has no parent types', + code: ` + class Test { + attr a: A + } + + class A + `, + expected: ['Any'], + }, + { + testName: 'should return "Any" if the first parent type is not a class', + code: ` + class Test { + attr a: A + } + + class A sub E + enum E + `, + expected: ['Any'], + }, + { + testName: 'should return the superclasses of a class (no cycle, parameterized parent type, implicit any)', + code: ` + class Test { + attr a: A + } + + class A sub B + class B + `, + expected: ['B', 'Any'], + }, + { + testName: 'should return the superclasses of a class (no cycle, fixed parent type, implicit any)', + code: ` + class Test { + attr a: A + } + + class A sub B + class B + `, + expected: ['B', 'Any'], + }, + { + testName: 'should return the superclasses of a class (no cycle, explicit any)', + code: ` + class Test { + attr a: A + } + + class A sub Any + `, + expected: ['Any'], + }, + { + testName: 'should return the superclasses of a class (cycle)', + code: ` + class Test { + attr a: A + } + + class A sub B + class B sub C + class C sub A + `, + expected: ['B', 'C', 'Any'], + }, + { + testName: 'should only consider the first parent type', + code: ` + class Test { + attr a: A + } + + class A sub B, C + class B + class C + `, + expected: ['B', 'Any'], + }, + ]; + + it.each(testCases)('$testName', async ({ code, expected }) => { + const firstAttribute = await getNodeOfType(services, code, isSdsAttribute); + const type = typeComputer.computeType(firstAttribute); + if (!(type instanceof ClassType)) { + throw new AssertionError({ message: 'Expected type to be an instance of ClassType.', actual: type }); + } + + expect(supertypesAsStrings(type)).toStrictEqual(expected); + }); +}); diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/maps/input.sdstest b/packages/safe-ds-lang/tests/resources/generation/expressions/maps/input.sdstest index dc6734c8e..29c194c70 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/maps/input.sdstest +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/maps/input.sdstest @@ -1,8 +1,10 @@ package tests.generator.maps -@Impure([ImpurityReason.Other]) fun g1(param: Map) +@Impure([ImpurityReason.Other]) fun g1(param: Map) -@Impure([ImpurityReason.Other]) fun g2(param: Map) +@Impure([ImpurityReason.Other]) fun g2(param: Map) + +@Impure([ImpurityReason.Other]) fun g3(param: Map) @Pure fun h1() -> result: Float @@ -10,8 +12,8 @@ package tests.generator.maps pipeline test { g1({}); - g1({"a": 1.2, "b": 1.0}); - g1({h2(): -0.5, "b": h1()}); - g2({1.2: "a", 1.0: "b"}); - g2({5.6: "c", h1(): h2()}); + g2({"a": 1.2, "b": 1.0}); + g2({h2(): -0.5, "b": h1()}); + g3({1.2: "a", 1.0: "b"}); + g3({5.6: "c", h1(): h2()}); } diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input.py b/packages/safe-ds-lang/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input.py index ab07e1b28..2886b68d0 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input.py +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input.py @@ -6,7 +6,7 @@ def test(): g1({}) - g1({'a': 1.2, 'b': 1.0}) - g1({safeds_runner.server.pipeline_manager.runner_memoized_function_call("tests.generator.maps.h2", h2, [], []): -0.5, 'b': safeds_runner.server.pipeline_manager.runner_memoized_function_call("tests.generator.maps.h1", h1, [], [])}) - g2({1.2: 'a', 1.0: 'b'}) - g2({5.6: 'c', safeds_runner.server.pipeline_manager.runner_memoized_function_call("tests.generator.maps.h1", h1, [], []): safeds_runner.server.pipeline_manager.runner_memoized_function_call("tests.generator.maps.h2", h2, [], [])}) + g2({'a': 1.2, 'b': 1.0}) + g2({safeds_runner.server.pipeline_manager.runner_memoized_function_call("tests.generator.maps.h2", h2, [], []): -0.5, 'b': safeds_runner.server.pipeline_manager.runner_memoized_function_call("tests.generator.maps.h1", h1, [], [])}) + g3({1.2: 'a', 1.0: 'b'}) + g3({5.6: 'c', safeds_runner.server.pipeline_manager.runner_memoized_function_call("tests.generator.maps.h1", h1, [], []): safeds_runner.server.pipeline_manager.runner_memoized_function_call("tests.generator.maps.h2", h2, [], [])}) diff --git a/packages/safe-ds-lang/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input.py.map b/packages/safe-ds-lang/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input.py.map index 4a5130086..fd44d6df0 100644 --- a/packages/safe-ds-lang/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input.py.map +++ b/packages/safe-ds-lang/tests/resources/generation/expressions/maps/output/tests/generator/maps/gen_input.py.map @@ -1 +1 @@ -{"version":3,"sources":["input.sdstest"],"names":["test","g1","h2","h1","g2"],"mappings":"AAAA;;;;;;AAUA,IAASA,IAAI;IACTC,EAAE,CAAC,EAAE;IACLA,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;IACtBA,EAAE,CAAC,CAAC,+FAAAC,EAAE,WAAI,IAAI,EAAE,GAAG,EAAE,+FAAAC,EAAE;IACvBC,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;IACtBA,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,+FAAAD,EAAE,WAAI,+FAAAD,EAAE","file":"gen_input.py"} \ No newline at end of file +{"version":3,"sources":["input.sdstest"],"names":["test","g1","g2","h2","h1","g3"],"mappings":"AAAA;;;;;;AAYA,IAASA,IAAI;IACTC,EAAE,CAAC,EAAE;IACLC,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;IACtBA,EAAE,CAAC,CAAC,+FAAAC,EAAE,WAAI,IAAI,EAAE,GAAG,EAAE,+FAAAC,EAAE;IACvBC,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;IACtBA,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,+FAAAD,EAAE,WAAI,+FAAAD,EAAE","file":"gen_input.py"} \ No newline at end of file diff --git a/packages/safe-ds-lang/tests/resources/scoping/named types/in same file/to type parameters/in global classes/main.sdstest b/packages/safe-ds-lang/tests/resources/scoping/named types/in same file/to type parameters/in global classes/main.sdstest index 593c53400..2f9b99849 100644 --- a/packages/safe-ds-lang/tests/resources/scoping/named types/in same file/to type parameters/in global classes/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/scoping/named types/in same file/to type parameters/in global classes/main.sdstest @@ -15,7 +15,10 @@ class MyClass<»Own«>( // $TEST$ unresolved d: »Unresolved«, -) where { +) +// $TEST$ references own +sub »Own« +where { // $TEST$ references own Own sub »Own«, diff --git a/packages/safe-ds-lang/tests/resources/scoping/named types/in same file/to type parameters/in nested classes/main.sdstest b/packages/safe-ds-lang/tests/resources/scoping/named types/in same file/to type parameters/in nested classes/main.sdstest index 7b1f2446a..80b0e5dda 100644 --- a/packages/safe-ds-lang/tests/resources/scoping/named types/in same file/to type parameters/in nested classes/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/scoping/named types/in same file/to type parameters/in nested classes/main.sdstest @@ -32,7 +32,12 @@ class MyClass1<»Container«, Overridden> { // $TEST$ unresolved h: »Unresolved«, - ) where { + ) + // $TEST$ references own + // $TEST$ references overridden + // $TEST$ references container + sub »Own«, »Overridden«, »Container« + where { // $TEST$ references own Own sub »Own«, diff --git a/packages/safe-ds-lang/tests/resources/typing/assignees/block lambda results/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/assignees/block lambda results/main.sdstest index 871afb67f..a6f46d811 100644 --- a/packages/safe-ds-lang/tests/resources/typing/assignees/block lambda results/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/assignees/block lambda results/main.sdstest @@ -11,7 +11,7 @@ segment mySegment() -> (r: Int) { () { // $TEST$ serialization literal<1> - // $TEST$ serialization ? + // $TEST$ serialization $unknown yield »r«, yield »s« = 1; }; diff --git a/packages/safe-ds-lang/tests/resources/typing/assignees/placeholders/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/assignees/placeholders/main.sdstest index 3ba738ab6..144cda8d8 100644 --- a/packages/safe-ds-lang/tests/resources/typing/assignees/placeholders/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/assignees/placeholders/main.sdstest @@ -10,7 +10,7 @@ segment mySegment1() -> (r: Int) { segment mySegment2() -> (r: Int, s: String) { // $TEST$ serialization literal<1> - // $TEST$ serialization ? + // $TEST$ serialization $unknown val »r«, val »s« = 1; } diff --git a/packages/safe-ds-lang/tests/resources/typing/assignees/yields/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/assignees/yields/main.sdstest index dbe2de364..b26079cd9 100644 --- a/packages/safe-ds-lang/tests/resources/typing/assignees/yields/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/assignees/yields/main.sdstest @@ -10,7 +10,7 @@ segment mySegment1() -> (r: Int) { segment mySegment2() -> (r: Int, s: String) { // $TEST$ serialization literal<1> - // $TEST$ serialization ? + // $TEST$ serialization $unknown »yield r«, »yield s« = 1; } diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/annotations/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/annotations/main.sdstest index e9529989a..d3e2a94a7 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/annotations/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/annotations/main.sdstest @@ -6,5 +6,5 @@ annotation »myAnnotation1« // $TEST$ serialization (p1: Int, p2: String) -> () annotation »myAnnotation3«(p1: Int, p2: String) -// $TEST$ serialization (p1: ?) -> () +// $TEST$ serialization (p1: $unknown) -> () annotation »myAnnotation5«(p1) diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/attributes/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/attributes/main.sdstest index d17f4f855..7b10c3bc6 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/attributes/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/attributes/main.sdstest @@ -1,14 +1,14 @@ package tests.typing.declarations.attributes class C { - // $TEST$ serialization ? + // $TEST$ serialization $unknown attr »a« // $TEST$ equivalence_class instanceAttribute // $TEST$ equivalence_class instanceAttribute attr »b«: »Int« - // $TEST$ serialization ? + // $TEST$ serialization $unknown static attr »c« // $TEST$ equivalence_class staticAttribute diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/functions/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/functions/main.sdstest index f3125cbd9..d8e86e848 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/functions/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/functions/main.sdstest @@ -12,5 +12,5 @@ fun »myFunction3«(p1: Int, p2: String) // $TEST$ serialization (p1: Int, p2: String) -> (r1: Int, r2: String) fun »myFunction4«(p1: Int, p2: String) -> (r1: Int, r2: String) -// $TEST$ serialization (p1: ?) -> (r1: ?) +// $TEST$ serialization (p1: $unknown) -> (r1: $unknown) fun »myFunction5«(p1) -> (r1) diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of annotations/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of annotations/main.sdstest index 55e75d847..765e82e7f 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of annotations/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of annotations/main.sdstest @@ -4,5 +4,5 @@ package tests.typing.declarations.parameters.ofAnnotations // $TEST$ equivalence_class parameterType annotation MyAnnotation1(»p«: »Int«) -// $TEST$ serialization ? +// $TEST$ serialization $unknown annotation MyAnnotation2(»p«) diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are isolated/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are isolated/main.sdstest index 2e9dcf7c5..bc78c346a 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are isolated/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are isolated/main.sdstest @@ -1,6 +1,6 @@ package tests.typing.declarations.parameters.ofBlockLambdas.thatAreIsolated segment mySegment() { - // $TEST$ serialization ? + // $TEST$ serialization $unknown (»p«) {}; } diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are passed as arguments/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are passed as arguments/main.sdstest index f76cd54bc..6acee7d54 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are passed as arguments/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are passed as arguments/main.sdstest @@ -12,15 +12,15 @@ segment mySegment() { // $TEST$ equivalence_class parameterType1 higherOrderFunction1(param = (»p«) {}); - // $TEST$ serialization ? + // $TEST$ serialization $unknown higherOrderFunction2((»p«) {}); - // $TEST$ serialization ? + // $TEST$ serialization $unknown higherOrderFunction2(param = (»p«) {}); - // $TEST$ serialization ? + // $TEST$ serialization $unknown normalFunction((»p«) {}); - // $TEST$ serialization ? + // $TEST$ serialization $unknown normalFunction(param = (»p«) {}); } diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are passed as default values/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are passed as default values/main.sdstest index 036b725a6..4cbdf821a 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are passed as default values/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are passed as default values/main.sdstest @@ -7,11 +7,11 @@ fun higherOrderFunction1( ) fun higherOrderFunction2( - // $TEST$ serialization ? + // $TEST$ serialization $unknown param: () -> () = (»p«) {} ) fun normalFunction( - // $TEST$ serialization ? + // $TEST$ serialization $unknown param: Int = (»p«) {} ) diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are yielded/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are yielded/main.sdstest index c37f96dd1..bcb8aca84 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are yielded/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of block lambdas/that are yielded/main.sdstest @@ -9,9 +9,9 @@ segment mySegment() -> ( // $TEST$ equivalence_class parameterType2 yield r = (»p«) {}; - // $TEST$ serialization ? + // $TEST$ serialization $unknown yield s = (»p«) {}; - // $TEST$ serialization ? + // $TEST$ serialization $unknown yield t = (»p«) {}; } diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of callable types/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of callable types/main.sdstest index 53ae744a3..17a6a7033 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of callable types/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of callable types/main.sdstest @@ -4,5 +4,5 @@ package tests.typing.declarations.parameters.ofCallableTypes // $TEST$ equivalence_class parameterType annotation MyAnnotation1(f: (»p«: »Int«) -> ()) -// $TEST$ serialization ? +// $TEST$ serialization $unknown annotation MyAnnotation2(f: (»p«) -> ()) diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of classes/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of classes/main.sdstest index 27bedb4f2..9185e184c 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of classes/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of classes/main.sdstest @@ -4,5 +4,5 @@ package tests.typing.declarations.parameters.ofClasses // $TEST$ equivalence_class parameterType class MyClass1(»p«: »Int«) -// $TEST$ serialization ? +// $TEST$ serialization $unknown class MyClass2(»p«) diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of enum variants/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of enum variants/main.sdstest index aaaef607c..8267ac61d 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of enum variants/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of enum variants/main.sdstest @@ -5,6 +5,6 @@ enum MyEnum { // $TEST$ equivalence_class parameterType MyEnumVariant1(»p«: »Int«) - // $TEST$ serialization ? + // $TEST$ serialization $unknown MyEnumVariant2(»p«) } diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are isolated/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are isolated/main.sdstest index 8fd19ecc7..51f0a097f 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are isolated/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are isolated/main.sdstest @@ -1,6 +1,6 @@ package tests.typing.declarations.parameters.ofExpressionLambdas.thatAreIsolated segment mySegment() { - // $TEST$ serialization ? + // $TEST$ serialization $unknown (»p«) -> 1; } diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are passed as arguments/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are passed as arguments/main.sdstest index b2ea8d897..5244fdaf5 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are passed as arguments/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are passed as arguments/main.sdstest @@ -12,15 +12,15 @@ segment mySegment() { // $TEST$ equivalence_class parameterType1 higherOrderFunction1(param = (»p«) -> ""); - // $TEST$ serialization ? + // $TEST$ serialization $unknown higherOrderFunction2((»p«) -> ""); - // $TEST$ serialization ? + // $TEST$ serialization $unknown higherOrderFunction2(param = (»p«) -> ""); - // $TEST$ serialization ? + // $TEST$ serialization $unknown normalFunction((»p«) -> ""); - // $TEST$ serialization ? + // $TEST$ serialization $unknown normalFunction(param = (»p«) -> ""); } diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are passed as default values/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are passed as default values/main.sdstest index b6286a79f..0476e790f 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are passed as default values/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are passed as default values/main.sdstest @@ -7,11 +7,11 @@ fun higherOrderFunction1( ) fun higherOrderFunction2( - // $TEST$ serialization ? + // $TEST$ serialization $unknown param: () -> r: String = (»p«) -> "" ) fun normalFunction( - // $TEST$ serialization ? + // $TEST$ serialization $unknown param: Int = (»p«) -> "" ) diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are yielded/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are yielded/main.sdstest index 97fb32438..c5da2c334 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are yielded/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of expression lambdas/that are yielded/main.sdstest @@ -9,9 +9,9 @@ segment mySegment() -> ( // $TEST$ equivalence_class parameterType2 yield r = (»p«) -> true; - // $TEST$ serialization ? + // $TEST$ serialization $unknown yield s = (»p«) -> true; - // $TEST$ serialization ? + // $TEST$ serialization $unknown yield t = (»p«) -> true; } diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of functions/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of functions/main.sdstest index 142e3ff75..854f7a2aa 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of functions/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of functions/main.sdstest @@ -4,5 +4,5 @@ package tests.typing.declarations.parameters.ofFunctions // $TEST$ equivalence_class parameterType fun myFunction1(»p«: »Int«) -// $TEST$ serialization ? +// $TEST$ serialization $unknown fun myFunction2(»p«) diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of segments/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of segments/main.sdstest index 1a1cdf974..5937cb36a 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of segments/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/parameters/of segments/main.sdstest @@ -4,5 +4,5 @@ package tests.typing.declarations.parameters.ofSegments // $TEST$ equivalence_class parameterType segment mySegment1(»p«: »Int«) {} -// $TEST$ serialization ? +// $TEST$ serialization $unknown segment mySegment2(»p«) {} diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/pipelines/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/pipelines/main.sdstest index 4d60c2fc2..51dd507d9 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/pipelines/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/pipelines/main.sdstest @@ -1,4 +1,4 @@ package tests.typing.declarations.pipelines -// $TEST$ serialization ? +// $TEST$ serialization $unknown pipeline »myPipeline« {} diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/segments/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/segments/main.sdstest index 91b152277..953e8a87b 100644 --- a/packages/safe-ds-lang/tests/resources/typing/declarations/segments/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/segments/main.sdstest @@ -12,5 +12,5 @@ segment »mySegment3«(p1: Int, p2: String) {} // $TEST$ serialization (p1: Int, p2: String) -> (r1: Int, r2: String) segment »mySegment4«(p1: Int, p2: String) -> (r1: Int, r2: String) {} -// $TEST$ serialization (p1: ?) -> (r1: ?) +// $TEST$ serialization (p1: $unknown) -> (r1: $unknown) segment »mySegment5«(p1) -> (r1) {} diff --git a/packages/safe-ds-lang/tests/resources/typing/declarations/type parameters/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/declarations/type parameters/main.sdstest new file mode 100644 index 000000000..52586950e --- /dev/null +++ b/packages/safe-ds-lang/tests/resources/typing/declarations/type parameters/main.sdstest @@ -0,0 +1,5 @@ +package tests.typing.declarations.typeParameters + +// $TEST$ serialization K +// $TEST$ serialization V +class MyClass<»K«, »V«> diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are isolated/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are isolated/main.sdstest index 7aa17b948..157a81272 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are isolated/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are isolated/main.sdstest @@ -3,12 +3,12 @@ package tests.typing.expressions.blockLambdas.thatAreIsolated fun g() -> r: Int segment mySegment() { - // $TEST$ serialization (p: ?) -> (r: literal<1>, s: ?) + // $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: $unknown) »(p) { yield r, yield s = 1; }«; - // $TEST$ serialization (p: ?) -> (r: Int, s: ?) + // $TEST$ serialization (p: $unknown) -> (r: Int, s: $unknown) val f = »(p) { yield r, yield s = g(); }«; diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are passed as arguments/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are passed as arguments/main.sdstest index 630df41f3..4303239a9 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are passed as arguments/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are passed as arguments/main.sdstest @@ -12,34 +12,34 @@ segment mySegment() { yield s = ""; }«); - // $TEST$ serialization (p: String) -> (r: literal<1>, s: ?) + // $TEST$ serialization (p: String) -> (r: literal<1>, s: $unknown) higherOrderFunction1(param = »(p) { yield r, yield s = 1; }«); - // $TEST$ serialization (p: ?) -> (r: literal<1>, s: literal<"">) + // $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: literal<"">) higherOrderFunction2(»(p) { yield r = 1; yield s = ""; }«); - // $TEST$ serialization (p: ?) -> (r: literal<1>, s: ?) + // $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: $unknown) higherOrderFunction2(param = »(p) { yield r, yield s = 1; }«); - // $TEST$ serialization (p: ?) -> (r: literal<1>, s: literal<"">) + // $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: literal<"">) normalFunction(»(p) { yield r = 1; yield s = ""; }«); - // $TEST$ serialization (p: ?) -> (r: literal<1>, s: ?) + // $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: $unknown) normalFunction(param = »(p) { yield r, yield s = 1; }«); - // $TEST$ serialization (p: ?) -> (r: literal<1>, s: ?) + // $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: $unknown) parameterlessFunction(»(p) { yield r, yield s = 1; }«); diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are passed as default values/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are passed as default values/main.sdstest index d483d5fc1..d77d639a3 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are passed as default values/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are passed as default values/main.sdstest @@ -6,31 +6,31 @@ fun higherOrderFunction1( yield r = 1; yield s = ""; }«, - // $TEST$ serialization (p: String) -> (r: literal<1>, s: ?) + // $TEST$ serialization (p: String) -> (r: literal<1>, s: $unknown) param2: (p: String) -> (r: Int, s: String) = »(p) { yield r, yield s = 1; }«, ) fun higherOrderFunction2( - // $TEST$ serialization (p: ?) -> (r: literal<1>, s: literal<"">) + // $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: literal<"">) param1: () -> () = »(p) { yield r = 1; yield s = ""; }«, - // $TEST$ serialization (p: ?) -> (r: literal<1>, s: ?) + // $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: $unknown) param2: () -> () = »(p) { yield r, yield s = 1; }«, ) fun normalFunction( - // $TEST$ serialization (p: ?) -> (r: literal<1>, s: literal<"">) + // $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: literal<"">) param1: Int = »(p) { yield r = 1; yield s = ""; }«, - // $TEST$ serialization (p: ?) -> (r: literal<1>, s: ?) + // $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: $unknown) param2: Int = »(p) { yield r, yield s = 1; }«, diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are yielded/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are yielded/main.sdstest index 5d551ff3b..7eaf2d530 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are yielded/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/that are yielded/main.sdstest @@ -11,13 +11,13 @@ segment mySegment() -> ( yield s = ""; }«; - // $TEST$ serialization (p: ?) -> (r: literal<1>, s: literal<"">) + // $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: literal<"">) yield s = »(p) { yield r = 1; yield s = ""; }«; - // $TEST$ serialization (p: ?) -> (r: literal<1>, s: ?) + // $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: $unknown) yield t = »(p) { yield r, yield s = 1; }«; diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/with manifest types/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/with manifest types/main.sdstest index d9383de26..aa34f81ae 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/with manifest types/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/block lambdas/with manifest types/main.sdstest @@ -7,7 +7,7 @@ segment mySegment() { yield s = ""; }«; - // $TEST$ serialization (p: String) -> (r: literal<1>, s: ?) + // $TEST$ serialization (p: String) -> (r: literal<1>, s: $unknown) »(p: String) { yield r, yield s = 1; }«; diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of annotations/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of annotations/main.sdstest index a585e6f58..b0782907a 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of annotations/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of annotations/main.sdstest @@ -3,6 +3,6 @@ package tests.typing.expressions.calls.ofAnnotations annotation MyAnnotation pipeline myPipeline { - // $TEST$ serialization ? + // $TEST$ serialization $unknown »MyAnnotation()«; } diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of classes/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of classes/main.sdstest index b96205278..c32f951cf 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of classes/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of classes/main.sdstest @@ -6,6 +6,6 @@ pipeline myPipeline { // $TEST$ serialization C »C()«; - // $TEST$ serialization ? + // $TEST$ serialization $unknown »C()()«; } diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of enum variants/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of enum variants/main.sdstest index 7ed609fcd..de052fdc9 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of enum variants/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of enum variants/main.sdstest @@ -10,7 +10,7 @@ pipeline myPipeline { // $TEST$ serialization MyEnumVariantWithoutParameterList »MyEnum.MyEnumVariantWithoutParameterList()«; - // $TEST$ serialization ? + // $TEST$ serialization $unknown val alias1 = MyEnum.MyEnumVariantWithoutParameterList; »alias1()«; @@ -18,14 +18,14 @@ pipeline myPipeline { // $TEST$ serialization MyEnumVariantWithoutParameters »MyEnum.MyEnumVariantWithoutParameters()«; - // $TEST$ serialization ? + // $TEST$ serialization $unknown val alias2 = MyEnum.MyEnumVariantWithoutParameters; »alias2()«; - // $TEST$ serialization ? + // $TEST$ serialization $unknown »MyEnum.MyEnumVariantWithoutParameters()()«; - // $TEST$ serialization ? + // $TEST$ serialization $unknown val alias3 = MyEnum.MyEnumVariantWithoutParameters(); »alias3()«; @@ -38,10 +38,10 @@ pipeline myPipeline { »alias4(1)«; - // $TEST$ serialization ? + // $TEST$ serialization $unknown »MyEnum.MyEnumVariantWithParameters(1)()«; - // $TEST$ serialization ? + // $TEST$ serialization $unknown val alias5 = MyEnum.MyEnumVariantWithParameters(1); »alias5()«; } diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of non-callable/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of non-callable/main.sdstest index ffa49ff51..4af0af416 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of non-callable/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/calls/of non-callable/main.sdstest @@ -3,6 +3,6 @@ package tests.typing.expressions.calls.ofNonCallables enum MyEnum pipeline myPipeline { - // $TEST$ serialization ? + // $TEST$ serialization $unknown »MyEnum()«; } diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/calls/unresolved/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/calls/unresolved/main.sdstest index e24d59c47..c95036525 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/calls/unresolved/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/calls/unresolved/main.sdstest @@ -1,6 +1,6 @@ package tests.typing.expressions.calls.ofUnresolved pipeline myPipeline { - // $TEST$ serialization ? + // $TEST$ serialization $unknown »unresolved()«; } diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are isolated/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are isolated/main.sdstest index 05e4f82ca..1cca1cc2f 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are isolated/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are isolated/main.sdstest @@ -3,9 +3,9 @@ package tests.typing.expressions.expressionLambdas.thatAreIsolated fun g() -> r: Int segment mySegment() { - // $TEST$ serialization (p: ?) -> (result: Int) + // $TEST$ serialization (p: $unknown) -> (result: Int) »(p) -> g()«; - // $TEST$ serialization (p: ?) -> (result: literal<1>) + // $TEST$ serialization (p: $unknown) -> (result: literal<1>) val f = »(p) -> 1«; } diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are passed as arguments/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are passed as arguments/main.sdstest index 1be90b44e..4277aabfa 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are passed as arguments/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are passed as arguments/main.sdstest @@ -12,18 +12,18 @@ segment mySegment() { // $TEST$ serialization (p: String) -> (result: literal<1>) higherOrderFunction1(param = »(p) -> 1«); - // $TEST$ serialization (p: ?) -> (result: literal<1>) + // $TEST$ serialization (p: $unknown) -> (result: literal<1>) higherOrderFunction2(»(p) -> 1«); - // $TEST$ serialization (p: ?) -> (result: literal<1>) + // $TEST$ serialization (p: $unknown) -> (result: literal<1>) higherOrderFunction2(param = »(p) -> 1«); - // $TEST$ serialization (p: ?) -> (result: literal<1>) + // $TEST$ serialization (p: $unknown) -> (result: literal<1>) normalFunction(»(p) -> 1«); - // $TEST$ serialization (p: ?) -> (result: literal<1>) + // $TEST$ serialization (p: $unknown) -> (result: literal<1>) normalFunction(param = »(p) -> 1«); - // $TEST$ serialization (p: ?) -> (result: literal<1>) + // $TEST$ serialization (p: $unknown) -> (result: literal<1>) parameterlessFunction(»(p) -> 1«); } diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are passed as default values/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are passed as default values/main.sdstest index 672fa86ed..201a0594b 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are passed as default values/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are passed as default values/main.sdstest @@ -6,11 +6,11 @@ fun higherOrderFunction1( ) fun higherOrderFunction2( - // $TEST$ serialization (p: ?) -> (result: literal<1>) + // $TEST$ serialization (p: $unknown) -> (result: literal<1>) param: () -> () = »(p) -> 1« ) fun normalFunction( - // $TEST$ serialization (p: ?) -> (result: literal<1>) + // $TEST$ serialization (p: $unknown) -> (result: literal<1>) param: Int = »(p) -> 1« ) diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are yielded/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are yielded/main.sdstest index 12d28d349..c8f26c2ab 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are yielded/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/expression lambdas/that are yielded/main.sdstest @@ -8,9 +8,9 @@ segment mySegment() -> ( // $TEST$ serialization (p: String) -> (result: literal<1>) yield r = »(p) -> 1«; - // $TEST$ serialization (p: ?) -> (result: literal<1>) + // $TEST$ serialization (p: $unknown) -> (result: literal<1>) yield s = »(p) -> 1«; - // $TEST$ serialization (p: ?) -> (result: literal<1>) + // $TEST$ serialization (p: $unknown) -> (result: literal<1>) yield t = »(p) -> 1«; } diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/indexed accesses/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/indexed accesses/main.sdstest index 1e4622183..b718d6f12 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/indexed accesses/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/indexed accesses/main.sdstest @@ -1,23 +1,27 @@ package tests.typing.expressions.indexedAccesses -// TODO: Improve once type parameters are supported segment mySegment1(param: List) { - // $TEST$ serialization Any? + // $TEST$ serialization Int »param[0]«; + + // $TEST$ serialization Int + »param[unresolved]«; } -// TODO: Improve once type parameters are supported segment mySegment2(param: Map) { - // $TEST$ serialization Any? + // $TEST$ serialization Int »param[""]«; + + // $TEST$ serialization Int + »param[unresolved]«; } segment mySegment3(param: String) { - // $TEST$ serialization ? + // $TEST$ serialization $unknown »param[0]«; } segment mySegment4() { - // $TEST$ serialization ? + // $TEST$ serialization $unknown »unresolved[0]«; } diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/lists/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/lists/main.sdstest index f1ffb64b0..270f8de32 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/lists/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/lists/main.sdstest @@ -1,8 +1,22 @@ package tests.typing.expressions.lists -// TODO: Improve once type parameters are supported +@Pure fun float() -> r: Float +@Pure fun string() -> r: String + pipeline myPipeline { - // $TEST$ serialization List + // $TEST$ serialization List »[]«; + + // $TEST$ serialization List> + »[1, 2, 3]«; + + // $TEST$ serialization List + »[1, float(), 3]«; + + // $TEST$ serialization List + »[1, string(), 3]«; + + // $TEST$ serialization List + »[1, string(), null]«; } diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/maps/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/maps/main.sdstest index c9a6a7476..2d055e1f6 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/maps/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/maps/main.sdstest @@ -1,8 +1,25 @@ package tests.typing.expressions.maps -// TODO: Improve once type parameters are supported +@Pure fun float() -> r: Float +@Pure fun string() -> r: String + pipeline myPipeline { - // $TEST$ serialization Map + // $TEST$ serialization Map »{}«; + + // $TEST$ serialization Map> + »{1: 1, 2: 2, 3: 3}«; + + // $TEST$ serialization Map + »{1: 1, float(): float(), 3: 3}«; + + // $TEST$ serialization Map + »{1: 1, string(): string(), 3: 3}«; + + // $TEST$ serialization Map + »{1: 1, string(): string(), null: null}«; + + // $TEST$ serialization Map + »{float(): string(), 1: null}«; } diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/member accesses/unresolved/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/member accesses/unresolved/main.sdstest index 6634f7127..9cef6afda 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/member accesses/unresolved/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/member accesses/unresolved/main.sdstest @@ -3,9 +3,9 @@ package tests.typing.expressions.memberAccesses.unresolved class C pipeline myPipeline { - // $TEST$ serialization ? + // $TEST$ serialization $unknown »C.unresolved«; - // $TEST$ serialization ? + // $TEST$ serialization $unknown »C?.unresolved«; } diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/operations/arithmetic/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/operations/arithmetic/main.sdstest index 6f29d46ad..814bd7029 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/operations/arithmetic/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/operations/arithmetic/main.sdstest @@ -102,3 +102,41 @@ pipeline nonConstantOperands { // $TEST$ serialization Float val negationFloat = »-anyFloat()«; } + +pipeline mixedOperands { + // $TEST$ serialization Int + val additionIntInt = »10 + anyInt()«; + // $TEST$ serialization Int + val subtractionIntInt = »10 - anyInt()«; + // $TEST$ serialization Int + val multiplicationIntInt = »10 * anyInt()«; + // $TEST$ serialization Int + val divisionIntInt = »10 / anyInt()«; + + // $TEST$ serialization Float + val additionIntFloat = »10 + anyFloat()«; + // $TEST$ serialization Float + val subtractionIntFloat = »10 - anyFloat()«; + // $TEST$ serialization Float + val multiplicationIntFloat = »10 * anyFloat()«; + // $TEST$ serialization Float + val divisionIntFloat = »10 / anyFloat()«; + + // $TEST$ serialization Float + val additionFloatInt = »1.5 + anyInt()«; + // $TEST$ serialization Float + val subtractionFloatInt = »1.5 - anyInt()«; + // $TEST$ serialization Float + val multiplicationFloatInt = »1.5 * anyInt()«; + // $TEST$ serialization Float + val divisionFloatInt = »1.5 / anyInt()«; + + // $TEST$ serialization Float + val additionFloatFloat = »1.5 + anyFloat()«; + // $TEST$ serialization Float + val subtractionFloatFloat = »1.5 - anyFloat()«; + // $TEST$ serialization Float + val multiplicationFloatFloat = »1.5 * anyFloat()«; + // $TEST$ serialization Float + val divisionFloatFloat = »1.5 / anyFloat()«; +} diff --git a/packages/safe-ds-lang/tests/resources/typing/expressions/references/unresolved/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/expressions/references/unresolved/main.sdstest index 6e2046eb8..a14b2c6e4 100644 --- a/packages/safe-ds-lang/tests/resources/typing/expressions/references/unresolved/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/expressions/references/unresolved/main.sdstest @@ -1,6 +1,6 @@ package tests.typing.expressions.references.unresolved pipeline myPipeline { - // $TEST$ serialization ? + // $TEST$ serialization $unknown »unresolved«; } diff --git a/packages/safe-ds-lang/tests/resources/typing/types/callable types/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/types/callable types/main.sdstest index 699f98941..da9f77e1b 100644 --- a/packages/safe-ds-lang/tests/resources/typing/types/callable types/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/types/callable types/main.sdstest @@ -12,5 +12,5 @@ fun myFunction3(f: »(p1: Int, p2: String) -> ()«) // $TEST$ serialization (p1: Int, p2: String) -> (r1: Int, r2: String) fun myFunction4(f: »(p1: Int, p2: String) -> (r1: Int, r2: String)«) -// $TEST$ serialization (p1: ?) -> (r1: ?) +// $TEST$ serialization (p1: $unknown) -> (r1: $unknown) fun myFunction5(f: »(p1) -> (r1)«) diff --git a/packages/safe-ds-lang/tests/resources/typing/types/member types/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/types/member types/main.sdstest index 426c79e62..c265ef113 100644 --- a/packages/safe-ds-lang/tests/resources/typing/types/member types/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/types/member types/main.sdstest @@ -20,7 +20,7 @@ fun nonNullableMemberTypes( b: »MyClass.MyNestedEnum«, // $TEST$ equivalence_class myEnumVariant d: »MyEnum.MyEnumVariant«, - // $TEST$ serialization ? + // $TEST$ serialization $unknown e: »MyEnum.unresolved«, ) @@ -31,6 +31,6 @@ fun nullableMemberTypes( b: »MyClass.MyNestedEnum?«, // $TEST$ serialization MyEnumVariant? d: »MyEnum.MyEnumVariant?«, - // $TEST$ serialization ? + // $TEST$ serialization $unknown e: »MyEnum.unresolved?«, ) diff --git a/packages/safe-ds-lang/tests/resources/typing/types/named types/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/types/named types/main.sdstest index 127afa8d4..a8863f836 100644 --- a/packages/safe-ds-lang/tests/resources/typing/types/named types/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/types/named types/main.sdstest @@ -9,7 +9,7 @@ fun nonNullableNamedTypes( a: »MyClass«, // $TEST$ serialization MyEnum b: »MyEnum«, - // $TEST$ serialization ? + // $TEST$ serialization $unknown c: »unresolved«, ) @@ -18,6 +18,6 @@ fun nullableNamedTypes( a: »MyClass?«, // $TEST$ serialization MyEnum? b: »MyEnum?«, - // $TEST$ serialization ? + // $TEST$ serialization $unknown c: »unresolved?«, ) diff --git a/packages/safe-ds-lang/tests/resources/typing/types/named types/with type parameters.sdstest b/packages/safe-ds-lang/tests/resources/typing/types/named types/with type parameters.sdstest new file mode 100644 index 000000000..05cd2dd79 --- /dev/null +++ b/packages/safe-ds-lang/tests/resources/typing/types/named types/with type parameters.sdstest @@ -0,0 +1,36 @@ +package tests.typing.types.namedTypes + +class MyClass2 +class MyClass3 + +fun nonNullableNamedTypes( + // $TEST$ serialization MyClass2 + a: »MyClass2«, + // $TEST$ serialization MyClass2<$unknown> + b: »MyClass2«, + // $TEST$ serialization MyClass2<$unknown> + c: »MyClass2«, + // $TEST$ serialization MyClass2 + d: »MyClass2«, + + // $TEST$ serialization MyClass3 + y: »MyClass3«, + // $TEST$ serialization $unknown + z: »unresolved«, +) + +fun nullableNamedTypes( + // $TEST$ serialization MyClass2? + a: »MyClass2?«, + // $TEST$ serialization MyClass2<$unknown>? + b: »MyClass2?«, + // $TEST$ serialization MyClass2<$unknown>? + c: »MyClass2?«, + // $TEST$ serialization MyClass2? + d: »MyClass2?«, + + // $TEST$ serialization MyClass3? + y: »MyClass3?«, + // $TEST$ serialization $unknown + z: »unresolved?«, +) diff --git a/packages/safe-ds-lang/tests/resources/typing/types/type arguments/main.sdstest b/packages/safe-ds-lang/tests/resources/typing/types/type arguments/main.sdstest index 1c2d8d8ba..0f6b3c214 100644 --- a/packages/safe-ds-lang/tests/resources/typing/types/type arguments/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/typing/types/type arguments/main.sdstest @@ -13,8 +13,8 @@ fun myFunction( // $TEST$ serialization Boolean f: unresolved<»T = Boolean«>, - // $TEST$ serialization ? + // $TEST$ serialization $unknown g: MyClass<»unresolved«>, - // $TEST$ serialization ? + // $TEST$ serialization $unknown h: MyClass<»T = unresolved«>, ) diff --git a/packages/safe-ds-lang/tests/resources/validation/experimental language feature/indexed access/main.sdstest b/packages/safe-ds-lang/tests/resources/validation/experimental language feature/indexed access/main.sdstest deleted file mode 100644 index 4ae8e1b56..000000000 --- a/packages/safe-ds-lang/tests/resources/validation/experimental language feature/indexed access/main.sdstest +++ /dev/null @@ -1,15 +0,0 @@ -package tests.validation.experimentalLanguageFeature.indexedAccess - -pipeline myPipeline { - // $TEST$ warning "Indexed accesses are experimental and may change without prior notice." - »[1, 2][1]«; - - // $TEST$ warning "Indexed accesses are experimental and may change without prior notice." - »{"a": "b"}["a"]«; - - // $TEST$ no warning "Indexed accesses are experimental and may change without prior notice." - [1, 2][»[1, 2][1]«]; - - // $TEST$ no warning "Indexed accesses are experimental and may change without prior notice." - {"a": "b"}[»{"a": "b"}["a"]«]; -} diff --git a/packages/safe-ds-lang/tests/resources/validation/experimental language feature/type argument lists/main.sdstest b/packages/safe-ds-lang/tests/resources/validation/experimental language feature/type argument lists/main.sdstest deleted file mode 100644 index 60cc2e1c6..000000000 --- a/packages/safe-ds-lang/tests/resources/validation/experimental language feature/type argument lists/main.sdstest +++ /dev/null @@ -1,11 +0,0 @@ - -package tests.validation.experimentalLanguageFeature.typeArgumentLists - -// $TEST$ warning "Type argument lists & type arguments are experimental and may change without prior notice." -class MyClass1(p: MyClass1»<>«) - -// $TEST$ no warning "Type argument lists & type arguments are experimental and may change without prior notice." -class MyClass2(p: MyClass2«>) - -// $TEST$ no warning "Type argument lists & type arguments are experimental and may change without prior notice." -class MyClass2(p: union»<>«) diff --git a/packages/safe-ds-lang/tests/resources/validation/experimental language feature/type parameter lists/main.sdstest b/packages/safe-ds-lang/tests/resources/validation/experimental language feature/type parameter lists/main.sdstest deleted file mode 100644 index 73c33fb2b..000000000 --- a/packages/safe-ds-lang/tests/resources/validation/experimental language feature/type parameter lists/main.sdstest +++ /dev/null @@ -1,8 +0,0 @@ - -package tests.validation.experimentalLanguageFeature.typeParameterLists - -// $TEST$ warning "Type parameter lists & type parameters are experimental and may change without prior notice." -class MyClass»<>« - -// $TEST$ warning "Type parameter lists & type parameters are experimental and may change without prior notice." -fun myFunction»<>«() diff --git a/packages/safe-ds-lang/tests/resources/validation/other/declarations/type parameters/usage of class type parameters/main.sdstest b/packages/safe-ds-lang/tests/resources/validation/other/declarations/type parameters/usage of class type parameters/main.sdstest index 5ad6b2b4d..6a5b11d7c 100644 --- a/packages/safe-ds-lang/tests/resources/validation/other/declarations/type parameters/usage of class type parameters/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/validation/other/declarations/type parameters/usage of class type parameters/main.sdstest @@ -1,7 +1,8 @@ package tests.validation.other.declarations.typeParameters.usageOfClassTypeParameters // $TEST$ no error "This type parameter of a containing class cannot be used here." -class MyClass(p: »T«) { +// $TEST$ no error "This type parameter of a containing class cannot be used here." +class MyClass(p: »T«) sub »T« { // $TEST$ no error "This type parameter of a containing class cannot be used here." attr a: »T« diff --git a/packages/safe-ds-lang/tests/resources/validation/types/checking/indexed access on list/main.sdstest b/packages/safe-ds-lang/tests/resources/validation/types/checking/indexed access on list/main.sdstest index bd29412a0..121e6234f 100644 --- a/packages/safe-ds-lang/tests/resources/validation/types/checking/indexed access on list/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/validation/types/checking/indexed access on list/main.sdstest @@ -1,15 +1,27 @@ package tests.validation.types.checking.indexedAccessOnList +@Pure fun list() -> list: List +@Pure fun index() -> index: Int + pipeline myPipeline { - // $TEST$ no error r"Expected type 'Int' but got .*\." - [1][»0«]; + // $TEST$ no error r"Expected type .* but got .*\." + [0][»0«]; // $TEST$ error "Expected type 'Int' but got 'literal<"">'." [0][»""«]; - // $TEST$ no error r"Expected type 'Int' but got .*\." - {"": ""}[»""«]; + // $TEST$ no error r"Expected type .* but got .*\." + [0][»index()«]; + + // $TEST$ no error r"Expected type .* but got .*\." + list()[»0«]; + + // $TEST$ error "Expected type 'Int' but got 'literal<"">'." + list()[»""«]; + + // $TEST$ no error r"Expected type .* but got .*\." + list()[»index()«]; - // $TEST$ no error r"Expected type 'Int' but got .*\." + // $TEST$ no error r"Expected type .* but got .*\." unresolved[»""«]; } diff --git a/packages/safe-ds-lang/tests/resources/validation/types/checking/indexed access on map/main.sdstest b/packages/safe-ds-lang/tests/resources/validation/types/checking/indexed access on map/main.sdstest new file mode 100644 index 000000000..2c8270047 --- /dev/null +++ b/packages/safe-ds-lang/tests/resources/validation/types/checking/indexed access on map/main.sdstest @@ -0,0 +1,27 @@ +package tests.validation.types.checking.indexedAccessOnMap + +@Pure fun map() -> map: Map +@Pure fun key() -> key: String + +pipeline myPipeline { + // $TEST$ no error r"Expected type .* but got .*\." + {"": ""}[»""«]; + + // $TEST$ error "Expected type 'String' but got 'literal<1>'." + {"": ""}[»1«]; + + // $TEST$ no error r"Expected type .* but got .*\." + {"": ""}[»key()«]; + + // $TEST$ no error r"Expected type .* but got .*\." + map()[»""«]; + + // $TEST$ error "Expected type 'String' but got 'literal<1>'." + map()[»1«]; + + // $TEST$ no error r"Expected type .* but got .*\." + map()[»key()«]; + + // $TEST$ no error r"Expected type .* but got .*\." + unresolved[»""«]; +} diff --git a/packages/safe-ds-lang/tests/resources/validation/types/checking/indexed access receiver/main.sdstest b/packages/safe-ds-lang/tests/resources/validation/types/checking/indexed access receiver/main.sdstest index eddbf11ea..709c88f92 100644 --- a/packages/safe-ds-lang/tests/resources/validation/types/checking/indexed access receiver/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/validation/types/checking/indexed access receiver/main.sdstest @@ -10,6 +10,6 @@ pipeline myPipeline { // $TEST$ error "Expected type 'List' or 'Map' but got 'literal<1>'." »1«[0]; - // $TEST$ error "Expected type 'List' or 'Map' but got '?'." + // $TEST$ error "Expected type 'List' or 'Map' but got '$unknown'." »unresolved«[0]; } diff --git a/packages/safe-ds-lang/tests/resources/validation/types/checking/infix operations/main.sdstest b/packages/safe-ds-lang/tests/resources/validation/types/checking/infix operations/main.sdstest index a4686c1b3..c5971c492 100644 --- a/packages/safe-ds-lang/tests/resources/validation/types/checking/infix operations/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/validation/types/checking/infix operations/main.sdstest @@ -8,8 +8,8 @@ pipeline myPipeline { // $TEST$ error "Expected type 'Boolean' but got 'literal<0>'." // $TEST$ error "Expected type 'Boolean' but got 'literal<0>'." »0« or »0«; - // $TEST$ error "Expected type 'Boolean' but got '?'." - // $TEST$ error "Expected type 'Boolean' but got '?'." + // $TEST$ error "Expected type 'Boolean' but got '$unknown'." + // $TEST$ error "Expected type 'Boolean' but got '$unknown'." »unresolved« or »unresolved«; // $TEST$ no error r"Expected type 'Boolean' but got .*\." @@ -18,8 +18,8 @@ pipeline myPipeline { // $TEST$ error "Expected type 'Boolean' but got 'literal<0>'." // $TEST$ error "Expected type 'Boolean' but got 'literal<0>'." »0« and »0«; - // $TEST$ error "Expected type 'Boolean' but got '?'." - // $TEST$ error "Expected type 'Boolean' but got '?'." + // $TEST$ error "Expected type 'Boolean' but got '$unknown'." + // $TEST$ error "Expected type 'Boolean' but got '$unknown'." »unresolved« and »unresolved«; @@ -32,8 +32,8 @@ pipeline myPipeline { // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." »""« + »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." »unresolved« + »unresolved«; // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." @@ -45,8 +45,8 @@ pipeline myPipeline { // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." »""« - »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." »unresolved« - »unresolved«; // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." @@ -58,8 +58,8 @@ pipeline myPipeline { // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." »""« * »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." »unresolved« * »unresolved«; // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." @@ -71,8 +71,8 @@ pipeline myPipeline { // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." »""« / »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." »unresolved« / »unresolved«; @@ -85,8 +85,8 @@ pipeline myPipeline { // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." »""« < »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." »unresolved« < »unresolved«; // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." @@ -98,8 +98,8 @@ pipeline myPipeline { // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." »""« <= »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." »unresolved« <= »unresolved«; // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." @@ -111,8 +111,8 @@ pipeline myPipeline { // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." »""« >= »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." »unresolved« >= »unresolved«; // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." @@ -124,7 +124,7 @@ pipeline myPipeline { // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." »""« > »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." »unresolved« > »unresolved«; } diff --git a/packages/safe-ds-lang/tests/resources/validation/types/checking/prefix operations/main.sdstest b/packages/safe-ds-lang/tests/resources/validation/types/checking/prefix operations/main.sdstest index 275f0f79b..750bcb508 100644 --- a/packages/safe-ds-lang/tests/resources/validation/types/checking/prefix operations/main.sdstest +++ b/packages/safe-ds-lang/tests/resources/validation/types/checking/prefix operations/main.sdstest @@ -6,7 +6,7 @@ segment mySegment() { not »true«; // $TEST$ error "Expected type 'Boolean' but got 'literal<0>'." not »0«; - // $TEST$ error "Expected type 'Boolean' but got '?'." + // $TEST$ error "Expected type 'Boolean' but got '$unknown'." not »unresolved«; // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." @@ -15,6 +15,6 @@ segment mySegment() { -»0«; // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." -»""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got '?'." + // $TEST$ error "Expected type 'Float' or 'Int' but got '$unknown'." -»unresolved«; }