Skip to content

Commit dc14223

Browse files
authored
feat: type checking for list & map literals (#751)
Closes #712 ### Summary of Changes Don't allow calls that produce no or multiple results inside list & map literals. In other cases, the existing type checking already took care of this.
1 parent 52374aa commit dc14223

File tree

4 files changed

+84
-1
lines changed

4 files changed

+84
-1
lines changed

packages/safe-ds-lang/src/language/validation/safe-ds-validator.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ import {
149149
indexedAccessIndexMustHaveCorrectType,
150150
indexedAccessReceiverMustBeListOrMap,
151151
infixOperationOperandsMustHaveCorrectType,
152+
listMustNotContainNamedTuples,
153+
mapMustNotContainNamedTuples,
152154
namedTypeMustSetAllTypeParameters,
153155
parameterDefaultValueTypeMustMatchParameterType,
154156
parameterMustHaveTypeHint,
@@ -253,14 +255,15 @@ export const registerValidationChecks = function (services: SafeDsServices) {
253255
lambdaParametersMustNotBeAnnotated,
254256
lambdaParameterMustNotHaveConstModifier,
255257
],
258+
SdsList: [listMustNotContainNamedTuples(services)],
256259
SdsLiteralType: [
257260
literalTypeMustHaveLiterals,
258261
literalTypeMustNotContainListLiteral,
259262
literalTypeMustNotContainMapLiteral,
260263
literalTypesShouldBeUsedWithCaution,
261264
literalTypeShouldNotHaveDuplicateLiteral(services),
262265
],
263-
SdsMap: [mapsShouldBeUsedWithCaution],
266+
SdsMap: [mapMustNotContainNamedTuples(services), mapsShouldBeUsedWithCaution],
264267
SdsMemberAccess: [
265268
memberAccessMustBeNullSafeIfReceiverIsNullable(services),
266269
memberAccessNullSafetyShouldBeNeeded(services),

packages/safe-ds-lang/src/language/validation/types.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
SdsCall,
1616
SdsIndexedAccess,
1717
SdsInfixOperation,
18+
SdsList,
19+
SdsMap,
1820
SdsNamedType,
1921
SdsParameter,
2022
SdsPrefixOperation,
@@ -23,6 +25,7 @@ import {
2325
} from '../generated/ast.js';
2426
import { getTypeArguments, getTypeParameters } from '../helpers/nodeProperties.js';
2527
import { SafeDsServices } from '../safe-ds-module.js';
28+
import { NamedTupleType } from '../typing/model.js';
2629

2730
export const CODE_TYPE_CALLABLE_RECEIVER = 'type/callable-receiver';
2831
export const CODE_TYPE_MISMATCH = 'type/mismatch';
@@ -191,6 +194,48 @@ export const infixOperationOperandsMustHaveCorrectType = (services: SafeDsServic
191194
};
192195
};
193196

197+
export const listMustNotContainNamedTuples = (services: SafeDsServices) => {
198+
const typeComputer = services.types.TypeComputer;
199+
200+
return (node: SdsList, accept: ValidationAcceptor): void => {
201+
for (const element of node.elements) {
202+
const elementType = typeComputer.computeType(element);
203+
if (elementType instanceof NamedTupleType) {
204+
accept('error', `Cannot add a value of type '${elementType}' to a list.`, {
205+
node: element,
206+
code: CODE_TYPE_MISMATCH,
207+
});
208+
}
209+
}
210+
};
211+
};
212+
213+
export const mapMustNotContainNamedTuples = (services: SafeDsServices) => {
214+
const typeComputer = services.types.TypeComputer;
215+
216+
return (node: SdsMap, accept: ValidationAcceptor): void => {
217+
for (const entry of node.entries) {
218+
const keyType = typeComputer.computeType(entry.key);
219+
if (keyType instanceof NamedTupleType) {
220+
accept('error', `Cannot use a value of type '${keyType}' as a map key.`, {
221+
node: entry,
222+
property: 'key',
223+
code: CODE_TYPE_MISMATCH,
224+
});
225+
}
226+
227+
const valueKey = typeComputer.computeType(entry.value);
228+
if (valueKey instanceof NamedTupleType) {
229+
accept('error', `Cannot use a value of type '${valueKey}' as a map value.`, {
230+
node: entry,
231+
property: 'value',
232+
code: CODE_TYPE_MISMATCH,
233+
});
234+
}
235+
}
236+
};
237+
};
238+
194239
export const parameterDefaultValueTypeMustMatchParameterType = (services: SafeDsServices) => {
195240
const typeChecker = services.types.TypeChecker;
196241
const typeComputer = services.types.TypeComputer;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package tests.validation.types.checking.lists
2+
3+
fun noResults()
4+
fun oneResult() -> r: Int
5+
fun twoResults() -> (r1: Int, r2: Int)
6+
7+
segment mySegment() {
8+
[
9+
// $TEST$ error "Cannot add a value of type '()' to a list."
10+
»noResults()«,
11+
// $TEST$ no error r"Cannot add a value of type '.*' to a list\."
12+
»oneResult()«,
13+
// $TEST$ error "Cannot add a value of type '(r1: Int, r2: Int)' to a list."
14+
»twoResults()«
15+
];
16+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package tests.validation.types.checking.maps
2+
3+
fun noResults()
4+
fun oneResult() -> r: Int
5+
fun twoResults() -> (r1: Int, r2: Int)
6+
7+
segment mySegment() {
8+
{
9+
// $TEST$ error "Cannot use a value of type '()' as a map key."
10+
// $TEST$ error "Cannot use a value of type '()' as a map value."
11+
»noResults()«: »noResults()«,
12+
// $TEST$ no error r"Cannot use a value of type '.*' as a map key\."
13+
// $TEST$ no error r"Cannot use a value of type '.*' as a map value\."
14+
»oneResult()«: »oneResult()«,
15+
// $TEST$ error "Cannot use a value of type '(r1: Int, r2: Int)' as a map key."
16+
// $TEST$ error "Cannot use a value of type '(r1: Int, r2: Int)' as a map value."
17+
»twoResults()«: »twoResults()«
18+
};
19+
}

0 commit comments

Comments
 (0)