Skip to content

Commit

Permalink
feat: apply type parameter substitutions of receiver type for member …
Browse files Browse the repository at this point in the history
…accesses (#859)

Closes partially #23

### Summary of Changes

Let's look at the following example:

```
class C<T> {
    attr member: T
}

segment mySegment(p: C<Int>) {
    val a = p.member;
```

Previously, the type of the placeholder `a` was computed as `T` (type
parameter type). Now, the type parameter substitutions that were
computed for the receiver get applied, so `a` now gets the correct type
`Int`.
  • Loading branch information
lars-reimann authored Feb 6, 2024
1 parent 9f283d3 commit 5780ed7
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 2 deletions.
10 changes: 9 additions & 1 deletion packages/safe-ds-lang/src/language/typing/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,15 @@ export class TypeParameterType extends NamedType<SdsTypeParameter> {
}

override substituteTypeParameters(substitutions: TypeParameterSubstitutions): Type {
return substitutions.get(this.declaration) ?? this;
const substitution = substitutions.get(this.declaration);

if (!substitution) {
return this;
} else if (this.isNullable) {
return substitution.updateNullability(true);
} else {
return substitution;
}
}

override updateNullability(isNullable: boolean): TypeParameterType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,16 @@ export class SafeDsTypeComputer {
}

const receiverType = this.computeType(node.receiver);
return memberType.updateNullability((receiverType.isNullable && node.isNullSafe) || memberType.isNullable);
const unsubstitutedResult = memberType.updateNullability(
(receiverType.isNullable && node.isNullSafe) || memberType.isNullable,
);

// Substitute type parameters
if (receiverType instanceof ClassType) {
return unsubstitutedResult.substituteTypeParameters(receiverType.substitutions);
} else {
return unsubstitutedResult;
}
}

private computeTypeOfArithmeticPrefixOperation(node: SdsPrefixOperation): Type {
Expand Down
5 changes: 5 additions & 0 deletions packages/safe-ds-lang/tests/language/typing/model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,11 @@ describe('type model', async () => {
substitutions: substitutions1,
expectedType: new LiteralType(new IntConstant(1n)),
},
{
type: new TypeParameterType(typeParameter1, true),
substitutions: substitutions1,
expectedType: new LiteralType(new IntConstant(1n), NullConstant),
},
{
type: new TypeParameterType(typeParameter2, false),
substitutions: substitutions1,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package tests.typing.expressions.memberAccesses.onClassWithTypeParameters

class C<T> {
attr nonNullableMember: T
attr nullableMember: T?
@Pure fun method() -> r: T
}

@Pure fun nullableC() -> result: C<Int>?

segment mySegment(p: C<Int>) {
// $TEST$ serialization Int
»p.nonNullableMember«;
// $TEST$ serialization Int?
»p.nullableMember«;
// $TEST$ serialization () -> (r: Int)
»p.method«;

// $TEST$ serialization Int
»p?.nonNullableMember«;
// $TEST$ serialization Int?
»p?.nullableMember«;
// $TEST$ serialization () -> (r: Int)
»p?.method«;


// $TEST$ serialization Int
»nullableC().nonNullableMember«;
// $TEST$ serialization Int?
»nullableC().nullableMember«;
// $TEST$ serialization () -> (r: Int)
»nullableC().method«;

// $TEST$ serialization Int?
»nullableC()?.nonNullableMember«;
// $TEST$ serialization Int?
»nullableC()?.nullableMember«;
// $TEST$ serialization union<() -> (r: Int), literal<null>>
»nullableC()?.method«;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ class C() {

// $TEST$ equivalence_class nullableMember
attr »nullableMember«: Any?

// $TEST$ equivalence_class method
@Pure fun »method«() -> r: Int
}

fun nullableC() -> result: C?
Expand All @@ -15,20 +18,28 @@ pipeline myPipeline {
»C().nonNullableMember«;
// $TEST$ equivalence_class nullableMember
»C().nullableMember«;
// $TEST$ equivalence_class method
»C().method«;

// $TEST$ equivalence_class nonNullableMember
»C()?.nonNullableMember«;
// $TEST$ equivalence_class nullableMember
»C()?.nullableMember«;
// $TEST$ equivalence_class method
»C()?.method«;


// $TEST$ equivalence_class nonNullableMember
»nullableC().nonNullableMember«;
// $TEST$ equivalence_class nullableMember
»nullableC().nullableMember«;
// $TEST$ equivalence_class method
»nullableC().method«;

// $TEST$ serialization Int?
»nullableC()?.nonNullableMember«;
// $TEST$ equivalence_class nullableMember
»nullableC()?.nullableMember«;
// $TEST$ serialization union<() -> (r: Int), literal<null>>
»nullableC()?.method«;
}

0 comments on commit 5780ed7

Please sign in to comment.