Skip to content

Commit

Permalink
feat: error if type parameters of functions are variant (#869)
Browse files Browse the repository at this point in the history
### Summary of Changes

Variant type parameters may only occur on classes. This PR adds
validation for this.
  • Loading branch information
lars-reimann authored Feb 9, 2024
1 parent 091b2c8 commit 9bf5fec
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { SafeDsNodeMapper } from '../../../helpers/safe-ds-node-mapper.js';

export const CODE_TYPE_PARAMETER_INSUFFICIENT_CONTEXT = 'type-parameter/insufficient-context';
export const CODE_TYPE_PARAMETER_USAGE = 'type-parameter/usage';
export const CODE_TYPE_PARAMETER_VARIANCE = 'type-parameter/variance';

export const typeParameterMustHaveSufficientContext = (node: SdsTypeParameter, accept: ValidationAcceptor) => {
const containingCallable = getContainerOfType(node, isSdsCallable);
Expand Down Expand Up @@ -196,3 +197,18 @@ const nextTypePosition = (aggregator: TypePosition, step: TypePosition): TypePos
return 'covariant';
}
};

export const typeParameterMustOnlyBeVariantOnClass = (node: SdsTypeParameter, accept: ValidationAcceptor) => {
if (TypeParameter.isInvariant(node)) {
return;
}

const declarationWithTypeParameter = getContainerOfType(node.$container, isSdsDeclaration);
if (declarationWithTypeParameter && !isSdsClass(declarationWithTypeParameter)) {
accept('error', 'Only type parameters of classes can be variant.', {
node,
property: 'variance',
code: CODE_TYPE_PARAMETER_VARIANCE,
});
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import { typeParameterConstraintLeftOperandMustBeOwnTypeParameter } from './othe
import {
typeParameterMustBeUsedInCorrectPosition,
typeParameterMustHaveSufficientContext,
typeParameterMustOnlyBeVariantOnClass,
} from './other/declarations/typeParameters.js';
import { callArgumentMustBeConstantIfParameterIsConstant, callMustNotBeRecursive } from './other/expressions/calls.js';
import { divisionDivisorMustNotBeZero } from './other/expressions/infixOperations.js';
Expand Down Expand Up @@ -347,7 +348,11 @@ export const registerValidationChecks = function (services: SafeDsServices) {
SdsStatement: [statementMustDoSomething(services)],
SdsTemplateString: [templateStringMustHaveExpressionBetweenTwoStringParts],
SdsTypeCast: [typeCastExpressionMustHaveUnknownType(services)],
SdsTypeParameter: [typeParameterMustHaveSufficientContext, typeParameterMustBeUsedInCorrectPosition(services)],
SdsTypeParameter: [
typeParameterMustHaveSufficientContext,
typeParameterMustBeUsedInCorrectPosition(services),
typeParameterMustOnlyBeVariantOnClass,
],
SdsTypeParameterConstraint: [typeParameterConstraintLeftOperandMustBeOwnTypeParameter],
SdsTypeParameterList: [
typeParameterListMustNotHaveRequiredTypeParametersAfterOptionalTypeParameters,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package tests.validation.other.declarations.typeParameters.usageOfVariantTypeParameters

// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
class MyClass1<in Contravariant>(p1: »Contravariant«) {
class MyClass1<in Contravariant>(p1: »Contravariant«)
// $TEST$ error "A contravariant type parameter cannot be used in covariant position."
// $TEST$ error "A contravariant type parameter cannot be used in invariant position."
// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
sub Producer<»Contravariant«>, Middleware<»Contravariant«>, Consumer<»Contravariant«> {
// $TEST$ error "A contravariant type parameter cannot be used in covariant position."
attr a1: »Contravariant«
// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package tests.validation.other.declarations.typeParameters.usageOfVariantTypeParameters

// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
class MyClass2<out Covariant>(p1: »Covariant«) {
class MyClass2<out Covariant>(p1: »Covariant«)
// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
// $TEST$ error "A covariant type parameter cannot be used in invariant position."
// $TEST$ error "A covariant type parameter cannot be used in contravariant position."
sub Producer<»Covariant«>, Middleware<»Covariant«>, Consumer<»Covariant«> {
// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
attr a1: »Covariant«
// $TEST$ error "A covariant type parameter cannot be used in contravariant position."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package tests.validation.other.declarations.typeParameters.usageOfVariantTypeParameters

// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
class MyClass3<Invariant>(p1: »Invariant«) {
class MyClass3<Invariant>(p1: »Invariant«)
// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
sub Producer<»Invariant«>, Middleware<»Invariant«>, Consumer<»Invariant«> {
// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
attr a1: »Invariant«
// $TEST$ no error r"A .*variant type parameter cannot be used in .*variant position."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package tests.validation.other.declarations.typeParameters.varianceOnlyOnClasses

// $TEST$ no error "Only type parameters of classes can be variant."

class C1<T>

fun f1<T>()
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package tests.validation.other.declarations.typeParameters.varianceOnlyOnClasses

// $TEST$ no error "Only type parameters of classes can be variant."
class C2<»in« T>
// $TEST$ no error "Only type parameters of classes can be variant."
class C3<»out« T>

// $TEST$ error "Only type parameters of classes can be variant."
fun f2<»in« T>()
// $TEST$ error "Only type parameters of classes can be variant."
fun f3<»out« T>()

0 comments on commit 9bf5fec

Please sign in to comment.