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 62b0e1c91..5a26858dc 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 @@ -130,7 +130,7 @@ export class SafeDsTypeChecker { const otherEntry = other.inputType.entries[i]!; // Names must match - if (typeEntry.name !== otherEntry.name) { + if (!options.ignoreParameterNames && typeEntry.name !== otherEntry.name) { return false; } @@ -438,9 +438,14 @@ export class SafeDsTypeChecker { /** * Options for {@link SafeDsTypeChecker.isSubtypeOf} and {@link SafeDsTypeChecker.isSupertypeOf}. */ -interface TypeCheckOptions { +export interface TypeCheckOptions { /** * Whether to ignore type parameters when comparing class types. */ ignoreTypeParameters?: boolean; + + /** + * Whether to ignore parameter names when comparing callable types. + */ + ignoreParameterNames?: boolean; } diff --git a/packages/safe-ds-lang/src/language/validation/types.ts b/packages/safe-ds-lang/src/language/validation/types.ts index f859a1d92..713deb63f 100644 --- a/packages/safe-ds-lang/src/language/validation/types.ts +++ b/packages/safe-ds-lang/src/language/validation/types.ts @@ -54,7 +54,7 @@ export const callArgumentTypesMustMatchParameterTypes = (services: SafeDsService const argumentType = typeComputer.computeType(argument).substituteTypeParameters(substitutions); const parameterType = typeComputer.computeType(parameter).substituteTypeParameters(substitutions); - if (!typeChecker.isSubtypeOf(argumentType, parameterType)) { + if (!typeChecker.isSubtypeOf(argumentType, parameterType, { ignoreParameterNames: true })) { accept('error', `Expected type '${parameterType}' but got '${argumentType}'.`, { node: argument, property: 'value', diff --git a/packages/safe-ds-lang/tests/language/typing/type checker/isSubOrSupertypeOf.test.ts b/packages/safe-ds-lang/tests/language/typing/type checker/isSubOrSupertypeOf.test.ts index c37209f0c..fdda32898 100644 --- a/packages/safe-ds-lang/tests/language/typing/type checker/isSubOrSupertypeOf.test.ts +++ b/packages/safe-ds-lang/tests/language/typing/type checker/isSubOrSupertypeOf.test.ts @@ -33,6 +33,7 @@ import { } from '../../../../src/language/typing/model.js'; import { getNodeOfType } from '../../../helpers/nodeFinder.js'; import { AstUtils } from 'langium'; +import { TypeCheckOptions } from '../../../../src/language/typing/safe-ds-type-checker.js'; const services = (await createSafeDsServices(NodeFileSystem)).SafeDs; const coreTypes = services.typing.CoreTypes; @@ -137,6 +138,14 @@ const basic = async (): Promise => { type2: callableType4, expected: false, }, + { + type1: callableType3, + type2: callableType4, + options: { + ignoreParameterNames: true, + }, + expected: true, + }, { type1: callableType3, type2: callableType5, @@ -1229,15 +1238,15 @@ const typeVariables = async (): Promise => { describe('SafeDsTypeChecker', async () => { const testCases = (await Promise.all([basic(), classTypesWithTypeParameters(), typeVariables()])).flat(); - describe.each(testCases)('isSubtypeOf', ({ type1, type2, expected }) => { - it(`should check whether ${type1} a subtype of ${type2}`, () => { - expect(typeChecker.isSubtypeOf(type1, type2)).toBe(expected); + describe.each(testCases)('isSubtypeOf', ({ type1, type2, options, expected }) => { + it(`should check whether ${type1} a subtype of ${type2} ${options ? `with options ${JSON.stringify(options)}` : ''}`, () => { + expect(typeChecker.isSubtypeOf(type1, type2, options)).toBe(expected); }); }); - describe.each(testCases)('isSupertypeOf', ({ type2, type1, expected }) => { - it(`should check whether ${type2} a supertype of ${type1}`, () => { - expect(typeChecker.isSupertypeOf(type2, type1)).toBe(expected); + describe.each(testCases)('isSupertypeOf', ({ type2, type1, options, expected }) => { + it(`should check whether ${type2} a supertype of ${type1} ${options ? `with options ${JSON.stringify(options)}` : ''}`, () => { + expect(typeChecker.isSupertypeOf(type2, type1, options)).toBe(expected); }); }); }); @@ -1263,6 +1272,11 @@ interface IsSubOrSupertypeOfTest { */ type2: Type; + /** + * Options for type checking. + */ + options?: TypeCheckOptions; + /** * Whether {@link type1} is expected to be assignable to {@link type2}. */ diff --git a/packages/safe-ds-lang/tests/resources/validation/types/checking/arguments/parameter names of callable types.sdsdev b/packages/safe-ds-lang/tests/resources/validation/types/checking/arguments/parameter names of callable types.sdsdev new file mode 100644 index 000000000..325387d92 --- /dev/null +++ b/packages/safe-ds-lang/tests/resources/validation/types/checking/arguments/parameter names of callable types.sdsdev @@ -0,0 +1,13 @@ +package tests.validation.types.checking.arguments.parameterNamesOfCallableTypes + +@Pure fun f(callback: (p: Int) -> (r: Int)) + +segment mySegment() { + // $TEST$ no error r"Expected type .* but got .*\." + f(»(q) { + yield s = 1; + }«); + + // $TEST$ no error r"Expected type .* but got .*\." + f(»(q) -> 1«); +}