Skip to content

Commit 87a945b

Browse files
committed
fix: assignability of static type to callable type
1 parent ed33676 commit 87a945b

File tree

2 files changed

+102
-10
lines changed

2 files changed

+102
-10
lines changed

packages/safe-ds-lang/src/language/typing/safe-ds-type-checker.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { getContainerOfType } from 'langium';
22
import type { SafeDsClasses } from '../builtins/safe-ds-classes.js';
3-
import { isSdsEnum, SdsDeclaration } from '../generated/ast.js';
3+
import { isSdsEnum, type SdsAbstractResult, SdsDeclaration } from '../generated/ast.js';
4+
import { getParameters } from '../helpers/nodeProperties.js';
45
import {
56
BooleanConstant,
67
Constant,
@@ -16,6 +17,7 @@ import {
1617
EnumType,
1718
EnumVariantType,
1819
LiteralType,
20+
NamedTupleEntry,
1921
NamedTupleType,
2022
StaticType,
2123
Type,
@@ -24,16 +26,19 @@ import {
2426
} from './model.js';
2527
import { SafeDsClassHierarchy } from './safe-ds-class-hierarchy.js';
2628
import { SafeDsCoreTypes } from './safe-ds-core-types.js';
29+
import type { SafeDsTypeComputer } from './safe-ds-type-computer.js';
2730

2831
export class SafeDsTypeChecker {
2932
private readonly builtinClasses: SafeDsClasses;
3033
private readonly classHierarchy: SafeDsClassHierarchy;
3134
private readonly coreTypes: SafeDsCoreTypes;
35+
private readonly typeComputer: () => SafeDsTypeComputer;
3236

3337
constructor(services: SafeDsServices) {
3438
this.builtinClasses = services.builtins.Classes;
3539
this.classHierarchy = services.types.ClassHierarchy;
3640
this.coreTypes = services.types.CoreTypes;
41+
this.typeComputer = () => services.types.TypeComputer;
3742
}
3843

3944
/**
@@ -218,8 +223,48 @@ export class SafeDsTypeChecker {
218223
}
219224
}
220225

221-
private staticTypeIsAssignableTo(type: Type, other: Type): boolean {
222-
return type.equals(other);
226+
private staticTypeIsAssignableTo(type: StaticType, other: Type): boolean {
227+
if (other instanceof CallableType) {
228+
return this.isAssignableTo(this.associatedCallableTypeForStaticType(type), other);
229+
} else {
230+
return type.equals(other);
231+
}
232+
}
233+
234+
private associatedCallableTypeForStaticType(type: StaticType): Type {
235+
const instanceType = type.instanceType;
236+
if (instanceType instanceof ClassType) {
237+
const declaration = instanceType.declaration;
238+
if (!declaration.parameterList) {
239+
return UnknownType;
240+
}
241+
242+
const parameterEntries = new NamedTupleType(
243+
...getParameters(declaration).map(
244+
(it) => new NamedTupleEntry(it, it.name, this.typeComputer().computeType(it)),
245+
),
246+
);
247+
const resultEntries = new NamedTupleType(
248+
new NamedTupleEntry<SdsAbstractResult>(undefined, 'instance', instanceType),
249+
);
250+
251+
return new CallableType(declaration, parameterEntries, resultEntries);
252+
} else if (instanceType instanceof EnumVariantType) {
253+
const declaration = instanceType.declaration;
254+
255+
const parameterEntries = new NamedTupleType(
256+
...getParameters(declaration).map(
257+
(it) => new NamedTupleEntry(it, it.name, this.typeComputer().computeType(it)),
258+
),
259+
);
260+
const resultEntries = new NamedTupleType(
261+
new NamedTupleEntry<SdsAbstractResult>(undefined, 'instance', instanceType),
262+
);
263+
264+
return new CallableType(declaration, parameterEntries, resultEntries);
265+
} else {
266+
return UnknownType;
267+
}
223268
}
224269

225270
private unionTypeIsAssignableTo(type: UnionType, other: Type): boolean {

packages/safe-ds-lang/tests/language/typing/safe-ds-type-checker.test.ts

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {
1919
} from '../../../src/language/partialEvaluation/model.js';
2020
import {
2121
ClassType,
22+
EnumType,
23+
EnumVariantType,
2224
LiteralType,
2325
NamedTupleEntry,
2426
NamedTupleType,
@@ -45,13 +47,15 @@ const code = `
4547
fun func8() -> (s: Int)
4648
fun func9() -> (r: Any)
4749
fun func10() -> (r: String)
50+
fun func11() -> (r: Class1)
51+
fun func12() -> (r: Enum1)
4852
49-
class Class1
50-
class Class2 sub Class1
53+
class Class1(p: Int)
54+
class Class2() sub Class1
5155
class Class3
5256
5357
enum Enum1 {
54-
Variant1
58+
Variant1(p: Int)
5559
Variant2
5660
}
5761
enum Enum2
@@ -68,6 +72,8 @@ const callableType7 = typeComputer.computeType(functions[6]);
6872
const callableType8 = typeComputer.computeType(functions[7]);
6973
const callableType9 = typeComputer.computeType(functions[8]);
7074
const callableType10 = typeComputer.computeType(functions[9]);
75+
const callableType11 = typeComputer.computeType(functions[10]);
76+
const callableType12 = typeComputer.computeType(functions[11]);
7177

7278
const classes = getModuleMembers(module).filter(isSdsClass);
7379
const class1 = classes[0];
@@ -80,14 +86,14 @@ const classType3 = typeComputer.computeType(class3) as ClassType;
8086
const enums = getModuleMembers(module).filter(isSdsEnum);
8187
const enum1 = enums[0];
8288
const enum2 = enums[1];
83-
const enumType1 = typeComputer.computeType(enum1);
84-
const enumType2 = typeComputer.computeType(enum2);
89+
const enumType1 = typeComputer.computeType(enum1) as EnumType;
90+
const enumType2 = typeComputer.computeType(enum2) as EnumType;
8591

8692
const enumVariants = streamAllContents(module).filter(isSdsEnumVariant).toArray();
8793
const enumVariant1 = enumVariants[0];
8894
const enumVariant2 = enumVariants[1];
89-
const enumVariantType1 = typeComputer.computeType(enumVariant1);
90-
const enumVariantType2 = typeComputer.computeType(enumVariant2);
95+
const enumVariantType1 = typeComputer.computeType(enumVariant1) as EnumVariantType;
96+
const enumVariantType2 = typeComputer.computeType(enumVariant2) as EnumVariantType;
9197

9298
describe('SafeDsTypeChecker', async () => {
9399
const testCases: IsAssignableToTest[] = [
@@ -521,6 +527,47 @@ describe('SafeDsTypeChecker', async () => {
521527
type2: enumType1,
522528
expected: false,
523529
},
530+
// Static type to callable type
531+
{
532+
type1: new StaticType(classType1),
533+
type2: callableType1,
534+
expected: false,
535+
},
536+
{
537+
type1: new StaticType(classType1),
538+
type2: callableType3,
539+
expected: true,
540+
},
541+
{
542+
type1: new StaticType(classType2),
543+
type2: callableType11,
544+
expected: true,
545+
},
546+
{
547+
type1: new StaticType(classType3),
548+
type2: callableType1,
549+
expected: false,
550+
},
551+
{
552+
type1: new StaticType(enumType1),
553+
type2: callableType1,
554+
expected: false,
555+
},
556+
{
557+
type1: new StaticType(enumVariantType1),
558+
type2: callableType1,
559+
expected: false,
560+
},
561+
{
562+
type1: new StaticType(enumVariantType1),
563+
type2: callableType3,
564+
expected: true,
565+
},
566+
{
567+
type1: new StaticType(enumVariantType2),
568+
type2: callableType12,
569+
expected: true,
570+
},
524571
// Static type to static type
525572
{
526573
type1: new StaticType(classType1),

0 commit comments

Comments
 (0)