Skip to content

Commit

Permalink
feat: unknown type (#973)
Browse files Browse the repository at this point in the history
Closes #967

### Summary of Changes

The keyword `unknown` can now be used as a type as well. It denotes that
the actual type is not known. To do anything with it, values with that
type must be cast to another type.
  • Loading branch information
lars-reimann authored Apr 1, 2024
1 parent dfb4b29 commit 4638249
Show file tree
Hide file tree
Showing 57 changed files with 161 additions and 133 deletions.
4 changes: 4 additions & 0 deletions docs/language/common/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,10 @@ If exactly one result is expected, the surrounding parentheses may be also remov
(a: Int, b: Int) -> r: Int
```

### Unknown

If the actual type of a declaration is not known, you can denote that with the special type `#!sds unknown`. However, to later use the declaration in any meaningful way, you will have to cast it to another type.

## Corresponding Python Code

**Note:** This section is only relevant if you are interested in the [stub language][stub-language].
Expand Down
7 changes: 7 additions & 0 deletions packages/safe-ds-lang/src/language/grammar/safe-ds.langium
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,7 @@ SdsPrimaryType returns SdsType:
| SdsLiteralType
| SdsNamedType
| SdsUnionType
| SdsUnknownType
;

interface SdsCallableType extends SdsCallable, SdsType {
Expand Down Expand Up @@ -975,6 +976,12 @@ SdsUnionTypeArgument returns SdsTypeArgument:
value=SdsType
;

interface SdsUnknownType extends SdsType {}

SdsUnknownType returns SdsType:
{SdsUnknownType} 'unknown'
;

SdsParentType returns SdsType:
SdsNamedType ({SdsMemberType.receiver=current} '.' member=SdsNamedType)*
;
Expand Down
2 changes: 1 addition & 1 deletion packages/safe-ds-lang/src/language/typing/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ class UnknownTypeClass extends Type {
}

override toString(): string {
return '$unknown';
return 'unknown';
}

override simplify(): Type {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
isSdsTypeParameter,
isSdsUnionType,
isSdsUnknown,
isSdsUnknownType,
isSdsYield,
SdsAbstractResult,
SdsAssignee,
Expand Down Expand Up @@ -605,6 +606,8 @@ export class SafeDsTypeComputer {
return this.factory.createUnionType(
...typeArguments.map((typeArgument) => this.computeType(typeArgument.value)),
);
} else if (isSdsUnknownType(node)) {
return UnknownType;
} /* c8 ignore start */ else {
return UnknownType;
} /* c8 ignore stop */
Expand Down Expand Up @@ -740,7 +743,7 @@ export class SafeDsTypeComputer {
/**
* Returns the upper bound for the given input. If no upper bound is specified explicitly, the result is `Any?`. If
* invalid upper bounds are specified, but are invalid (e.g. because of an unresolved reference or a cycle),
* `$unknown` is returned. The result is simplified as much as possible.
* `unknown` is returned. The result is simplified as much as possible.
*/
computeUpperBound(nodeOrType: SdsTypeParameter | TypeParameterType, options: ComputeUpperBoundOptions = {}): Type {
let type: TypeParameterType;
Expand Down
10 changes: 5 additions & 5 deletions packages/safe-ds-lang/tests/language/typing/model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ describe('type model', async () => {
factory.createNamedTupleType(new NamedTupleEntry(parameter1, 'p1', UnknownType)),
factory.createNamedTupleType(),
),
expectedString: '(p1: $unknown) -> ()',
expectedString: '(p1: unknown) -> ()',
},
{
value: factory.createCallableType(
Expand All @@ -168,15 +168,15 @@ describe('type model', async () => {
factory.createNamedTupleType(new NamedTupleEntry(parameter2, 'p2', UnknownType)),
factory.createNamedTupleType(),
),
expectedString: '(p2?: $unknown) -> ()',
expectedString: '(p2?: unknown) -> ()',
},
{
value: factory.createLiteralType(new BooleanConstant(true)),
expectedString: 'literal<true>',
},
{
value: factory.createNamedTupleType(new NamedTupleEntry(parameter1, 'p1', UnknownType)),
expectedString: '(p1: $unknown)',
expectedString: '(p1: unknown)',
},
{
value: new ClassType(class1, new Map(), false),
Expand Down Expand Up @@ -220,11 +220,11 @@ describe('type model', async () => {
},
{
value: factory.createUnionType(UnknownType),
expectedString: 'union<$unknown>',
expectedString: 'union<unknown>',
},
{
value: UnknownType,
expectedString: '$unknown',
expectedString: 'unknown',
},
];
describe.each(toStringTests)('toString', ({ value, expectedString }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
segment mySegment(x: unknown) {}

// -----------------------------------------------------------------------------

segment mySegment(x: unknown) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// $TEST$ no_syntax_error

segment mySegment(
x: unknown
) {}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ segment mySegment() -> (r: Int) {

() {
// $TEST$ serialization literal<1>
// $TEST$ serialization $unknown
// $TEST$ serialization unknown
yield »r«, yield »s« = 1;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ segment mySegment1() -> (r: Int) {

segment mySegment2() -> (r: Int, s: String) {
// $TEST$ serialization literal<1>
// $TEST$ serialization $unknown
// $TEST$ serialization unknown
val »r«, val »s« = 1;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ segment mySegment1() -> (r: Int) {

segment mySegment2() -> (r: Int, s: String) {
// $TEST$ serialization literal<1>
// $TEST$ serialization $unknown
// $TEST$ serialization unknown
»yield r«, »yield s« = 1;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ annotation »myAnnotation1«
// $TEST$ serialization (p1: Int, p2: String) -> ()
annotation »myAnnotation3«(p1: Int, p2: String)

// $TEST$ serialization (p1: $unknown) -> ()
// $TEST$ serialization (p1: unknown) -> ()
annotation »myAnnotation5«(p1)
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package tests.typing.declarations.attributes

class C {
// $TEST$ serialization $unknown
// $TEST$ serialization unknown
attr »a«

// $TEST$ equivalence_class instanceAttribute
// $TEST$ equivalence_class instanceAttribute
attr »b«: »Int«

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
static attr »c«

// $TEST$ equivalence_class staticAttribute
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: $unknown) -> (r1: $unknown)
// $TEST$ serialization (p1: unknown) -> (r1: unknown)
fun »myFunction5«(p1) -> (r1)
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ package tests.typing.declarations.parameters.ofAnnotations
// $TEST$ equivalence_class parameterType
annotation MyAnnotation1(»p«: »Int«)

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
annotation MyAnnotation2(»p«)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package tests.typing.declarations.parameters.ofBlockLambdas.thatAreIsolated

segment mySegment() {
// $TEST$ serialization $unknown
// $TEST$ serialization unknown
(»p«) {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ segment mySegment() {
// $TEST$ equivalence_class parameterType1
higherOrderFunction1(param = (»p«) {});

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
higherOrderFunction2((»p«) {});

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
higherOrderFunction2(param = (»p«) {});

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
normalFunction((»p«) {});

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
normalFunction(param = (»p«) {});
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ fun higherOrderFunction1(
)

fun higherOrderFunction2(
// $TEST$ serialization $unknown
// $TEST$ serialization unknown
param: () -> () = (»p«) {}
)

fun normalFunction(
// $TEST$ serialization $unknown
// $TEST$ serialization unknown
param: Int = (»p«) {}
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ segment mySegment() -> (
// $TEST$ equivalence_class parameterType2
yield r = (»p«) {};

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
yield s = (»p«) {};

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
yield t = (»p«) {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ package tests.typing.declarations.parameters.ofCallableTypes
// $TEST$ equivalence_class parameterType
annotation MyAnnotation1(f: (»p«: »Int«) -> ())

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
annotation MyAnnotation2(f: (»p«) -> ())
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ package tests.typing.declarations.parameters.ofClasses
// $TEST$ equivalence_class parameterType
class MyClass1(»p«: »Int«)

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
class MyClass2(»p«)
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ enum MyEnum {
// $TEST$ equivalence_class parameterType
MyEnumVariant1(»p«: »Int«)

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
MyEnumVariant2(»p«)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package tests.typing.declarations.parameters.ofExpressionLambdas.thatAreIsolated

segment mySegment() {
// $TEST$ serialization $unknown
// $TEST$ serialization unknown
(»p«) -> 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ segment mySegment() {
// $TEST$ equivalence_class parameterType1
higherOrderFunction1(param = (»p«) -> "");

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
higherOrderFunction2((»p«) -> "");

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
higherOrderFunction2(param = (»p«) -> "");

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
normalFunction((»p«) -> "");

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
normalFunction(param = (»p«) -> "");
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ fun higherOrderFunction1(
)

fun higherOrderFunction2(
// $TEST$ serialization $unknown
// $TEST$ serialization unknown
param: () -> r: String = (»p«) -> ""
)

fun normalFunction(
// $TEST$ serialization $unknown
// $TEST$ serialization unknown
param: Int = (»p«) -> ""
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ segment mySegment() -> (
// $TEST$ equivalence_class parameterType2
yield r = (»p«) -> true;

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
yield s = (»p«) -> true;

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
yield t = (»p«) -> true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ package tests.typing.declarations.parameters.ofFunctions
// $TEST$ equivalence_class parameterType
fun myFunction1(»p«: »Int«)

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
fun myFunction2(»p«)
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ package tests.typing.declarations.parameters.ofSegments
// $TEST$ equivalence_class parameterType
segment mySegment1(»p«: »Int«) {}

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
segment mySegment2(»p«) {}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package tests.typing.declarations.pipelines

// $TEST$ serialization $unknown
// $TEST$ serialization unknown
pipeline »myPipeline« {}
Original file line number Diff line number Diff line change
Expand Up @@ -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: $unknown) -> (r1: $unknown)
// $TEST$ serialization (p1: unknown) -> (r1: unknown)
segment »mySegment5«(p1) -> (r1) {}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package tests.typing.expressions.blockLambdas.thatAreIsolated
fun g() -> r: Int

segment mySegment() {
// $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: $unknown)
// $TEST$ serialization (p: unknown) -> (r: literal<1>, s: unknown)
»(p) {
yield r, yield s = 1;
}«;

// $TEST$ serialization (p: $unknown) -> (r: Int, s: $unknown)
// $TEST$ serialization (p: unknown) -> (r: Int, s: unknown)
val f = »(p) {
yield r, yield s = g();
}«;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,34 @@ segment mySegment() {
yield s = "";
}«);

// $TEST$ serialization (p: String) -> (r: literal<1>, s: $unknown)
// $TEST$ serialization (p: String) -> (r: literal<1>, s: unknown)
higherOrderFunction1(param = »(p) {
yield r, yield s = 1;
}«);

// $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: literal<"">)
// $TEST$ serialization (p: unknown) -> (r: literal<1>, s: literal<"">)
higherOrderFunction2(»(p) {
yield r = 1;
yield s = "";
}«);

// $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: $unknown)
// $TEST$ serialization (p: unknown) -> (r: literal<1>, s: unknown)
higherOrderFunction2(param = »(p) {
yield r, yield s = 1;
}«);

// $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: literal<"">)
// $TEST$ serialization (p: unknown) -> (r: literal<1>, s: literal<"">)
normalFunction(»(p) {
yield r = 1;
yield s = "";
}«);

// $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: $unknown)
// $TEST$ serialization (p: unknown) -> (r: literal<1>, s: unknown)
normalFunction(param = »(p) {
yield r, yield s = 1;
}«);

// $TEST$ serialization (p: $unknown) -> (r: literal<1>, s: $unknown)
// $TEST$ serialization (p: unknown) -> (r: literal<1>, s: unknown)
parameterlessFunction(»(p) {
yield r, yield s = 1;
}«);
Expand Down
Loading

0 comments on commit 4638249

Please sign in to comment.