From 60ca2f35e76710503e26398d0b994c2cfe3967aa Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Thu, 31 Oct 2024 16:46:19 +0100 Subject: [PATCH] feat: handle cells when type checking --- .../src/language/validation/types.ts | 64 ++--- .../checking/infix operations/main.sdsdev | 224 ++++++++++-------- .../checking/prefix operations/main.sdsdev | 24 +- 3 files changed, 180 insertions(+), 132 deletions(-) diff --git a/packages/safe-ds-lang/src/language/validation/types.ts b/packages/safe-ds-lang/src/language/validation/types.ts index 84b9dbd40..e9db69dca 100644 --- a/packages/safe-ds-lang/src/language/validation/types.ts +++ b/packages/safe-ds-lang/src/language/validation/types.ts @@ -161,17 +161,27 @@ export const infixOperationOperandsMustHaveCorrectType = (services: SafeDsServic return (node: SdsInfixOperation, accept: ValidationAcceptor): void => { const leftType = typeComputer.computeType(node.leftOperand); const rightType = typeComputer.computeType(node.rightOperand); + const cellType = coreTypes.Cell(); + switch (node.operator) { case 'or': case 'and': - if (node.leftOperand && !typeChecker.isSubtypeOf(leftType, coreTypes.Boolean)) { - accept('error', `Expected type '${coreTypes.Boolean}' but got '${leftType}'.`, { + if ( + node.leftOperand && + !typeChecker.isSubtypeOf(leftType, coreTypes.Boolean) && + !typeChecker.isSubtypeOf(leftType, cellType) + ) { + accept('error', `This operator is not defined for type '${leftType}'.`, { node: node.leftOperand, code: CODE_TYPE_MISMATCH, }); } - if (node.rightOperand && !typeChecker.isSubtypeOf(rightType, coreTypes.Boolean)) { - accept('error', `Expected type '${coreTypes.Boolean}' but got '${rightType}'.`, { + if ( + node.rightOperand && + !typeChecker.isSubtypeOf(rightType, coreTypes.Boolean) && + !typeChecker.isSubtypeOf(rightType, cellType) + ) { + accept('error', `This operator is not defined for type '${rightType}'.`, { node: node.rightOperand, code: CODE_TYPE_MISMATCH, }); @@ -202,9 +212,10 @@ export const infixOperationOperandsMustHaveCorrectType = (services: SafeDsServic if ( node.leftOperand && !typeChecker.isSubtypeOf(leftType, coreTypes.Float) && - !typeChecker.isSubtypeOf(leftType, coreTypes.Int) + !typeChecker.isSubtypeOf(leftType, coreTypes.Int) && + !typeChecker.isSubtypeOf(leftType, cellType) ) { - accept('error', `Expected type '${coreTypes.Float}' or '${coreTypes.Int}' but got '${leftType}'.`, { + accept('error', `This operator is not defined for type '${leftType}'.`, { node: node.leftOperand, code: CODE_TYPE_MISMATCH, }); @@ -212,16 +223,13 @@ export const infixOperationOperandsMustHaveCorrectType = (services: SafeDsServic if ( node.rightOperand && !typeChecker.isSubtypeOf(rightType, coreTypes.Float) && - !typeChecker.isSubtypeOf(rightType, coreTypes.Int) + !typeChecker.isSubtypeOf(rightType, coreTypes.Int) && + !typeChecker.isSubtypeOf(rightType, cellType) ) { - accept( - 'error', - `Expected type '${coreTypes.Float}' or '${coreTypes.Int}' but got '${rightType}'.`, - { - node: node.rightOperand, - code: CODE_TYPE_MISMATCH, - }, - ); + accept('error', `This operator is not defined for type '${rightType}'.`, { + node: node.rightOperand, + code: CODE_TYPE_MISMATCH, + }); } return; } @@ -338,10 +346,15 @@ export const prefixOperationOperandMustHaveCorrectType = (services: SafeDsServic return (node: SdsPrefixOperation, accept: ValidationAcceptor): void => { const operandType = typeComputer.computeType(node.operand); + const cellType = coreTypes.Cell(coreTypes.AnyOrNull); + switch (node.operator) { case 'not': - if (!typeChecker.isSubtypeOf(operandType, coreTypes.Boolean)) { - accept('error', `Expected type '${coreTypes.Boolean}' but got '${operandType}'.`, { + if ( + !typeChecker.isSubtypeOf(operandType, coreTypes.Boolean) && + !typeChecker.isSubtypeOf(operandType, cellType) + ) { + accept('error', `This operator is not defined for type '${operandType}'.`, { node, property: 'operand', code: CODE_TYPE_MISMATCH, @@ -351,17 +364,14 @@ export const prefixOperationOperandMustHaveCorrectType = (services: SafeDsServic case '-': if ( !typeChecker.isSubtypeOf(operandType, coreTypes.Float) && - !typeChecker.isSubtypeOf(operandType, coreTypes.Int) + !typeChecker.isSubtypeOf(operandType, coreTypes.Int) && + !typeChecker.isSubtypeOf(operandType, cellType) ) { - accept( - 'error', - `Expected type '${coreTypes.Float}' or '${coreTypes.Int}' but got '${operandType}'.`, - { - node, - property: 'operand', - code: CODE_TYPE_MISMATCH, - }, - ); + accept('error', `This operator is not defined for type '${operandType}'.`, { + node, + property: 'operand', + code: CODE_TYPE_MISMATCH, + }); } return; } diff --git a/packages/safe-ds-lang/tests/resources/validation/types/checking/infix operations/main.sdsdev b/packages/safe-ds-lang/tests/resources/validation/types/checking/infix operations/main.sdsdev index bf8587f87..4bb281ee8 100644 --- a/packages/safe-ds-lang/tests/resources/validation/types/checking/infix operations/main.sdsdev +++ b/packages/safe-ds-lang/tests/resources/validation/types/checking/infix operations/main.sdsdev @@ -1,131 +1,163 @@ package tests.validation.types.checking.infixOperations +@Pure fun cell() -> cell: Cell + pipeline myPipeline { - // $TEST$ no error r"Expected type 'Boolean' but got .*\." - // $TEST$ no error r"Expected type 'Boolean' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »true« or »true« ; - // $TEST$ error "Expected type 'Boolean' but got 'literal<0>'." - // $TEST$ error "Expected type 'Boolean' but got 'literal<0>'." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + »cell()« or »cell()«; + // $TEST$ error "This operator is not defined for type 'literal<0>'." + // $TEST$ error "This operator is not defined for type 'literal<0>'." »0« or »0«; - // $TEST$ error "Expected type 'Boolean' but got 'unknown'." - // $TEST$ error "Expected type 'Boolean' but got 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." »unresolved« or »unresolved«; - // $TEST$ no error r"Expected type 'Boolean' but got .*\." - // $TEST$ no error r"Expected type 'Boolean' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »true« and »true«; - // $TEST$ error "Expected type 'Boolean' but got 'literal<0>'." - // $TEST$ error "Expected type 'Boolean' but got 'literal<0>'." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + »cell()« and »cell()«; + // $TEST$ error "This operator is not defined for type 'literal<0>'." + // $TEST$ error "This operator is not defined for type 'literal<0>'." »0« and »0«; - // $TEST$ error "Expected type 'Boolean' but got 'unknown'." - // $TEST$ error "Expected type 'Boolean' but got 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." »unresolved« and »unresolved«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0.0« + »0.0«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0« + »0«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal'." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + »cell()« + »cell()«; + // $TEST$ error "This operator is not defined for type 'literal'." + // $TEST$ error "This operator is not defined for type 'literal'." »true« + »false«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." »unresolved« + »unresolved«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0.0« - »0.0«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0« - »0«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + »cell()« - »cell()«; + // $TEST$ error "This operator is not defined for type 'literal<"">'." + // $TEST$ error "This operator is not defined for type 'literal<"">'." »""« - »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." »unresolved« - »unresolved«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0.0« * »0.0«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0« * »0«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + »cell()« * »cell()«; + // $TEST$ error "This operator is not defined for type 'literal<"">'." + // $TEST$ error "This operator is not defined for type 'literal<"">'." »""« * »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." »unresolved« * »unresolved«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0.0« / »0.0«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0« / »0«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + »cell()« / »cell()«; + // $TEST$ error "This operator is not defined for type 'literal<"">'." + // $TEST$ error "This operator is not defined for type 'literal<"">'." »""« / »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." »unresolved« / »unresolved«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0.0« < »0.0«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0« < »0«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + »cell()« < »cell()«; + // $TEST$ error "This operator is not defined for type 'literal<"">'." + // $TEST$ error "This operator is not defined for type 'literal<"">'." »""« < »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." »unresolved« < »unresolved«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0.0« <= »0.0«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0« <= »0«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + »cell()« <= »cell()«; + // $TEST$ error "This operator is not defined for type 'literal<"">'." + // $TEST$ error "This operator is not defined for type 'literal<"">'." »""« <= »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." »unresolved« <= »unresolved«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0.0« >= »0.0«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0« >= »0«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + »cell()« >= »cell()«; + // $TEST$ error "This operator is not defined for type 'literal<"">'." + // $TEST$ error "This operator is not defined for type 'literal<"">'." »""« >= »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." »unresolved« >= »unresolved«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0.0« > »0.0«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." »0« > »0«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." + // $TEST$ no error r"This operator is not defined for type .*\." + // $TEST$ no error r"This operator is not defined for type .*\." + »cell()« > »cell()«; + // $TEST$ error "This operator is not defined for type 'literal<"">'." + // $TEST$ error "This operator is not defined for type 'literal<"">'." »""« > »""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." »unresolved« > »unresolved«; } @@ -133,37 +165,37 @@ pipeline myPipeline { class MyClass( p1: T, - // $TEST$ error "Expected type 'Boolean' but got 'T'." - // $TEST$ error "Expected type 'Boolean' but got 'T'." + // $TEST$ error "This operator is not defined for type 'T'." + // $TEST$ error "This operator is not defined for type 'T'." a1: Any? = »p1« or »p1«, - // $TEST$ error "Expected type 'Boolean' but got 'T'." - // $TEST$ error "Expected type 'Boolean' but got 'T'." + // $TEST$ error "This operator is not defined for type 'T'." + // $TEST$ error "This operator is not defined for type 'T'." a2: Any? = »p1« and »p1«, - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." + // $TEST$ error "This operator is not defined for type 'T'." + // $TEST$ error "This operator is not defined for type 'T'." b1: Any? = »p1« + »p1«, - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." + // $TEST$ error "This operator is not defined for type 'T'." + // $TEST$ error "This operator is not defined for type 'T'." b2: Any? = »p1« - »p1«, - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." + // $TEST$ error "This operator is not defined for type 'T'." + // $TEST$ error "This operator is not defined for type 'T'." b3: Any? = »p1« * »p1«, - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." + // $TEST$ error "This operator is not defined for type 'T'." + // $TEST$ error "This operator is not defined for type 'T'." b4: Any? = »p1« / »p1«, - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." + // $TEST$ error "This operator is not defined for type 'T'." + // $TEST$ error "This operator is not defined for type 'T'." c1: Any? = »p1« < »p1«, - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." + // $TEST$ error "This operator is not defined for type 'T'." + // $TEST$ error "This operator is not defined for type 'T'." c2: Any? = »p1« <= »p1«, - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." + // $TEST$ error "This operator is not defined for type 'T'." + // $TEST$ error "This operator is not defined for type 'T'." c3: Any? = »p1« >= »p1«, - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." + // $TEST$ error "This operator is not defined for type 'T'." + // $TEST$ error "This operator is not defined for type 'T'." c4: Any? = »p1« > »p1«, ) diff --git a/packages/safe-ds-lang/tests/resources/validation/types/checking/prefix operations/main.sdsdev b/packages/safe-ds-lang/tests/resources/validation/types/checking/prefix operations/main.sdsdev index 9daaccdcb..7d029ac9a 100644 --- a/packages/safe-ds-lang/tests/resources/validation/types/checking/prefix operations/main.sdsdev +++ b/packages/safe-ds-lang/tests/resources/validation/types/checking/prefix operations/main.sdsdev @@ -1,21 +1,27 @@ package tests.validation.types.checking.prefixOperations +@Pure fun cell() -> cell: Cell + segment mySegment() { - // $TEST$ no error r"Expected type 'Boolean' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." not »true«; - // $TEST$ error "Expected type 'Boolean' but got 'literal<0>'." + // $TEST$ no error r"This operator is not defined for type .*\." + not »cell()«; + // $TEST$ error "This operator is not defined for type 'literal<0>'." not »0«; - // $TEST$ error "Expected type 'Boolean' but got 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." not »unresolved«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." -»0.0«; - // $TEST$ no error r"Expected type 'Float' or 'Int' but got .*\." + // $TEST$ no error r"This operator is not defined for type .*\." -»0«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'literal<"">'." + // $TEST$ no error r"This operator is not defined for type .*\." + -»cell()«; + // $TEST$ error "This operator is not defined for type 'literal<"">'." -»""«; - // $TEST$ error "Expected type 'Float' or 'Int' but got 'unknown'." + // $TEST$ error "This operator is not defined for type 'unknown'." -»unresolved«; } @@ -23,9 +29,9 @@ segment mySegment() { class MyClass( p1: T, - // $TEST$ error "Expected type 'Boolean' but got 'T'." + // $TEST$ error "This operator is not defined for type 'T'." a: Any? = not »p1«, - // $TEST$ error "Expected type 'Float' or 'Int' but got 'T'." + // $TEST$ error "This operator is not defined for type 'T'." b: Any? = -»p1«, )