From 8d68a42732fe8bce43b9d29d9e561a8c70906c7f Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Thu, 5 Oct 2023 11:15:31 +0200 Subject: [PATCH] feat: intermediate type computer (#600) Closes partially #541. ### Summary of Changes Port almost the entire functionality of the Xtext type computer to Langium. Only the following tasks are still missing for feature parity, but they require work in other areas of the language first: * Elvis operator with nullable left side (**needs type checker**) * Parameters of block lambdas passed as arguments (**needs scoping**) * Parameters of expression lambdas passed as arguments (**needs scoping**) --- src/language/formatting/safe-ds-formatter.ts | 6 +- src/language/grammar/safe-ds.langium | 172 ++-- src/language/helpers/checks.ts | 4 +- src/language/typing/model.ts | 394 ++++++--- src/language/typing/safe-ds-type-computer.ts | 766 ++++++++++-------- src/language/typing/typeConformance.ts | 141 ++++ .../other/declarations/parameterLists.ts | 6 +- .../other/declarations/parameters.ts | 2 +- .../validation/other/types/callableTypes.ts | 2 +- tests/helpers/location.ts | 4 +- .../block lambda results/main.sdstest | 23 + .../assignees/placeholders/main.sdstest | 21 + .../typing/assignees/yields/main.sdstest | 21 + .../declarations/annotations/main.sdstest | 10 + .../declarations/attributes/main.sdstest | 17 + .../typing/declarations/classes/main.sdstest | 7 + .../declarations/enum variants/main.sdstest | 9 + .../typing/declarations/enums/main.sdstest | 7 + .../declarations/functions/main.sdstest | 16 + .../parameters/of annotations/main.sdstest | 11 + .../skip-that are passed as argument.sdstest | 26 + .../that are isolated.sdstest | 6 + .../of block lambdas/that are yielded.sdstest | 17 + .../with manifest types.sdstest | 10 + .../parameters/of callable types/main.sdstest | 11 + .../parameters/of classes/main.sdstest | 11 + .../parameters/of enum variants/main.sdstest | 13 + .../skip-that are passed as argument.sdstest | 26 + .../that are isolated.sdstest | 6 + .../that are yielded.sdstest | 17 + .../with manifest types.sdstest | 10 + .../parameters/of functions/main.sdstest | 11 + .../parameters/of segments/main.sdstest | 11 + .../declarations/pipelines/main.sdstest | 4 + .../typing/declarations/results/main.sdstest | 14 + .../typing/declarations/segments/main.sdstest | 16 + .../typing/expressions/arguments/main.sdstest | 40 + .../skip-that are passed as argument.sdstest | 40 + .../block lambdas/that are isolated.sdstest | 13 + .../block lambdas/that are yielded.sdstest | 24 + .../block lambdas/with manifest types.sdstest | 14 + .../calls/of annotations/main.sdstest | 8 + .../calls/of block lambdas/main.sdstest | 14 + .../calls/of callable types/main.sdstest | 12 + .../expressions/calls/of classes/main.sdstest | 11 + .../calls/of enum variants/main.sdstest | 13 + .../calls/of expression lambdas/main.sdstest | 6 + .../calls/of functions/main.sdstest | 12 + .../calls/of non-callable/main.sdstest | 8 + .../calls/of segments/main.sdstest | 17 + .../expressions/calls/unresolved/main.sdstest | 6 + .../skip-that are passed as argument.sdstest | 25 + .../that are isolated.sdstest | 9 + .../that are yielded.sdstest | 16 + .../with manifest types.sdstest | 9 + .../expressions/indexed accesses/main.sdstest | 17 + .../to enum variants/main.sdstest | 10 + .../to nested classes/main.sdstest | 10 + .../to nested enums/main.sdstest | 10 + .../member accesses/to other/main.sdstest | 21 + .../member accesses/unresolved/main.sdstest | 11 + .../operations/arithmetic/main.sdstest | 36 + .../operations/comparison/main.sdstest | 21 + .../elvis/non nullable left operand.sdstest | 18 + .../elvis/skip-nullable left operand.sdstest | 19 + .../operations/equality/main.sdstest | 13 + .../operations/logical/main.sdstest | 17 + .../parenthesized expressions/main.sdstest | 23 + .../references/to global classes/main.sdstest | 8 + .../references/to global enums/main.sdstest | 8 + .../references/to other/main.sdstest | 13 + .../references/unresolved/main.sdstest | 6 + .../skip-assignees/blockLambdaResults.sdstest | 10 - .../skip-assignees/placeholders.sdstest | 8 - .../typing/skip-assignees/yields.sdstest | 8 - .../skip-declarations/attributes.sdstest | 5 - .../typing/skip-declarations/classes.sdstest | 3 - .../skip-declarations/enumVariants.sdstest | 7 - .../typing/skip-declarations/enums.sdstest | 5 - .../skip-declarations/functions.sdstest | 3 - .../skip-declarations/parameters.sdstest | 17 - .../typing/skip-declarations/results.sdstest | 6 - .../typing/skip-declarations/steps.sdstest | 6 - .../typing/skip-expressions/arguments.sdstest | 8 - .../skip-expressions/blockLambdas.sdstest | 49 -- .../typing/skip-expressions/calls.sdstest | 37 - .../expressionLambdas.sdstest | 27 - .../skip-expressions/indexedAccesses.sdstest | 17 - .../skip-expressions/memberAccesses.sdstest | 19 - .../operations/arithmetic.sdstest | 21 - .../operations/comparison.sdstest | 12 - .../skip-expressions/operations/elvis.sdstest | 21 - .../operations/equality.sdstest | 6 - .../operations/logical.sdstest | 10 - .../operations/strictEquality.sdstest | 6 - .../parenthesizedExpressions.sdstest | 6 - .../skip-expressions/references.sdstest | 10 - .../typing/skip-types/callableTypes.sdstest | 3 - .../typing/skip-types/memberTypes.sdstest | 9 - .../typing/skip-types/namedTypes.sdstest | 4 - .../skip-types/parenthesizedTypes.sdstest | 3 - .../typing/skip-types/unionTypes.sdstest | 3 - .../typing/types/callable types/main.sdstest | 16 + .../typing/types/member types/main.sdstest | 36 + .../typing/types/named types/main.sdstest | 23 + .../typing/types/union types/main.sdstest | 7 + 106 files changed, 1924 insertions(+), 913 deletions(-) create mode 100644 src/language/typing/typeConformance.ts create mode 100644 tests/resources/typing/assignees/block lambda results/main.sdstest create mode 100644 tests/resources/typing/assignees/placeholders/main.sdstest create mode 100644 tests/resources/typing/assignees/yields/main.sdstest create mode 100644 tests/resources/typing/declarations/annotations/main.sdstest create mode 100644 tests/resources/typing/declarations/attributes/main.sdstest create mode 100644 tests/resources/typing/declarations/classes/main.sdstest create mode 100644 tests/resources/typing/declarations/enum variants/main.sdstest create mode 100644 tests/resources/typing/declarations/enums/main.sdstest create mode 100644 tests/resources/typing/declarations/functions/main.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of annotations/main.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of block lambdas/skip-that are passed as argument.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of block lambdas/that are isolated.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of block lambdas/that are yielded.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of block lambdas/with manifest types.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of callable types/main.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of classes/main.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of enum variants/main.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of expression lambdas/skip-that are passed as argument.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of expression lambdas/that are isolated.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of expression lambdas/that are yielded.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of expression lambdas/with manifest types.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of functions/main.sdstest create mode 100644 tests/resources/typing/declarations/parameters/of segments/main.sdstest create mode 100644 tests/resources/typing/declarations/pipelines/main.sdstest create mode 100644 tests/resources/typing/declarations/results/main.sdstest create mode 100644 tests/resources/typing/declarations/segments/main.sdstest create mode 100644 tests/resources/typing/expressions/arguments/main.sdstest create mode 100644 tests/resources/typing/expressions/block lambdas/skip-that are passed as argument.sdstest create mode 100644 tests/resources/typing/expressions/block lambdas/that are isolated.sdstest create mode 100644 tests/resources/typing/expressions/block lambdas/that are yielded.sdstest create mode 100644 tests/resources/typing/expressions/block lambdas/with manifest types.sdstest create mode 100644 tests/resources/typing/expressions/calls/of annotations/main.sdstest create mode 100644 tests/resources/typing/expressions/calls/of block lambdas/main.sdstest create mode 100644 tests/resources/typing/expressions/calls/of callable types/main.sdstest create mode 100644 tests/resources/typing/expressions/calls/of classes/main.sdstest create mode 100644 tests/resources/typing/expressions/calls/of enum variants/main.sdstest create mode 100644 tests/resources/typing/expressions/calls/of expression lambdas/main.sdstest create mode 100644 tests/resources/typing/expressions/calls/of functions/main.sdstest create mode 100644 tests/resources/typing/expressions/calls/of non-callable/main.sdstest create mode 100644 tests/resources/typing/expressions/calls/of segments/main.sdstest create mode 100644 tests/resources/typing/expressions/calls/unresolved/main.sdstest create mode 100644 tests/resources/typing/expressions/expression lambdas/skip-that are passed as argument.sdstest create mode 100644 tests/resources/typing/expressions/expression lambdas/that are isolated.sdstest create mode 100644 tests/resources/typing/expressions/expression lambdas/that are yielded.sdstest create mode 100644 tests/resources/typing/expressions/expression lambdas/with manifest types.sdstest create mode 100644 tests/resources/typing/expressions/indexed accesses/main.sdstest create mode 100644 tests/resources/typing/expressions/member accesses/to enum variants/main.sdstest create mode 100644 tests/resources/typing/expressions/member accesses/to nested classes/main.sdstest create mode 100644 tests/resources/typing/expressions/member accesses/to nested enums/main.sdstest create mode 100644 tests/resources/typing/expressions/member accesses/to other/main.sdstest create mode 100644 tests/resources/typing/expressions/member accesses/unresolved/main.sdstest create mode 100644 tests/resources/typing/expressions/operations/arithmetic/main.sdstest create mode 100644 tests/resources/typing/expressions/operations/comparison/main.sdstest create mode 100644 tests/resources/typing/expressions/operations/elvis/non nullable left operand.sdstest create mode 100644 tests/resources/typing/expressions/operations/elvis/skip-nullable left operand.sdstest create mode 100644 tests/resources/typing/expressions/operations/equality/main.sdstest create mode 100644 tests/resources/typing/expressions/operations/logical/main.sdstest create mode 100644 tests/resources/typing/expressions/parenthesized expressions/main.sdstest create mode 100644 tests/resources/typing/expressions/references/to global classes/main.sdstest create mode 100644 tests/resources/typing/expressions/references/to global enums/main.sdstest create mode 100644 tests/resources/typing/expressions/references/to other/main.sdstest create mode 100644 tests/resources/typing/expressions/references/unresolved/main.sdstest delete mode 100644 tests/resources/typing/skip-assignees/blockLambdaResults.sdstest delete mode 100644 tests/resources/typing/skip-assignees/placeholders.sdstest delete mode 100644 tests/resources/typing/skip-assignees/yields.sdstest delete mode 100644 tests/resources/typing/skip-declarations/attributes.sdstest delete mode 100644 tests/resources/typing/skip-declarations/classes.sdstest delete mode 100644 tests/resources/typing/skip-declarations/enumVariants.sdstest delete mode 100644 tests/resources/typing/skip-declarations/enums.sdstest delete mode 100644 tests/resources/typing/skip-declarations/functions.sdstest delete mode 100644 tests/resources/typing/skip-declarations/parameters.sdstest delete mode 100644 tests/resources/typing/skip-declarations/results.sdstest delete mode 100644 tests/resources/typing/skip-declarations/steps.sdstest delete mode 100644 tests/resources/typing/skip-expressions/arguments.sdstest delete mode 100644 tests/resources/typing/skip-expressions/blockLambdas.sdstest delete mode 100644 tests/resources/typing/skip-expressions/calls.sdstest delete mode 100644 tests/resources/typing/skip-expressions/expressionLambdas.sdstest delete mode 100644 tests/resources/typing/skip-expressions/indexedAccesses.sdstest delete mode 100644 tests/resources/typing/skip-expressions/memberAccesses.sdstest delete mode 100644 tests/resources/typing/skip-expressions/operations/arithmetic.sdstest delete mode 100644 tests/resources/typing/skip-expressions/operations/comparison.sdstest delete mode 100644 tests/resources/typing/skip-expressions/operations/elvis.sdstest delete mode 100644 tests/resources/typing/skip-expressions/operations/equality.sdstest delete mode 100644 tests/resources/typing/skip-expressions/operations/logical.sdstest delete mode 100644 tests/resources/typing/skip-expressions/operations/strictEquality.sdstest delete mode 100644 tests/resources/typing/skip-expressions/parenthesizedExpressions.sdstest delete mode 100644 tests/resources/typing/skip-expressions/references.sdstest delete mode 100644 tests/resources/typing/skip-types/callableTypes.sdstest delete mode 100644 tests/resources/typing/skip-types/memberTypes.sdstest delete mode 100644 tests/resources/typing/skip-types/namedTypes.sdstest delete mode 100644 tests/resources/typing/skip-types/parenthesizedTypes.sdstest delete mode 100644 tests/resources/typing/skip-types/unionTypes.sdstest create mode 100644 tests/resources/typing/types/callable types/main.sdstest create mode 100644 tests/resources/typing/types/member types/main.sdstest create mode 100644 tests/resources/typing/types/named types/main.sdstest create mode 100644 tests/resources/typing/types/union types/main.sdstest diff --git a/src/language/formatting/safe-ds-formatter.ts b/src/language/formatting/safe-ds-formatter.ts index 85b24662b..77e60c6c3 100644 --- a/src/language/formatting/safe-ds-formatter.ts +++ b/src/language/formatting/safe-ds-formatter.ts @@ -313,7 +313,7 @@ export class SafeDsFormatter extends AbstractFormatter { const formatter = this.getNodeFormatter(node); if (annotationCallsOrEmpty(node).length > 0) { - if (node.static) { + if (node.isStatic) { formatter.keyword('static').prepend(newLine()); } else { formatter.keyword('attr').prepend(newLine()); @@ -421,7 +421,7 @@ export class SafeDsFormatter extends AbstractFormatter { const formatter = this.getNodeFormatter(node); if (annotationCallsOrEmpty(node).length > 0) { - if (node.static) { + if (node.isStatic) { formatter.keyword('static').prepend(newLine()); } else { formatter.keyword('fun').prepend(newLine()); @@ -550,7 +550,7 @@ export class SafeDsFormatter extends AbstractFormatter { const formatter = this.getNodeFormatter(node); if (annotationCallsOrEmpty(node).length === 0) { - if (node.variadic) { + if (node.isVariadic) { formatter.property('name').prepend(oneSpace()); } } else { diff --git a/src/language/grammar/safe-ds.langium b/src/language/grammar/safe-ds.langium index 0d8e74438..6c7fa01e9 100644 --- a/src/language/grammar/safe-ds.langium +++ b/src/language/grammar/safe-ds.langium @@ -11,12 +11,12 @@ interface SdsAnnotatedObject extends SdsObject { } interface SdsAbstractCall extends SdsObject { - argumentList: SdsArgumentList + argumentList: SdsArgumentList } interface SdsDeclaration extends SdsAnnotatedObject { annotationCallList?: SdsAnnotationCallList - name: string + name: string } interface SdsLocalVariable extends SdsDeclaration {} @@ -156,7 +156,7 @@ SdsUnannotatedModuleMember returns SdsModuleMember: ; interface SdsAnnotation extends SdsCallable, SdsModuleMember { - constraintList?: SdsConstraintList + constraintList?: SdsConstraintList } fragment SdsAnnotationFragment: @@ -166,10 +166,10 @@ fragment SdsAnnotationFragment: ; interface SdsClass extends SdsCallable, SdsClassMember, SdsModuleMember, SdsNamedTypeDeclaration { - typeParameterList?: SdsTypeParameterList - parentTypeList?: SdsParentTypeList + typeParameterList?: SdsTypeParameterList + parentTypeList?: SdsParentTypeList constraintList?: SdsConstraintList - body?: SdsClassBody + body?: SdsClassBody } fragment SdsClassFragment: @@ -183,7 +183,7 @@ fragment SdsClassFragment: ; interface SdsParentTypeList extends SdsObject { - parentTypes: SdsType[] + parentTypes: SdsType[] } SdsParentTypeList returns SdsParentTypeList: @@ -194,7 +194,7 @@ SdsParentTypeList returns SdsParentTypeList: ; interface SdsClassBody extends SdsObject { - members: SdsClassMember[] + members: SdsClassMember[] } SdsClassBody returns SdsClassBody: @@ -222,7 +222,7 @@ SdsAnnotatedClassMember returns SdsClassMember: SdsEnumFragment | {SdsFunction.annotationCallList=current} - static?='static'? + isStatic?='static'? SdsFunctionFragment ) ; @@ -238,24 +238,24 @@ SdsUnannotatedClassMember returns SdsClassMember: SdsEnumFragment | {SdsFunction} - static?='static'? + isStatic?='static'? SdsFunctionFragment ; interface SdsAttribute extends SdsClassMember { - static: boolean - ^type?: SdsType + isStatic: boolean + ^type?: SdsType } fragment SdsAttributeFragment: - static?='static'? + isStatic?='static'? 'attr' name=ID (':' ^type=SdsType)? ; interface SdsEnum extends SdsNamedTypeDeclaration, SdsClassMember, SdsModuleMember { - body?: SdsEnumBody + body?: SdsEnumBody } fragment SdsEnumFragment: @@ -265,7 +265,7 @@ fragment SdsEnumFragment: ; interface SdsEnumBody extends SdsObject { - variants: SdsEnumVariant[] + variants: SdsEnumVariant[] } SdsEnumBody returns SdsEnumBody: @@ -273,8 +273,8 @@ SdsEnumBody returns SdsEnumBody: ; interface SdsEnumVariant extends SdsCallable, SdsNamedTypeDeclaration { - typeParameterList?: SdsTypeParameterList - constraintList?: SdsConstraintList + typeParameterList?: SdsTypeParameterList + constraintList?: SdsConstraintList } SdsEnumVariant returns SdsEnumVariant: @@ -286,10 +286,10 @@ SdsEnumVariant returns SdsEnumVariant: ; interface SdsFunction extends SdsCallable, SdsClassMember, SdsModuleMember { - static: boolean - typeParameterList?: SdsTypeParameterList - resultList?: SdsResultList - constraintList?: SdsConstraintList + isStatic: boolean + typeParameterList?: SdsTypeParameterList + resultList?: SdsResultList + constraintList?: SdsConstraintList } fragment SdsFunctionFragment: @@ -302,7 +302,7 @@ fragment SdsFunctionFragment: ; interface SdsPipeline extends SdsModuleMember { - body: SdsBlock + body: SdsBlock } fragment SdsPipelineFragment: @@ -312,9 +312,9 @@ fragment SdsPipelineFragment: ; interface SdsSegment extends SdsCallable, SdsModuleMember { - visibility?: string - resultList?: SdsResultList - body: SdsBlock + visibility?: string + resultList?: SdsResultList + body: SdsBlock } fragment SdsSegmentFragment: @@ -334,7 +334,7 @@ fragment SdsSegmentFragment: interface SdsAnnotationCallList extends SdsAnnotatedObject {} interface SdsAnnotationCall extends SdsAbstractCall { - annotation?: @SdsAnnotation + annotation?: @SdsAnnotation } SdsAnnotationCall returns SdsAnnotationCall: @@ -355,7 +355,7 @@ SdsAnnotationCallArgument returns SdsArgument: // ----------------------------------------------------------------------------- interface SdsConstraintList extends SdsObject { - constraints: SdsConstraint[] + constraints: SdsConstraint[] } SdsConstraintList returns SdsConstraintList: @@ -376,9 +376,9 @@ SdsConstraint returns SdsConstraint: ; interface SdsTypeParameterConstraint extends SdsConstraint { - leftOperand: @SdsTypeParameter - operator: string - rightOperand: SdsType + leftOperand: @SdsTypeParameter + operator: string + rightOperand: SdsType } SdsTypeParameterConstraint returns SdsTypeParameterConstraint: @@ -397,11 +397,11 @@ SdsTypeParameterConstraintOperator returns string: // ----------------------------------------------------------------------------- interface SdsCallable extends SdsObject { - parameterList?: SdsParameterList + parameterList?: SdsParameterList } interface SdsParameterList extends SdsObject { - parameters: SdsParameter[] + parameters: SdsParameter[] } SdsParameterList returns SdsParameterList: @@ -416,21 +416,21 @@ SdsParameterList returns SdsParameterList: ; interface SdsParameter extends SdsLocalVariable { - variadic: boolean - ^type?: SdsType - defaultValue?: SdsExpression + isVariadic: boolean + ^type?: SdsType + defaultValue?: SdsExpression } SdsParameter returns SdsParameter: annotationCalls+=SdsAnnotationCall* - variadic?='vararg'? + isVariadic?='vararg'? name=ID (':' ^type=SdsType)? ('=' defaultValue=SdsExpression)? ; interface SdsResultList extends SdsObject { - results: SdsResult[] + results: SdsResult[] } SdsResultList returns SdsResultList: @@ -441,7 +441,7 @@ SdsResultList returns SdsResultList: interface SdsAbstractResult extends SdsDeclaration {} interface SdsResult extends SdsAbstractResult { - ^type?: SdsType + ^type?: SdsType } SdsResult returns SdsResult: @@ -458,7 +458,7 @@ SdsResult returns SdsResult: interface SdsStatement extends SdsObject {} interface SdsBlock extends SdsObject { - statements: SdsStatement[] + statements: SdsStatement[] } SdsBlock returns SdsBlock: @@ -471,8 +471,8 @@ SdsStatement returns SdsStatement: ; interface SdsAssignment extends SdsStatement { - assigneeList?: SdsAssigneeList - expression?: SdsExpression + assigneeList?: SdsAssigneeList + expression?: SdsExpression } SdsAssignment returns SdsAssignment: @@ -480,7 +480,7 @@ SdsAssignment returns SdsAssignment: ; interface SdsAssigneeList extends SdsObject { - assignees: SdsAssignee[] + assignees: SdsAssignee[] } SdsAssigneeList returns SdsAssigneeList: @@ -504,7 +504,7 @@ SdsAssignee returns SdsAssignee: ; interface SdsExpressionStatement extends SdsStatement { - expression: SdsExpression + expression: SdsExpression } SdsExpressionStatement returns SdsExpressionStatement: @@ -525,11 +525,11 @@ SdsExpression returns SdsExpression: interface SdsLambda extends SdsCallable, SdsExpression {} interface SdsBlockLambda extends SdsLambda { - body: SdsBlock + body: SdsBlock } interface SdsExpressionLambda extends SdsLambda { - result: SdsExpression + result: SdsExpression } SdsLambda returns SdsExpression: @@ -565,14 +565,14 @@ SdsBlockLambdaAssignee returns SdsAssignee: ; interface SdsInfixOperation extends SdsExpression { - leftOperand: SdsExpression - operator: string - rightOperand: SdsExpression + leftOperand: SdsExpression + operator: string + rightOperand: SdsExpression } interface SdsPrefixOperation extends SdsExpression { - operand: SdsExpression - operator: string + operand: SdsExpression + operator: string } SdsOrExpression returns SdsExpression: @@ -665,18 +665,18 @@ SdsUnaryOperation returns SdsExpression: ; interface SdsChainedExpression extends SdsExpression { - receiver: SdsExpression + receiver: SdsExpression } interface SdsCall extends SdsAbstractCall, SdsChainedExpression {} interface SdsIndexedAccess extends SdsChainedExpression { - index: SdsExpression + index: SdsExpression } interface SdsMemberAccess extends SdsChainedExpression { - nullSafe: boolean - member: SdsReference + isNullSafe: boolean + member: SdsReference } SdsChainedExpression returns SdsExpression: @@ -689,14 +689,14 @@ SdsChainedExpression returns SdsExpression: '[' index=SdsExpression ']' | {SdsMemberAccess.receiver=current} - (nullSafe?='?')? + (isNullSafe?='?')? '.' - member=SdsReference* + member=SdsReference )* ; interface SdsArgumentList extends SdsObject { - arguments: SdsArgument[] + arguments: SdsArgument[] } SdsCallArgumentList returns SdsArgumentList: @@ -711,8 +711,8 @@ SdsCallArgumentList returns SdsArgumentList: ; interface SdsArgument extends SdsExpression { - parameter?: @SdsParameter - value: SdsExpression + parameter?: @SdsParameter + value: SdsExpression } SdsCallArgument returns SdsArgument: @@ -737,7 +737,7 @@ SdsLiteral returns SdsLiteral: ; interface SdsBoolean extends SdsLiteral { - value: boolean + value: boolean } SdsBoolean returns SdsBoolean: @@ -748,7 +748,7 @@ SdsBoolean returns SdsBoolean: interface SdsNumber extends SdsLiteral {} interface SdsFloat extends SdsNumber { - value: number + value: number } SdsFloat returns SdsFloat: @@ -756,7 +756,7 @@ SdsFloat returns SdsFloat: ; interface SdsInt extends SdsNumber { - value: number + value: number } SdsInt returns SdsInt: @@ -770,7 +770,7 @@ SdsNull returns SdsNull: ; interface SdsString extends SdsLiteral { - value: string + value: string } SdsString returns SdsString: @@ -778,7 +778,7 @@ SdsString returns SdsString: ; interface SdsReference extends SdsExpression { - target: @SdsDeclaration + target: @SdsDeclaration } SdsReference returns SdsReference: @@ -786,7 +786,7 @@ SdsReference returns SdsReference: ; interface SdsParenthesizedExpression extends SdsExpression { - expression: SdsExpression + expression: SdsExpression } SdsParenthesizedExpression returns SdsParenthesizedExpression: @@ -794,7 +794,7 @@ SdsParenthesizedExpression returns SdsParenthesizedExpression: ; interface SdsTemplateString extends SdsExpression { - expressions: SdsExpression[] + expressions: SdsExpression[] } SdsTemplateString returns SdsTemplateString: @@ -805,7 +805,7 @@ SdsTemplateString returns SdsTemplateString: ; interface SdsTemplateStringPart extends SdsLiteral { - value: string + value: string } interface SdsTemplateStringStart extends SdsTemplateStringPart {} @@ -839,8 +839,8 @@ interface SdsType extends SdsObject {} interface SdsNamedTypeDeclaration extends SdsDeclaration {} interface SdsMemberType extends SdsType { - member: SdsNamedType - receiver: SdsType + member: SdsNamedType + receiver: SdsType } SdsType returns SdsType: @@ -855,7 +855,7 @@ SdsPrimaryType returns SdsType: ; interface SdsCallableType extends SdsCallable, SdsType { - resultList: SdsResultList + resultList: SdsResultList } SdsCallableType returns SdsCallableType: @@ -864,7 +864,7 @@ SdsCallableType returns SdsCallableType: ; interface SdsLiteralType extends SdsType { - literalList: SdsLiteralList + literalList: SdsLiteralList } SdsLiteralType returns SdsLiteralType: @@ -887,19 +887,19 @@ SdsLiteralList returns SdsLiteralList: ; interface SdsNamedType extends SdsType { - declaration: @SdsNamedTypeDeclaration - typeArgumentList?: SdsTypeArgumentList - nullable: boolean + declaration: @SdsNamedTypeDeclaration + typeArgumentList?: SdsTypeArgumentList + isNullable: boolean } SdsNamedType returns SdsNamedType: declaration=[SdsNamedTypeDeclaration:ID] typeArgumentList=SdsTypeArgumentList? - (nullable?='?' )? + (isNullable?='?' )? ; interface SdsUnionType extends SdsType { - typeArgumentList: SdsTypeArgumentList + typeArgumentList: SdsTypeArgumentList } SdsUnionType returns SdsUnionType: @@ -930,7 +930,7 @@ SdsParentType returns SdsType: ; interface SdsTypeParameterList extends SdsObject { - typeParameters: SdsTypeParameter[] + typeParameters: SdsTypeParameter[] } SdsTypeParameterList returns SdsTypeParameterList: @@ -945,7 +945,7 @@ SdsTypeParameterList returns SdsTypeParameterList: ; interface SdsTypeParameter extends SdsNamedTypeDeclaration { - variance?: string + variance?: string } SdsTypeParameter returns SdsTypeParameter: @@ -959,7 +959,7 @@ SdsTypeParameterVariance returns string: ; interface SdsTypeArgumentList extends SdsObject { - typeArguments: SdsTypeArgument[] + typeArguments: SdsTypeArgument[] } SdsTypeArgumentList returns SdsTypeArgumentList: @@ -970,8 +970,8 @@ SdsTypeArgumentList returns SdsTypeArgumentList: ; interface SdsTypeArgument extends SdsObject { - typeParameter?: @SdsTypeParameter - value: SdsTypeArgumentValue + typeParameter?: @SdsTypeParameter + value: SdsTypeArgumentValue } SdsTypeArgument returns SdsTypeArgument: @@ -982,7 +982,7 @@ SdsTypeArgument returns SdsTypeArgument: interface SdsTypeArgumentValue extends SdsObject {} interface SdsTypeProjection extends SdsTypeArgumentValue { - ^type: SdsType + ^type: SdsType } SdsTypeArgumentValue returns SdsTypeArgumentValue: @@ -995,7 +995,7 @@ SdsTypeArgumentValue returns SdsTypeArgumentValue: // ----------------------------------------------------------------------------- interface SdsSchema extends SdsModuleMember { - columnList: SdsColumnList + columnList: SdsColumnList } fragment SdsSchemaFragment: @@ -1005,7 +1005,7 @@ fragment SdsSchemaFragment: ; interface SdsColumnList extends SdsObject { - columns: SdsColumn[] + columns: SdsColumn[] } SdsColumnList returns SdsColumnList: @@ -1013,8 +1013,8 @@ SdsColumnList returns SdsColumnList: ; interface SdsColumn extends SdsObject { - columnName: SdsString - columnType: SdsType + columnName: SdsString + columnType: SdsType } SdsColumn returns SdsColumn: diff --git a/src/language/helpers/checks.ts b/src/language/helpers/checks.ts index f1fef942c..f9fe22b4d 100644 --- a/src/language/helpers/checks.ts +++ b/src/language/helpers/checks.ts @@ -16,9 +16,9 @@ export const isStatic = (node: SdsClassMember): boolean => { if (isSdsClass(node) || isSdsEnum(node)) { return true; } else if (isSdsAttribute(node)) { - return node.static; + return node.isStatic; } else if (isSdsFunction(node)) { - return node.static; + return node.isStatic; } else { /* c8 ignore next 2 */ return false; diff --git a/src/language/typing/model.ts b/src/language/typing/model.ts index 61b092432..d791bdba7 100644 --- a/src/language/typing/model.ts +++ b/src/language/typing/model.ts @@ -1,14 +1,12 @@ -// package com.larsreimann.safeds.staticAnalysis.typing -// -// import com.larsreimann.safeds.emf.containingEnumOrNull -// import com.larsreimann.safeds.naming.qualifiedNameOrNull -// import com.larsreimann.safeds.safeDS.SdsAbstractDeclaration -// import com.larsreimann.safeds.safeDS.SdsClass -// import com.larsreimann.safeds.safeDS.SdsEnum -// import com.larsreimann.safeds.safeDS.SdsEnumVariant -// import org.eclipse.xtext.naming.QualifiedName - -import { SdsClass, SdsDeclaration, SdsEnum, SdsEnumVariant } from '../generated/ast.js'; +import { + isSdsNull, + SdsCallable, + SdsClass, + SdsDeclaration, + SdsEnum, + SdsEnumVariant, + SdsLiteral, +} from '../generated/ast.js'; /* c8 ignore start */ export abstract class Type { @@ -21,11 +19,149 @@ export abstract class Type { abstract toString(): string; } +export class CallableType extends Type { + override isNullable: boolean = false; + + constructor( + readonly callable: SdsCallable, + readonly inputType: NamedTupleType, + readonly outputType: NamedTupleType, + ) { + super(); + } + + getParameterTypeByPosition(position: number): Type | undefined { + return this.inputType.getTypeOfEntryByPosition(position); + } + + override copyWithNullability(_isNullable: boolean): CallableType { + return this; + } + + override equals(other: Type): boolean { + if (other === this) { + return true; + } + + if (!(other instanceof CallableType)) { + return false; + } + + return ( + other.callable === this.callable && + other.inputType.equals(this.inputType) && + other.outputType.equals(this.outputType) + ); + } + + override toString(): string { + return `${this.inputType} -> ${this.outputType}`; + } +} + +export class LiteralType extends Type { + override readonly isNullable: boolean; + + constructor(readonly values: SdsLiteral[]) { + super(); + + this.isNullable = values.some(isSdsNull); + } + + override copyWithNullability(isNullable: boolean): LiteralType { + if (isNullable && !this.isNullable) { + throw Error('Not implemented'); + } else if (!isNullable && this.isNullable) { + throw Error('Not implemented'); + } else { + return this; + } + } + + override equals(other: Type): boolean { + if (other === this) { + return true; + } + + if (!(other instanceof LiteralType)) { + return false; + } + + throw Error('Not implemented'); + } + + override toString(): string { + throw Error('Not implemented'); + } +} + +export class NamedTupleType extends Type { + override readonly isNullable = false; + + constructor(readonly entries: NamedTupleEntry[]) { + super(); + } + + getTypeOfEntryByPosition(position: number): Type | undefined { + return this.entries[position]?.type; + } + + override copyWithNullability(_isNullable: boolean): NamedTupleType { + return this; + } + + override equals(other: Type): boolean { + if (other === this) { + return true; + } + + if (!(other instanceof NamedTupleType)) { + return false; + } + + if (other.entries.length !== this.entries.length) { + return false; + } + + for (let i = 0; i < this.entries.length; i++) { + const otherEntry = other.entries[i]; + const entry = this.entries[i]; + + if (!entry.equals(otherEntry)) { + return false; + } + } + + return true; + } + + override toString(): string { + return `(${this.entries.join(', ')})`; + } +} + +export class NamedTupleEntry { + constructor( + readonly name: string, + readonly type: Type, + ) {} + + toString(): string { + return `${this.name}: ${this.type}`; + } + + equals(other: NamedTupleEntry): boolean { + return this.name === other.name && this.type.equals(other.type); + } +} + export abstract class NamedType extends Type { - protected constructor(private readonly sdsDeclaration: SdsDeclaration) { + protected constructor(readonly sdsDeclaration: SdsDeclaration) { super(); } + abstract override copyWithNullability(isNullable: boolean): NamedType; + override toString(): string { if (this.isNullable) { return `${this.sdsDeclaration.name}?`; @@ -33,8 +169,6 @@ export abstract class NamedType extends Type { return this.sdsDeclaration.name; } } - - // TODO: toQualifiedString(): string that uses qualified names instead of simple names } export class ClassType extends NamedType { @@ -45,7 +179,7 @@ export class ClassType extends NamedType { super(sdsClass); } - override copyWithNullability(isNullable: boolean): Type { + override copyWithNullability(isNullable: boolean): ClassType { return new ClassType(this.sdsClass, isNullable); } @@ -70,7 +204,7 @@ export class EnumType extends NamedType { super(sdsEnum); } - override copyWithNullability(isNullable: boolean): Type { + override copyWithNullability(isNullable: boolean): EnumType { return new EnumType(this.sdsEnum, isNullable); } @@ -95,7 +229,7 @@ export class EnumVariantType extends NamedType { super(sdsEnumVariant); } - override copyWithNullability(isNullable: boolean): Type { + override copyWithNullability(isNullable: boolean): EnumVariantType { return new EnumVariantType(this.sdsEnumVariant, isNullable); } @@ -110,130 +244,134 @@ export class EnumVariantType extends NamedType { return other.sdsEnumVariant === this.sdsEnumVariant && other.isNullable === this.isNullable; } +} + +/** + * A type that represents an actual class, enum, or enum variant instead of an instance of it. + */ +export class StaticType extends Type { + override readonly isNullable = false; + + constructor(readonly instanceType: NamedType) { + super(); + } + + override copyWithNullability(_isNullable: boolean): StaticType { + return this; + } + + override equals(other: Type): boolean { + if (other === this) { + return true; + } + + if (!(other instanceof StaticType)) { + return false; + } + + return other.instanceType.equals(this.instanceType); + } + + override toString(): string { + return `$type<${this.instanceType}>`; + } +} + +export class UnionType extends Type { + override readonly isNullable = false; + + constructor(readonly possibleTypes: Type[]) { + super(); + } + + override copyWithNullability(_isNullable: boolean): UnionType { + return this; + } + + override equals(other: Type): boolean { + if (other === this) { + return true; + } + + if (!(other instanceof UnionType)) { + return false; + } + + if (other.possibleTypes.length !== this.possibleTypes.length) { + return false; + } + + return other.possibleTypes.every((otherPossibleType) => + this.possibleTypes.some((possibleType) => possibleType.equals(otherPossibleType)), + ); + } - // override fun toSimpleString() = buildString { - // sdsEnumVariant.containingEnumOrNull()?.let { append("${it.name}.") } - // append(sdsEnumVariant.name) - // // nullability - // } + override toString(): string { + return `union<${this.possibleTypes.join(', ')}>`; + } } -// class RecordType(resultToType: List>) : Type() { -// private val resultToType = resultToType.toMap() -// -// override val isNullable = false -// override fun setIsNullableOnCopy(isNullable: boolean) = this -// -// override fun toString(): String { -// val types = resultToType.entries.joinToString { (name, type) -> "$name: $type" } -// return "($types)" -// } -// -// override fun toSimpleString(): String { -// val types = resultToType.entries.joinToString { (name, type) -> "$name: ${type.toSimpleString()}" } -// return "($types)" -// } -// -// override fun equals(other: Any?): boolean { -// if (this === other) return true -// if (javaClass != other?.javaClass) return false -// -// other as RecordType -// -// if (resultToType != other.resultToType) return false -// if (isNullable != other.isNullable) return false -// -// return true -// } -// -// override fun hashCode(): Int { -// var result = resultToType.hashCode() -// result = 31 * result + isNullable.hashCode() -// return result -// } -// } -// -// data class CallableType(val parameters: List, val results: List) : Type() { -// override val isNullable = false -// override fun setIsNullableOnCopy(isNullable: boolean) = this -// -// override fun toString(): String { -// val parameters = parameters.joinToString() -// val results = results.joinToString() -// -// return "($parameters) -> ($results)" -// } -// -// override fun toSimpleString(): String { -// val parameters = parameters.joinToString { it.toSimpleString() } -// val results = results.joinToString { it.toSimpleString() } -// -// return "($parameters) -> ($results)" -// } -// } -// -// -// LiteralType -// -// } -// -// data class UnionType(val possibleTypes: Set) : Type() { -// override val isNullable = false -// override fun setIsNullableOnCopy(isNullable: boolean) = this -// -// override fun toString(): String { -// return "union<${possibleTypes.joinToString()}>" -// } -// -// override fun toSimpleString(): String { -// return "union<${possibleTypes.joinToString { it.toSimpleString() }}>" -// } -// } -// -// data class VariadicType(val elementType: Type) : Type() { -// override val isNullable = false -// override fun setIsNullableOnCopy(isNullable: boolean) = this -// -// override fun toString(): String { -// return "vararg<$elementType>" -// } -// -// override fun toSimpleString(): String { -// return "vararg<${elementType.toSimpleString()}>" -// } -// } -// -// data class ParameterisedType( -// val sdsAbstractNamedTypeDeclaration: SdsAbstractDeclaration, -// val kind: String, -// ) : Type() { -// override val isNullable = false -// override fun setIsNullableOnCopy(isNullable: boolean) = this -// -// override fun toString(): String { -// return "::$kind" -// } -// -// override fun toSimpleString(): String { -// return "::$kind" -// } -// } - -class UnresolvedTypeClass extends Type { +export class VariadicType extends Type { + override readonly isNullable = false; + + constructor(readonly elementType: Type) { + super(); + } + + override copyWithNullability(_isNullable: boolean): VariadicType { + return this; + } + + override equals(other: Type): boolean { + if (other === this) { + return true; + } + + if (!(other instanceof VariadicType)) { + return false; + } + + return other.elementType.equals(this.elementType); + } + + override toString(): string { + return `vararg<${this.elementType}>`; + } +} + +class UnknownTypeClass extends Type { readonly isNullable = false; - copyWithNullability(_isNullable: boolean): Type { + copyWithNullability(_isNullable: boolean): UnknownTypeClass { + return this; + } + + override equals(other: Type): boolean { + return other instanceof UnknownTypeClass; + } + + toString(): string { + return '$Unknown'; + } +} + +export const UnknownType = new UnknownTypeClass(); + +class NotImplementedTypeClass extends Type { + override readonly isNullable = false; + + copyWithNullability(_isNullable: boolean): NotImplementedTypeClass { return this; } override equals(other: Type): boolean { - return other instanceof UnresolvedTypeClass; + return other instanceof NotImplementedTypeClass; } toString(): string { - return '$Unresolved'; + return '$NotImplemented'; } } -export const UnresolvedType = new UnresolvedTypeClass(); +export const NotImplementedType = new NotImplementedTypeClass(); /* c8 ignore stop */ diff --git a/src/language/typing/safe-ds-type-computer.ts b/src/language/typing/safe-ds-type-computer.ts index 1d1f22b80..142708c3f 100644 --- a/src/language/typing/safe-ds-type-computer.ts +++ b/src/language/typing/safe-ds-type-computer.ts @@ -1,28 +1,85 @@ -import { AstNode, AstNodeLocator, getDocument, WorkspaceCache } from 'langium'; +import { AstNode, AstNodeLocator, getContainerOfType, getDocument, WorkspaceCache } from 'langium'; import { SafeDsServices } from '../safe-ds-module.js'; import { SafeDsCoreClasses } from '../builtins/safe-ds-core-classes.js'; -import { ClassType, Type, UnresolvedType } from './model.js'; import { + CallableType, + ClassType, + EnumType, + EnumVariantType, + NamedTupleEntry, + NamedTupleType, + NamedType, + NotImplementedType, + StaticType, + Type, + UnionType, + UnknownType, + VariadicType, +} from './model.js'; +import { + isSdsAnnotation, + isSdsArgument, isSdsAssignee, + isSdsAssignment, + isSdsAttribute, + isSdsBlockLambda, isSdsBoolean, + isSdsCall, + isSdsCallable, + isSdsCallableType, + isSdsClass, isSdsDeclaration, + isSdsEnum, + isSdsEnumVariant, isSdsExpression, + isSdsExpressionLambda, isSdsFloat, + isSdsFunction, + isSdsIndexedAccess, + isSdsInfixOperation, isSdsInt, + isSdsLambda, + isSdsLiteralType, + isSdsMemberAccess, + isSdsMemberType, + isSdsNamedType, + isSdsNamedTypeDeclaration, isSdsNull, + isSdsParameter, + isSdsParenthesizedExpression, + isSdsPipeline, + isSdsPrefixOperation, + isSdsReference, + isSdsResult, + isSdsSegment, isSdsString, isSdsTemplateString, isSdsType, - isSdsTypeArgument, isSdsTypeProjection, + isSdsUnionType, + isSdsYield, SdsAssignee, + SdsCall, + SdsCallableType, SdsClass, SdsDeclaration, SdsExpression, + SdsFunction, + SdsInfixOperation, + SdsParameter, + SdsPrefixOperation, + SdsReference, + SdsSegment, SdsType, } from '../generated/ast.js'; +import { + assigneesOrEmpty, + blockLambdaResultsOrEmpty, + parametersOrEmpty, + resultsOrEmpty, + typeArgumentsOrEmpty, +} from '../helpers/shortcuts.js'; -/* c8 ignore start */ export class SafeDsTypeComputer { readonly astNodeLocator: AstNodeLocator; readonly coreClasses: SafeDsCoreClasses; @@ -38,7 +95,7 @@ export class SafeDsTypeComputer { computeType(node: AstNode | undefined): Type { if (!node) { - return UnresolvedType; + return UnknownType; } const documentUri = getDocument(node).uri.toString(); @@ -47,6 +104,21 @@ export class SafeDsTypeComputer { return this.typeCache.get(key, () => this.doComputeType(node)); } + // fun SdsAbstractObject.hasPrimitiveType(): Boolean { + // val type = type() + // if (type !is ClassType) { + // return false + // } + // + // val qualifiedName = type.sdsClass.qualifiedNameOrNull() + // return qualifiedName in setOf( + // StdlibClasses.Boolean, + // StdlibClasses.Float, + // StdlibClasses.Int, + // StdlibClasses.String, + // ) + // } + private doComputeType(node: AstNode): Type { if (isSdsAssignee(node)) { return this.computeTypeOfAssignee(node); @@ -56,23 +128,124 @@ export class SafeDsTypeComputer { return this.computeTypeOfExpression(node); } else if (isSdsType(node)) { return this.computeTypeOfType(node); - } else if (isSdsTypeArgument(node)) { - return UnresolvedType; - // return this.computeType(node.value); } else if (isSdsTypeProjection(node)) { - return UnresolvedType; - // return this.computeTypeOfType(node.type); - } else { - return this.Any(); + return this.computeTypeOfType(node.type); + } /* c8 ignore start */ else { + return UnknownType; + } /* c8 ignore stop */ + } + + private computeTypeOfAssignee(node: SdsAssignee): Type { + const containingAssignment = getContainerOfType(node, isSdsAssignment); + if (!containingAssignment) { + /* c8 ignore next 2 */ + return UnknownType; + } + + const nodePosition = node.$containerIndex ?? -1; + const expressionType = this.computeType(containingAssignment?.expression); + if (expressionType instanceof NamedTupleType) { + return expressionType.getTypeOfEntryByPosition(nodePosition) ?? UnknownType; + } else if (nodePosition === 0) { + return expressionType; } + + return UnknownType; + } + + private computeTypeOfDeclaration(node: SdsDeclaration): Type { + if (isSdsAnnotation(node)) { + const parameterEntries = parametersOrEmpty(node.parameterList).map( + (it) => new NamedTupleEntry(it.name, this.computeType(it.type)), + ); + + return new CallableType(node, new NamedTupleType(parameterEntries), new NamedTupleType([])); + } else if (isSdsAttribute(node)) { + return this.computeType(node.type); + } else if (isSdsClass(node)) { + return new ClassType(node, false); + } else if (isSdsEnum(node)) { + return new EnumType(node, false); + } else if (isSdsEnumVariant(node)) { + return new EnumVariantType(node, false); + } else if (isSdsFunction(node)) { + return this.computeTypeOfCallableWithManifestTypes(node); + } else if (isSdsParameter(node)) { + return this.computeTypeOfParameter(node); + } else if (isSdsPipeline(node)) { + return UnknownType; + } else if (isSdsResult(node)) { + return this.computeType(node.type); + } else if (isSdsSegment(node)) { + return this.computeTypeOfCallableWithManifestTypes(node); + } /* c8 ignore start */ else { + return UnknownType; + } /* c8 ignore stop */ } - private computeTypeOfAssignee(_node: SdsAssignee): Type { - return UnresolvedType; + private computeTypeOfCallableWithManifestTypes(node: SdsFunction | SdsSegment | SdsCallableType): Type { + const parameterEntries = parametersOrEmpty(node.parameterList).map( + (it) => new NamedTupleEntry(it.name, this.computeType(it.type)), + ); + const resultEntries = resultsOrEmpty(node.resultList).map( + (it) => new NamedTupleEntry(it.name, this.computeType(it.type)), + ); + + return new CallableType(node, new NamedTupleType(parameterEntries), new NamedTupleType(resultEntries)); } - private computeTypeOfDeclaration(_node: SdsDeclaration): Type { - return UnresolvedType; + private computeTypeOfParameter(node: SdsParameter): Type { + // Manifest type + if (node.type) { + const manifestParameterType = this.computeType(node.type); + if (node.isVariadic) { + return new VariadicType(manifestParameterType); + } else { + return manifestParameterType; + } + } + + // Infer type from context + const containingCallable = getContainerOfType(node, isSdsCallable); + if (!isSdsLambda(containingCallable)) { + return UnknownType; + } + + const containerOfLambda = containingCallable.$container; + + // Lambda passed as argument + /* c8 ignore start */ + if (isSdsArgument(containerOfLambda)) { + // val containerType = when (val container = callable.eContainer()) { + // is SdsArgument -> container.parameterOrNull()?.inferType(context) + // } + // + // return when (containerType) { + // is CallableType -> containerType.parameters.getOrElse(thisIndex) { Any(context) } + // else -> Any(context) + // } + + return NotImplementedType; + } + /* c8 ignore stop */ + + // Yielded lambda + else if (isSdsAssignment(containerOfLambda)) { + const firstAssignee = assigneesOrEmpty(containerOfLambda)[0]; + if (!isSdsYield(firstAssignee)) { + return UnknownType; + } + + const resultType = this.computeType(firstAssignee.result?.ref); + if (!(resultType instanceof CallableType)) { + return UnknownType; + } + + const parameterPosition = node.$containerIndex ?? -1; + return resultType.getParameterTypeByPosition(parameterPosition) ?? UnknownType; + } + + return UnknownType; } private computeTypeOfExpression(node: SdsExpression): Type { @@ -84,382 +257,299 @@ export class SafeDsTypeComputer { } else if (isSdsInt(node)) { return this.Int(); } else if (isSdsNull(node)) { - return this.Nothing(true); + return this.NothingOrNull(); } else if (isSdsString(node)) { return this.String(); } else if (isSdsTemplateString(node)) { return this.String(); } - return UnresolvedType; + // Recursive cases + else if (isSdsArgument(node)) { + return this.computeType(node.value); + } else if (isSdsCall(node)) { + return this.computeTypeOfCall(node); + } else if (isSdsBlockLambda(node)) { + const parameterEntries = parametersOrEmpty(node.parameterList).map( + (it) => new NamedTupleEntry(it.name, this.computeType(it)), + ); + const resultEntries = blockLambdaResultsOrEmpty(node).map( + (it) => new NamedTupleEntry(it.name, this.computeType(it)), + ); + + return new CallableType(node, new NamedTupleType(parameterEntries), new NamedTupleType(resultEntries)); + } else if (isSdsExpressionLambda(node)) { + const parameterEntries = parametersOrEmpty(node.parameterList).map( + (it) => new NamedTupleEntry(it.name, this.computeType(it)), + ); + const resultEntries = [new NamedTupleEntry('result', this.computeType(node.result))]; + + return new CallableType(node, new NamedTupleType(parameterEntries), new NamedTupleType(resultEntries)); + } else if (isSdsIndexedAccess(node)) { + const receiverType = this.computeType(node.receiver); + if (receiverType instanceof VariadicType) { + return receiverType.elementType; + } else { + return UnknownType; + } + } else if (isSdsInfixOperation(node)) { + switch (node.operator) { + // Boolean operators + case 'or': + case 'and': + return this.Boolean(); + + // Equality operators + case '==': + case '!=': + case '===': + case '!==': + return this.Boolean(); + + // Comparison operators + case '<': + case '<=': + case '>=': + case '>': + return this.Boolean(); + + // Arithmetic operators + case '+': + case '-': + case '*': + case '/': + return this.computeTypeOfArithmeticInfixOperation(node); + + // Elvis operator + case '?:': + return this.computeTypeOfElvisOperation(node); + + // Unknown operator + /* c8 ignore next 2 */ + default: + return UnknownType; + } + } else if (isSdsMemberAccess(node)) { + const memberType = this.computeType(node.member); + return memberType.copyWithNullability(node.isNullSafe || memberType.isNullable); + } else if (isSdsParenthesizedExpression(node)) { + return this.computeType(node.expression); + } else if (isSdsPrefixOperation(node)) { + switch (node.operator) { + case 'not': + return this.Boolean(); + case '-': + return this.computeTypeOfArithmeticPrefixOperation(node); + + // Unknown operator + /* c8 ignore next 2 */ + default: + return UnknownType; + } + } else if (isSdsReference(node)) { + return this.computeTypeOfReference(node); + } /* c8 ignore start */ else { + return UnknownType; + } /* c8 ignore stop */ } - private computeTypeOfType(_node: SdsType): Type { - return UnresolvedType; - } + private computeTypeOfCall(node: SdsCall): Type { + const receiverType = this.computeType(node.receiver); - private cachedAny: Type = UnresolvedType; + if (receiverType instanceof CallableType) { + if (!isSdsAnnotation(receiverType.callable)) { + return receiverType.outputType; + } + } else if (receiverType instanceof StaticType) { + const instanceType = receiverType.instanceType; + const declaration = instanceType.sdsDeclaration; - private Any(): Type { - if (this.cachedAny === UnresolvedType) { - this.cachedAny = this.createCoreType(this.coreClasses.Any); + if (isSdsClass(declaration) || isSdsEnumVariant(declaration)) { + return instanceType; + } } - return this.cachedAny; - } - - private cachedBoolean: Type = UnresolvedType; - private Boolean(): Type { - if (this.cachedBoolean === UnresolvedType) { - this.cachedBoolean = this.createCoreType(this.coreClasses.Boolean); - } - return this.cachedBoolean; + return UnknownType; } - private cachedFloat: Type = UnresolvedType; + private computeTypeOfArithmeticInfixOperation(node: SdsInfixOperation): Type { + const leftOperandType = this.computeType(node.leftOperand); + const rightOperandType = this.computeType(node.rightOperand); - private Float(): Type { - if (this.cachedFloat === UnresolvedType) { - this.cachedFloat = this.createCoreType(this.coreClasses.Float); + if (leftOperandType === this.Int() && rightOperandType === this.Int()) { + return this.Int(); + } else { + return this.Float(); } - return this.cachedFloat; } - private cachedInt: Type = UnresolvedType; - - private Int(): Type { - if (this.cachedInt === UnresolvedType) { - this.cachedInt = this.createCoreType(this.coreClasses.Int); + private computeTypeOfElvisOperation(node: SdsInfixOperation): Type { + const leftOperandType = this.computeType(node.leftOperand); + if (leftOperandType.isNullable) { + /* c8 ignore next 3 */ + const rightOperandType = this.computeType(node.rightOperand); + return this.lowestCommonSupertype(leftOperandType.copyWithNullability(false), rightOperandType); + } else { + return leftOperandType; } - return this.cachedInt; } - private cachedNullableNothing: Type = UnresolvedType; - private cachedNothing: Type = UnresolvedType; + private computeTypeOfArithmeticPrefixOperation(node: SdsPrefixOperation): Type { + const leftOperandType = this.computeType(node.operand); - private Nothing(isNullable: boolean): Type { - if (isNullable) { - if (this.cachedNullableNothing === UnresolvedType) { - this.cachedNullableNothing = this.createCoreType(this.coreClasses.Nothing, true); - } - return this.cachedNullableNothing; + if (leftOperandType === this.Int()) { + return this.Int(); } else { - if (this.cachedNothing === UnresolvedType) { - this.cachedNothing = this.createCoreType(this.coreClasses.Nothing); - } - return this.cachedNothing; + return this.Float(); } } - private cachedString: Type = UnresolvedType; + private computeTypeOfReference(node: SdsReference): Type { + const target = node.target.ref; + const instanceType = this.computeType(target); - private String(): Type { - if (this.cachedString === UnresolvedType) { - this.cachedString = this.createCoreType(this.coreClasses.String); - } - return this.cachedString; - } - - private createCoreType(coreClass: SdsClass | undefined, isNullable: boolean = false): Type { - if (coreClass) { - return new ClassType(coreClass, isNullable); + if (isSdsNamedTypeDeclaration(target) && instanceType instanceof NamedType) { + return new StaticType(instanceType.copyWithNullability(false)); } else { - return UnresolvedType; + return instanceType; } } -} - -/* c8 ignore stop */ -/* -fun SdsAbstractObject.hasPrimitiveType(): Boolean { - val type = type() - if (type !is ClassType) { - return false + private computeTypeOfType(node: SdsType): Type { + if (isSdsCallableType(node)) { + return this.computeTypeOfCallableWithManifestTypes(node); + } else if (isSdsLiteralType(node)) { + /* c8 ignore next */ + return NotImplementedType; + } else if (isSdsMemberType(node)) { + return this.computeType(node.member); + } else if (isSdsNamedType(node)) { + return this.computeType(node.declaration.ref).copyWithNullability(node.isNullable); + } else if (isSdsUnionType(node)) { + const typeArguments = typeArgumentsOrEmpty(node.typeArgumentList); + return new UnionType(typeArguments.map((typeArgument) => this.computeType(typeArgument.value))); + } /* c8 ignore start */ else { + return UnknownType; + } /* c8 ignore stop */ } - val qualifiedName = type.sdsClass.qualifiedNameOrNull() - return qualifiedName in setOf( - StdlibClasses.Boolean, - StdlibClasses.Float, - StdlibClasses.Int, - StdlibClasses.String, - ) -} + // ----------------------------------------------------------------------------------------------------------------- + // Helpers + // ----------------------------------------------------------------------------------------------------------------- -private fun SdsAbstractAssignee.inferTypeForAssignee(context: EObject): Type { - return when { - this.eIsProxy() -> UnresolvedType - this is SdsBlockLambdaResult || this is SdsPlaceholder || this is SdsYield -> { - val assigned = assignedOrNull() ?: return Nothing(context) - assigned.inferType(context) - } - else -> Any(context) + /* c8 ignore start */ + private lowestCommonSupertype(..._types: Type[]): Type { + return NotImplementedType; } -} -@OptIn(ExperimentalSdsApi::class) -private fun SdsAbstractDeclaration.inferTypeForDeclaration(context: EObject): Type { - return when { - this.eIsProxy() -> UnresolvedType - this is SdsAttribute -> type.inferTypeForType(context) - this is SdsClass -> { - val typeParametersTypes = this.typeParametersOrEmpty() - .map { it.inferTypeForDeclaration(context) } - .filterIsInstance() - - ClassType(this, typeParametersTypes, isNullable = false) - } - this is SdsEnum -> EnumType(this, isNullable = false) - this is SdsEnumVariant -> EnumVariantType(this, isNullable = false) - this is SdsFunction -> CallableType( - parametersOrEmpty().map { it.inferTypeForDeclaration(context) }, - resultsOrEmpty().map { it.inferTypeForDeclaration(context) }, - ) - this is SdsParameter -> { - // Declared parameter type - if (this.type != null) { - val declaredParameterType = this.type.inferTypeForType(context) - return when { - this.isVariadic -> VariadicType(declaredParameterType) - else -> declaredParameterType - } - } - - // Inferred lambda parameter type - val callable = this.closestAncestorOrNull() - val thisIndex = callable.parametersOrEmpty().indexOf(this) - if (callable is SdsAbstractLambda) { - val containerType = when (val container = callable.eContainer()) { - is SdsArgument -> container.parameterOrNull()?.inferType(context) - is SdsAssignment -> - container - .yieldsOrEmpty() - .find { it.assignedOrNull() == callable } - ?.result - ?.inferType(context) - else -> null - } - - return when (containerType) { - is CallableType -> containerType.parameters.getOrElse(thisIndex) { Any(context) } - else -> Any(context) - } - } + /* c8 ignore stop */ + + // private fun lowestCommonSupertype(context: EObject, types: List): Type { + // if (types.isEmpty()) { + // return Nothing(context) + // } + // + // val unwrappedTypes = unwrapUnionTypes(types) + // val isNullable = unwrappedTypes.any { it.isNullable } + // var candidate = unwrappedTypes.first().setIsNullableOnCopy(isNullable) + // + // while (!isLowestCommonSupertype(candidate, unwrappedTypes)) { + // candidate = when (candidate) { + // is CallableType -> Any(context, candidate.isNullable) + // is ClassType -> { + // val superClass = candidate.sdsClass.superClasses().firstOrNull() + // ?: return Any(context, candidate.isNullable) + // + // ClassType(superClass, typeParametersTypes, candidate.isNullable) + // } + // is EnumType -> Any(context, candidate.isNullable) + // is EnumVariantType -> { + // val containingEnum = candidate.sdsEnumVariant.containingEnumOrNull() + // ?: return Any(context, candidate.isNullable) + // EnumType(containingEnum, candidate.isNullable) + // } + // is RecordType -> Any(context, candidate.isNullable) + // // TODO: Correct ? + // is UnionType -> throw AssertionError("Union types should have been unwrapped.") + // UnresolvedType -> Any(context, candidate.isNullable) + // is VariadicType -> Any(context, candidate.isNullable) + // } + // } + // + // return candidate + // } + // + // private fun unwrapUnionTypes(types: List): List { + // return types.flatMap { + // when (it) { + // is UnionType -> it.possibleTypes + // else -> listOf(it) + // } + // } + // } + // + // private fun isLowestCommonSupertype(candidate: Type, otherTypes: List): Boolean { + // if (candidate is ClassType && candidate.sdsClass.qualifiedNameOrNull() == StdlibClasses.Any) { + // return true + // } + // + // return otherTypes.all { it.isSubstitutableFor(candidate) } + // } + + // ----------------------------------------------------------------------------------------------------------------- + // Builtin types + // ----------------------------------------------------------------------------------------------------------------- + + private cachedBoolean: Type = UnknownType; - // We don't know better - return Any(context) + private Boolean(): Type { + if (this.cachedBoolean === UnknownType) { + this.cachedBoolean = this.createCoreType(this.coreClasses.Boolean); } - this is SdsResult -> type.inferTypeForType(context) - // For now all Schema placeholders are of type 'ParameterisedType(::$SchemaType)' - this is SdsSchemaPlaceholder -> ParameterisedType(this, SdsKind.SchemaKind.toString()) - this is SdsStep -> CallableType( - parametersOrEmpty().map { it.inferTypeForDeclaration(context) }, - resultsOrEmpty().map { it.inferTypeForDeclaration(context) }, - ) - // Todo: resolve TypeParameter for "non kind" TypeParameter too - this is SdsTypeParameter && this.kind != null -> ParameterisedType(this, kind) - else -> Any(context) + return this.cachedBoolean; } -} -private fun SdsAbstractExpression.inferTypeExpression(context: EObject): Type { - return when { - // Terminal cases - this.eIsProxy() -> UnresolvedType - this is SdsBoolean -> Boolean(context) - this is SdsFloat -> Float(context) - this is SdsInt -> Int(context) - this is SdsNull -> Nothing(context, isNullable = true) - this is SdsString -> String(context) - this is SdsTemplateString -> String(context) + private cachedFloat: Type = UnknownType; - // Recursive cases - this is SdsArgument -> this.value.inferTypeExpression(context) - this is SdsBlockLambda -> CallableType( - this.parametersOrEmpty().map { it.inferTypeForDeclaration(context) }, - blockLambdaResultsOrEmpty().map { it.inferTypeForAssignee(context) }, - ) - this is SdsCall -> when (val callable = callableOrNull()) { - is SdsClass -> { - val typeParametersTypes = callable.typeParametersOrEmpty() - .map { it.inferTypeForDeclaration(context) } - .filterIsInstance() - - ClassType(callable, typeParametersTypes, isNullable = false) - } - is SdsCallableType -> { - val results = callable.resultsOrEmpty() - when (results.size) { - 1 -> results.first().inferTypeForDeclaration(context) - else -> RecordType(results.map { it.name to it.inferTypeForDeclaration(context) }) - } - } - is SdsFunction -> { - val results = callable.resultsOrEmpty() - when (results.size) { - 1 -> results.first().inferTypeForDeclaration(context) - else -> RecordType(results.map { it.name to it.inferTypeForDeclaration(context) }) - } - } - is SdsBlockLambda -> { - val results = callable.blockLambdaResultsOrEmpty() - when (results.size) { - 1 -> results.first().inferTypeForAssignee(context) - else -> RecordType(results.map { it.name to it.inferTypeForAssignee(context) }) - } - } - is SdsEnumVariant -> { - EnumVariantType(callable, isNullable = false) - } - is SdsExpressionLambda -> { - callable.result.inferTypeExpression(context) - } - is SdsStep -> { - val results = callable.resultsOrEmpty() - when (results.size) { - 1 -> results.first().inferTypeForDeclaration(context) - else -> RecordType(results.map { it.name to it.inferTypeForDeclaration(context) }) - } - } - else -> Any(context) - } - this is SdsExpressionLambda -> CallableType( - this.parametersOrEmpty().map { it.inferTypeForDeclaration(context) }, - listOf(result.inferTypeExpression(context)), - ) - this is SdsIndexedAccess -> { - when (val receiverType = this.receiver.inferTypeExpression(context)) { - is UnresolvedType -> UnresolvedType - is VariadicType -> receiverType.elementType - else -> Nothing(context) - } - } - this is SdsInfixOperation -> when (operator) { - "<", "<=", ">=", ">" -> Boolean(context) - "==", "!=" -> Boolean(context) - "===", "!==" -> Boolean(context) - "or", "and" -> Boolean(context) - "+", "-", "*", "/" -> when { - this.leftOperand.inferTypeExpression(context) == Int(context) && - this.rightOperand.inferTypeExpression(context) == Int(context) -> Int(context) - else -> Float(context) - } - "?:" -> { - val leftOperandType = this.leftOperand.inferTypeExpression(context) - if (leftOperandType.isNullable) { - lowestCommonSupertype( - context, - listOf( - leftOperandType.setIsNullableOnCopy(isNullable = false), - this.rightOperand.inferTypeExpression(context), - ), - ) - } else { - leftOperandType - } - } - else -> Nothing(context) - } - this is SdsMemberAccess -> { - val memberType = this.member.inferTypeExpression(context) - memberType.setIsNullableOnCopy(this.isNullSafe || memberType.isNullable) - } - this is SdsParenthesizedExpression -> this.expression.inferTypeExpression(context) - this is SdsPrefixOperation -> when (operator) { - "not" -> Boolean(context) - "-" -> when (this.operand.inferTypeExpression(context)) { - Int(context) -> Int(context) - else -> Float(context) - } - else -> Nothing(context) + private Float(): Type { + if (this.cachedFloat === UnknownType) { + this.cachedFloat = this.createCoreType(this.coreClasses.Float); } - this is SdsReference -> this.declaration.inferType(context) - this is SdsSchemaReference -> this.type.inferTypeForType(context) - else -> Any(context) + return this.cachedFloat; } -} -private fun SdsAbstractType.inferTypeForType(context: EObject): Type { - return when { - this.eIsProxy() -> UnresolvedType - this is SdsCallableType -> CallableType( - this.parametersOrEmpty().map { it.inferTypeForDeclaration(context) }, - this.resultsOrEmpty().map { it.inferTypeForDeclaration(context) }, - ) - this is SdsMemberType -> { - this.member.inferTypeForType(context) - } - this is SdsNamedType -> { - this.declaration.inferTypeForDeclaration(context).setIsNullableOnCopy(this.isNullable) - } - this is SdsParenthesizedType -> { - this.type.inferTypeForType(context) - } - this is SdsSchemaType -> { - this.declaration.inferTypeForDeclaration(context) - } - this is SdsUnionType -> { - UnionType(this.typeArgumentsOrEmpty().map { it.value.inferType(context) }.toSet()) - } - else -> Any(context) - } -} + private cachedInt: Type = UnknownType; -private fun lowestCommonSupertype(context: EObject, types: List): Type { - if (types.isEmpty()) { - return Nothing(context) + private Int(): Type { + if (this.cachedInt === UnknownType) { + this.cachedInt = this.createCoreType(this.coreClasses.Int); + } + return this.cachedInt; } - val unwrappedTypes = unwrapUnionTypes(types) - val isNullable = unwrappedTypes.any { it.isNullable } - var candidate = unwrappedTypes.first().setIsNullableOnCopy(isNullable) - - while (!isLowestCommonSupertype(candidate, unwrappedTypes)) { - candidate = when (candidate) { - is CallableType -> Any(context, candidate.isNullable) - is ClassType -> { - val superClass = candidate.sdsClass.superClasses().firstOrNull() - ?: return Any(context, candidate.isNullable) - - val typeParametersTypes = superClass.typeParametersOrEmpty() - .map { it.inferTypeForDeclaration(context) } - .filterIsInstance() + private cachedNothingOrNull: Type = UnknownType; - ClassType(superClass, typeParametersTypes, candidate.isNullable) - } - is EnumType -> Any(context, candidate.isNullable) - is EnumVariantType -> { - val containingEnum = candidate.sdsEnumVariant.containingEnumOrNull() - ?: return Any(context, candidate.isNullable) - EnumType(containingEnum, candidate.isNullable) - } - is RecordType -> Any(context, candidate.isNullable) - // TODO: Correct ? - is ParameterisedType -> Any(context, candidate.isNullable) - is UnionType -> throw AssertionError("Union types should have been unwrapped.") - UnresolvedType -> Any(context, candidate.isNullable) - is VariadicType -> Any(context, candidate.isNullable) + private NothingOrNull(): Type { + if (this.cachedNothingOrNull === UnknownType) { + this.cachedNothingOrNull = this.createCoreType(this.coreClasses.Nothing, true); } + return this.cachedNothingOrNull; } - return candidate -} + private cachedString: Type = UnknownType; -private fun unwrapUnionTypes(types: List): List { - return types.flatMap { - when (it) { - is UnionType -> it.possibleTypes - else -> listOf(it) + private String(): Type { + if (this.cachedString === UnknownType) { + this.cachedString = this.createCoreType(this.coreClasses.String); } + return this.cachedString; } -} -private fun isLowestCommonSupertype(candidate: Type, otherTypes: List): Boolean { - if (candidate is ClassType && candidate.sdsClass.qualifiedNameOrNull() == StdlibClasses.Any) { - return true + private createCoreType(coreClass: SdsClass | undefined, isNullable: boolean = false): Type { + if (coreClass) { + return new ClassType(coreClass, isNullable); + } /* c8 ignore start */ else { + return UnknownType; + } /* c8 ignore stop */ } - - return otherTypes.all { it.isSubstitutableFor(candidate) } } - */ diff --git a/src/language/typing/typeConformance.ts b/src/language/typing/typeConformance.ts new file mode 100644 index 000000000..35f42f6dc --- /dev/null +++ b/src/language/typing/typeConformance.ts @@ -0,0 +1,141 @@ +// package com.larsreimann.safeds.staticAnalysis.typing +// +// import com.larsreimann.safeds.constant.kind +// import com.larsreimann.safeds.emf.variantsOrEmpty +// import com.larsreimann.safeds.naming.qualifiedNameOrNull +// import com.larsreimann.safeds.staticAnalysis.classHierarchy.isSubtypeOf +// import com.larsreimann.safeds.stdlibAccess.StdlibClasses +// import com.larsreimann.safeds.utils.ExperimentalSdsApi +// +// fun Type.isSubstitutableFor(other: Type, resultIfUnresolved: Boolean = false): Boolean { +// if (other == UnresolvedType) { +// return resultIfUnresolved +// } +// +// return when (this) { +// is CallableType -> this.isSubstitutableFor(other) +// is ClassType -> this.isSubstitutableFor(other) +// is EnumType -> this.isSubstitutableFor(other) +// is EnumVariantType -> this.isSubstitutableFor(other) +// is ParameterisedType -> this.isSubstitutableFor(other) +// is UnionType -> this.isSubstitutableFor(other) +// is VariadicType -> this.isSubstitutableFor(other) +// is RecordType -> false +// UnresolvedType -> resultIfUnresolved +// } +// } +// +// private fun CallableType.isSubstitutableFor(other: Type): Boolean { +// return when (val unwrappedOther = unwrapVariadicType(other)) { +// is CallableType -> { +// // TODO: We need to compare names of parameters & results and can allow additional optional parameters +// +// // Sizes must match (too strict requirement -> should be loosened later) +// if (this.parameters.size != unwrappedOther.parameters.size || this.results.size != this.results.size) { +// return false +// } +// +// // Actual parameters must be supertypes of expected parameters (contravariance) +// this.parameters.zip(unwrappedOther.parameters).forEach { (thisParameter, otherParameter) -> +// if (!otherParameter.isSubstitutableFor(thisParameter)) { +// return false +// } +// } +// +// // Expected results must be subtypes of expected results (covariance) +// this.results.zip(unwrappedOther.results).forEach { (thisResult, otherResult) -> +// if (!thisResult.isSubstitutableFor(otherResult)) { +// return false +// } +// } +// +// true +// } +// is ClassType -> { +// unwrappedOther.sdsClass.qualifiedNameOrNull() == StdlibClasses.Any +// } +// is UnionType -> { +// unwrappedOther.possibleTypes.any { this.isSubstitutableFor(it) } +// } +// else -> false +// } +// } +// +// private fun ClassType.isSubstitutableFor(other: Type): Boolean { +// return when (val unwrappedOther = unwrapVariadicType(other)) { +// is ClassType -> { +// (!this.isNullable || unwrappedOther.isNullable) && this.sdsClass.isSubtypeOf(unwrappedOther.sdsClass) +// } +// is UnionType -> { +// unwrappedOther.possibleTypes.any { this.isSubstitutableFor(it) } +// } +// else -> false +// } +// } +// +// private fun EnumType.isSubstitutableFor(other: Type): Boolean { +// return when (val unwrappedOther = unwrapVariadicType(other)) { +// is ClassType -> { +// (!this.isNullable || unwrappedOther.isNullable) && +// unwrappedOther.sdsClass.qualifiedNameOrNull() == StdlibClasses.Any +// } +// is EnumType -> { +// (!this.isNullable || unwrappedOther.isNullable) && this.sdsEnum == unwrappedOther.sdsEnum +// } +// is UnionType -> { +// unwrappedOther.possibleTypes.any { this.isSubstitutableFor(it) } +// } +// else -> false +// } +// } +// +// private fun EnumVariantType.isSubstitutableFor(other: Type): Boolean { +// return when (val unwrappedOther = unwrapVariadicType(other)) { +// is ClassType -> { +// (!this.isNullable || unwrappedOther.isNullable) && +// unwrappedOther.sdsClass.qualifiedNameOrNull() == StdlibClasses.Any +// } +// is EnumType -> { +// (!this.isNullable || unwrappedOther.isNullable) && +// this.sdsEnumVariant in unwrappedOther.sdsEnum.variantsOrEmpty() +// } +// is EnumVariantType -> { +// (!this.isNullable || unwrappedOther.isNullable) && this.sdsEnumVariant == unwrappedOther.sdsEnumVariant +// } +// is UnionType -> unwrappedOther.possibleTypes.any { this.isSubstitutableFor(it) } +// else -> false +// } +// } +// +// @OptIn(ExperimentalSdsApi::class) +// private fun ParameterisedType.isSubstitutableFor(other: Type): Boolean { +// return when (other) { +// is ParameterisedType -> (!this.isNullable || other.isNullable) && this.kind() == other.kind() +// else -> false +// } +// } +// +// /** +// * A [UnionType] can be substituted for another type of all its possible types can be substituted for the other type. +// */ +// private fun UnionType.isSubstitutableFor(other: Type): Boolean { +// return this.possibleTypes.all { it.isSubstitutableFor(other) } +// } +// +// private fun VariadicType.isSubstitutableFor(other: Type): Boolean { +// return when (other) { +// is ClassType -> other.sdsClass.qualifiedNameOrNull() == StdlibClasses.Any +// is VariadicType -> this.elementType.isSubstitutableFor(other) +// else -> false +// } +// } +// +// /** +// * Returns the elementType of a [VariadicType] or, otherwise, the type itself. +// */ +// private fun unwrapVariadicType(type: Type): Type { +// return when (type) { +// is VariadicType -> type.elementType +// else -> type +// } +// } diff --git a/src/language/validation/other/declarations/parameterLists.ts b/src/language/validation/other/declarations/parameterLists.ts index f7851aabb..e2458e5d8 100644 --- a/src/language/validation/other/declarations/parameterLists.ts +++ b/src/language/validation/other/declarations/parameterLists.ts @@ -11,7 +11,7 @@ export const parameterListMustNotHaveOptionalAndVariadicParameters = ( ) => { const hasOptional = node.parameters.find((p) => p.defaultValue); if (hasOptional) { - const variadicRequiredParameters = node.parameters.filter((p) => p.variadic && !p.defaultValue); + const variadicRequiredParameters = node.parameters.filter((p) => p.isVariadic && !p.defaultValue); for (const variadic of variadicRequiredParameters) { accept('error', 'A callable with optional parameters must not have a variadic parameter.', { @@ -31,7 +31,7 @@ export const parameterListMustNotHaveRequiredParametersAfterOptionalParameters = for (const parameter of node.parameters) { if (parameter.defaultValue) { foundOptional = true; - } else if (foundOptional && !parameter.variadic) { + } else if (foundOptional && !parameter.isVariadic) { accept('error', 'After the first optional parameter all parameters must be optional.', { node: parameter, property: 'name', @@ -52,7 +52,7 @@ export const parameterListVariadicParameterMustBeLast = (node: SdsParameterList, }); } - if (parameter.variadic) { + if (parameter.isVariadic) { foundVariadic = true; } } diff --git a/src/language/validation/other/declarations/parameters.ts b/src/language/validation/other/declarations/parameters.ts index 064fd74bd..473db1763 100644 --- a/src/language/validation/other/declarations/parameters.ts +++ b/src/language/validation/other/declarations/parameters.ts @@ -4,7 +4,7 @@ import { ValidationAcceptor } from 'langium'; export const CODE_PARAMETER_VARIADIC_AND_OPTIONAL = 'parameter/variadic-and-optional'; export const parameterMustNotBeVariadicAndOptional = (node: SdsParameter, accept: ValidationAcceptor) => { - if (node.variadic && node.defaultValue) { + if (node.isVariadic && node.defaultValue) { accept('error', 'Variadic parameters must not be optional.', { node, property: 'name', diff --git a/src/language/validation/other/types/callableTypes.ts b/src/language/validation/other/types/callableTypes.ts index 28058acde..fba81efac 100644 --- a/src/language/validation/other/types/callableTypes.ts +++ b/src/language/validation/other/types/callableTypes.ts @@ -6,7 +6,7 @@ export const CODE_CALLABLE_TYPE_NO_OPTIONAL_PARAMETERS = 'callable-type/no-optio export const callableTypeMustNotHaveOptionalParameters = (node: SdsCallableType, accept: ValidationAcceptor): void => { for (const parameter of parametersOrEmpty(node.parameterList)) { - if (parameter.defaultValue && !parameter.variadic) { + if (parameter.defaultValue && !parameter.isVariadic) { accept('error', 'A callable type must not have optional parameters.', { node: parameter, property: 'defaultValue', diff --git a/tests/helpers/location.ts b/tests/helpers/location.ts index ef18fc5ed..9e0d41a48 100644 --- a/tests/helpers/location.ts +++ b/tests/helpers/location.ts @@ -47,7 +47,7 @@ export const isLocationEqual = (location1: Location, location2: Location): boole }; /** - * Find the AstNode at the given location. It must fill the range exactly. + * Find the AstNode at the given location. It must either fill the entire range or have a name node that does. * * @param services The services to use. * @param location The location of the node to find. @@ -73,7 +73,7 @@ export const getNodeByLocation = (services: SafeDsServices, location: Location): }); } - for (const node of streamAllContents(root, { range: location.range })) { + for (const node of streamAllContents(root)) { // Entire node matches the range const actualRange = node.$cstNode?.range; if (actualRange && isRangeEqual(actualRange, location.range)) { diff --git a/tests/resources/typing/assignees/block lambda results/main.sdstest b/tests/resources/typing/assignees/block lambda results/main.sdstest new file mode 100644 index 000000000..a8ace56be --- /dev/null +++ b/tests/resources/typing/assignees/block lambda results/main.sdstest @@ -0,0 +1,23 @@ +package tests.typing.assignees.blockLambdaResults + +fun f() -> (r1: Int, r2: Float, r3: String) + +segment mySegment() -> (r: Int) { + () { + // $TEST$ equivalence_class assignedValue + // $TEST$ equivalence_class assignedValue + yield »r« = »1«; + }; + + () { + // $TEST$ serialization Int + // $TEST$ serialization $Unknown + yield »r«, yield »s« = 1; + }; + + () { + // $TEST$ serialization Int + // $TEST$ serialization String + yield »r«, _, yield »s« = f(); + }; +} diff --git a/tests/resources/typing/assignees/placeholders/main.sdstest b/tests/resources/typing/assignees/placeholders/main.sdstest new file mode 100644 index 000000000..f091e8e32 --- /dev/null +++ b/tests/resources/typing/assignees/placeholders/main.sdstest @@ -0,0 +1,21 @@ +package tests.typing.assignees.placeholders + +fun f() -> (r1: Int, r2: Float, r3: String) + +segment mySegment1() -> (r: Int) { + // $TEST$ equivalence_class assignedValue + // $TEST$ equivalence_class assignedValue + val »r« = »1«; +} + +segment mySegment2() -> (r: Int, s: String) { + // $TEST$ serialization Int + // $TEST$ serialization $Unknown + val »r«, val »s« = 1; +} + +segment mySegment3() -> (r: Int, s: String) { + // $TEST$ serialization Int + // $TEST$ serialization String + val »r«, _, val »s« = f(); +} diff --git a/tests/resources/typing/assignees/yields/main.sdstest b/tests/resources/typing/assignees/yields/main.sdstest new file mode 100644 index 000000000..21f442a3e --- /dev/null +++ b/tests/resources/typing/assignees/yields/main.sdstest @@ -0,0 +1,21 @@ +package tests.typing.assignees.yields + +fun f() -> (r1: Int, r2: Float, r3: String) + +segment mySegment1() -> (r: Int) { + // $TEST$ equivalence_class assignedValue + // $TEST$ equivalence_class assignedValue + »yield r«, _ = »1«; // the wildcards prevents the marker from matching the entire assignee list +} + +segment mySegment2() -> (r: Int, s: String) { + // $TEST$ serialization Int + // $TEST$ serialization $Unknown + »yield r«, »yield s« = 1; +} + +segment mySegment3() -> (r: Int, s: String) { + // $TEST$ serialization Int + // $TEST$ serialization String + »yield r«, _, »yield s« = f(); +} diff --git a/tests/resources/typing/declarations/annotations/main.sdstest b/tests/resources/typing/declarations/annotations/main.sdstest new file mode 100644 index 000000000..2c3c7a752 --- /dev/null +++ b/tests/resources/typing/declarations/annotations/main.sdstest @@ -0,0 +1,10 @@ +package tests.typing.declarations.annotations + +// $TEST$ serialization () -> () +annotation »myAnnotation1« + +// $TEST$ serialization (p1: Int, p2: String) -> () +annotation »myAnnotation3«(p1: Int, p2: String) + +// $TEST$ serialization (p1: $Unknown) -> () +annotation »myAnnotation5«(p1) diff --git a/tests/resources/typing/declarations/attributes/main.sdstest b/tests/resources/typing/declarations/attributes/main.sdstest new file mode 100644 index 000000000..48c8c0a1f --- /dev/null +++ b/tests/resources/typing/declarations/attributes/main.sdstest @@ -0,0 +1,17 @@ +package tests.typing.declarations.attributes + +class C { + // $TEST$ serialization $Unknown + attr »a« + + // $TEST$ equivalence_class instanceAttribute + // $TEST$ equivalence_class instanceAttribute + attr »b«: »Int« + + // $TEST$ serialization $Unknown + static attr »c« + + // $TEST$ equivalence_class staticAttribute + // $TEST$ equivalence_class staticAttribute + static attr »d«: »Int« +} diff --git a/tests/resources/typing/declarations/classes/main.sdstest b/tests/resources/typing/declarations/classes/main.sdstest new file mode 100644 index 000000000..17c8bbeb3 --- /dev/null +++ b/tests/resources/typing/declarations/classes/main.sdstest @@ -0,0 +1,7 @@ +package tests.typing.declarations.classes + +// $TEST$ serialization MyClass1 +class »MyClass1« + +// $TEST$ serialization MyClass2 +class »MyClass2« diff --git a/tests/resources/typing/declarations/enum variants/main.sdstest b/tests/resources/typing/declarations/enum variants/main.sdstest new file mode 100644 index 000000000..a3dbfa674 --- /dev/null +++ b/tests/resources/typing/declarations/enum variants/main.sdstest @@ -0,0 +1,9 @@ +package tests.typing.declarations.enumVariants + +enum MyEnum { + // $TEST$ serialization MyEnumVariant1 + »MyEnumVariant1« + + // $TEST$ serialization MyEnumVariant2 + »MyEnumVariant2« +} diff --git a/tests/resources/typing/declarations/enums/main.sdstest b/tests/resources/typing/declarations/enums/main.sdstest new file mode 100644 index 000000000..e1297fb1d --- /dev/null +++ b/tests/resources/typing/declarations/enums/main.sdstest @@ -0,0 +1,7 @@ +package tests.typing.declarations.enums + +// $TEST$ serialization MyEnum1 +enum »MyEnum1« + +// $TEST$ serialization MyEnum2 +enum »MyEnum2« diff --git a/tests/resources/typing/declarations/functions/main.sdstest b/tests/resources/typing/declarations/functions/main.sdstest new file mode 100644 index 000000000..7b4c02a25 --- /dev/null +++ b/tests/resources/typing/declarations/functions/main.sdstest @@ -0,0 +1,16 @@ +package tests.typing.declarations.functions + +// $TEST$ serialization () -> () +fun »myFunction1«() + +// $TEST$ serialization () -> (r1: Int, r2: String) +fun »myFunction2«() -> (r1: Int, r2: String) + +// $TEST$ serialization (p1: Int, p2: String) -> () +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) +fun »myFunction5«(p1) -> (r1) diff --git a/tests/resources/typing/declarations/parameters/of annotations/main.sdstest b/tests/resources/typing/declarations/parameters/of annotations/main.sdstest new file mode 100644 index 000000000..ebf0930ae --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of annotations/main.sdstest @@ -0,0 +1,11 @@ +package tests.typing.declarations.parameters.ofAnnotations + +// $TEST$ equivalence_class parameterType +// $TEST$ equivalence_class parameterType +annotation MyAnnotation1(»p«: »Int«) + +// $TEST$ serialization vararg +annotation MyAnnotation2(vararg »p«: String) + +// $TEST$ serialization $Unknown +annotation MyAnnotation3(»p«) diff --git a/tests/resources/typing/declarations/parameters/of block lambdas/skip-that are passed as argument.sdstest b/tests/resources/typing/declarations/parameters/of block lambdas/skip-that are passed as argument.sdstest new file mode 100644 index 000000000..9e165397c --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of block lambdas/skip-that are passed as argument.sdstest @@ -0,0 +1,26 @@ +package tests.typing.declarations.parameters.ofBlockLambdas + +// $TEST$ equivalence_class parameterType1 +fun higherOrderFunction1(param: (a: »String«) -> ()) +fun higherOrderFunction2(param: () -> ()) +fun normalFunction(param: Int) + +segment mySegment() { + // $TEST$ equivalence_class parameterType1 + higherOrderFunction1((»p«) {}); + + // $TEST$ equivalence_class parameterType1 + higherOrderFunction1(param = (»p«) {}); + + // $TEST$ serialization $Unknown + higherOrderFunction2((»p«) {}); + + // $TEST$ serialization $Unknown + higherOrderFunction2(param = (»p«) {}); + + // $TEST$ serialization $Unknown + normalFunction((»p«) {}); + + // $TEST$ serialization $Unknown + normalFunction(param = (»p«) {}); +} diff --git a/tests/resources/typing/declarations/parameters/of block lambdas/that are isolated.sdstest b/tests/resources/typing/declarations/parameters/of block lambdas/that are isolated.sdstest new file mode 100644 index 000000000..0fa3e694c --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of block lambdas/that are isolated.sdstest @@ -0,0 +1,6 @@ +package tests.typing.declarations.parameters.ofBlockLambdas + +segment mySegment() { + // $TEST$ serialization $Unknown + (»p«) {}; +} diff --git a/tests/resources/typing/declarations/parameters/of block lambdas/that are yielded.sdstest b/tests/resources/typing/declarations/parameters/of block lambdas/that are yielded.sdstest new file mode 100644 index 000000000..74818c8d5 --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of block lambdas/that are yielded.sdstest @@ -0,0 +1,17 @@ +package tests.typing.declarations.parameters.ofBlockLambdas + +segment mySegment() -> ( + // $TEST$ equivalence_class parameterType2 + r: (p: »String«) -> (), + s: () -> (), + t: Int, +) { + // $TEST$ equivalence_class parameterType2 + yield r = (»p«) {}; + + // $TEST$ serialization $Unknown + yield s = (»p«) {}; + + // $TEST$ serialization $Unknown + yield t = (»p«) {}; +} diff --git a/tests/resources/typing/declarations/parameters/of block lambdas/with manifest types.sdstest b/tests/resources/typing/declarations/parameters/of block lambdas/with manifest types.sdstest new file mode 100644 index 000000000..3e481344e --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of block lambdas/with manifest types.sdstest @@ -0,0 +1,10 @@ +package tests.typing.declarations.parameters.ofBlockLambdas + +segment mySegment() { + // $TEST$ equivalence_class parameterType3 + // $TEST$ equivalence_class parameterType3 + (»p«: »Int«) {}; + + // $TEST$ serialization vararg + (vararg »p«: String) {}; +} diff --git a/tests/resources/typing/declarations/parameters/of callable types/main.sdstest b/tests/resources/typing/declarations/parameters/of callable types/main.sdstest new file mode 100644 index 000000000..716f12cb9 --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of callable types/main.sdstest @@ -0,0 +1,11 @@ +package tests.typing.declarations.parameters.ofCallableTypes + +// $TEST$ equivalence_class parameterType +// $TEST$ equivalence_class parameterType +annotation MyAnnotation1(f: (»p«: »Int«) -> ()) + +// $TEST$ serialization vararg +annotation MyAnnotation2(f: (vararg »p«: String) -> ()) + +// $TEST$ serialization $Unknown +annotation MyAnnotation3(f: (»p«) -> ()) diff --git a/tests/resources/typing/declarations/parameters/of classes/main.sdstest b/tests/resources/typing/declarations/parameters/of classes/main.sdstest new file mode 100644 index 000000000..1ad48613e --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of classes/main.sdstest @@ -0,0 +1,11 @@ +package tests.typing.declarations.parameters.ofClasses + +// $TEST$ equivalence_class parameterType +// $TEST$ equivalence_class parameterType +class MyClass1(»p«: »Int«) + +// $TEST$ serialization vararg +class MyClass2(vararg »p«: String) + +// $TEST$ serialization $Unknown +class MyClass3(»p«) diff --git a/tests/resources/typing/declarations/parameters/of enum variants/main.sdstest b/tests/resources/typing/declarations/parameters/of enum variants/main.sdstest new file mode 100644 index 000000000..208ce02a5 --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of enum variants/main.sdstest @@ -0,0 +1,13 @@ +package tests.typing.declarations.parameters.ofEnumVariants + +enum MyEnum { + // $TEST$ equivalence_class parameterType + // $TEST$ equivalence_class parameterType + MyEnumVariant1(»p«: »Int«) + + // $TEST$ serialization vararg + MyEnumVariant2(vararg »p«: String) + + // $TEST$ serialization $Unknown + MyEnumVariant3(»p«) +} diff --git a/tests/resources/typing/declarations/parameters/of expression lambdas/skip-that are passed as argument.sdstest b/tests/resources/typing/declarations/parameters/of expression lambdas/skip-that are passed as argument.sdstest new file mode 100644 index 000000000..ecfc233fe --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of expression lambdas/skip-that are passed as argument.sdstest @@ -0,0 +1,26 @@ +package tests.typing.declarations.parameters.ofExpressionLambdas + +// $TEST$ equivalence_class parameterType1 +fun higherOrderFunction1(param: (a: »String«) -> r: String) +fun higherOrderFunction2(param: () -> r: String) +fun normalFunction(param: Int) + +segment mySegment() { + // $TEST$ equivalence_class parameterType1 + higherOrderFunction1((»p«) -> ""); + + // $TEST$ equivalence_class parameterType1 + higherOrderFunction1(param = (»p«) -> ""); + + // $TEST$ serialization $Unknown + higherOrderFunction2((»p«) -> ""); + + // $TEST$ serialization $Unknown + higherOrderFunction2(param = (»p«) -> ""); + + // $TEST$ serialization $Unknown + normalFunction((»p«) -> ""); + + // $TEST$ serialization $Unknown + normalFunction(param = (»p«) -> ""); +} diff --git a/tests/resources/typing/declarations/parameters/of expression lambdas/that are isolated.sdstest b/tests/resources/typing/declarations/parameters/of expression lambdas/that are isolated.sdstest new file mode 100644 index 000000000..a4460a231 --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of expression lambdas/that are isolated.sdstest @@ -0,0 +1,6 @@ +package tests.typing.declarations.parameters.ofExpressionLambdas + +segment mySegment() { + // $TEST$ serialization $Unknown + (»p«) -> 1; +} diff --git a/tests/resources/typing/declarations/parameters/of expression lambdas/that are yielded.sdstest b/tests/resources/typing/declarations/parameters/of expression lambdas/that are yielded.sdstest new file mode 100644 index 000000000..b4c92b7b4 --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of expression lambdas/that are yielded.sdstest @@ -0,0 +1,17 @@ +package tests.typing.declarations.parameters.ofExpressionLambdas + +segment mySegment() -> ( + // $TEST$ equivalence_class parameterType2 + r: (p: »String«) -> r: String, + s: () -> r: String, + t: Int, +) { + // $TEST$ equivalence_class parameterType2 + yield r = (»p«) -> true; + + // $TEST$ serialization $Unknown + yield s = (»p«) -> true; + + // $TEST$ serialization $Unknown + yield t = (»p«) -> true; +} diff --git a/tests/resources/typing/declarations/parameters/of expression lambdas/with manifest types.sdstest b/tests/resources/typing/declarations/parameters/of expression lambdas/with manifest types.sdstest new file mode 100644 index 000000000..0e9c1fad3 --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of expression lambdas/with manifest types.sdstest @@ -0,0 +1,10 @@ +package tests.typing.declarations.parameters.ofExpressionLambdas + +segment mySegment() { + // $TEST$ equivalence_class parameterType3 + // $TEST$ equivalence_class parameterType3 + (»p«: »Int«) -> 1; + + // $TEST$ serialization vararg + (vararg »p«: String) -> 1; +} diff --git a/tests/resources/typing/declarations/parameters/of functions/main.sdstest b/tests/resources/typing/declarations/parameters/of functions/main.sdstest new file mode 100644 index 000000000..2374778db --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of functions/main.sdstest @@ -0,0 +1,11 @@ +package tests.typing.declarations.parameters.ofFunctions + +// $TEST$ equivalence_class parameterType +// $TEST$ equivalence_class parameterType +fun myFunction1(»p«: »Int«) + +// $TEST$ serialization vararg +fun myFunction2(vararg »p«: String) + +// $TEST$ serialization $Unknown +fun myFunction3(»p«) diff --git a/tests/resources/typing/declarations/parameters/of segments/main.sdstest b/tests/resources/typing/declarations/parameters/of segments/main.sdstest new file mode 100644 index 000000000..25e9217d6 --- /dev/null +++ b/tests/resources/typing/declarations/parameters/of segments/main.sdstest @@ -0,0 +1,11 @@ +package tests.typing.declarations.parameters.ofSegments + +// $TEST$ equivalence_class parameterType +// $TEST$ equivalence_class parameterType +segment mySegment1(»p«: »Int«) {} + +// $TEST$ serialization vararg +segment mySegment2(vararg »p«: String) {} + +// $TEST$ serialization $Unknown +segment mySegment3(»p«) {} diff --git a/tests/resources/typing/declarations/pipelines/main.sdstest b/tests/resources/typing/declarations/pipelines/main.sdstest new file mode 100644 index 000000000..b971899a6 --- /dev/null +++ b/tests/resources/typing/declarations/pipelines/main.sdstest @@ -0,0 +1,4 @@ +package tests.typing.declarations.pipelines + +// $TEST$ serialization $Unknown +pipeline »myPipeline« {} diff --git a/tests/resources/typing/declarations/results/main.sdstest b/tests/resources/typing/declarations/results/main.sdstest new file mode 100644 index 000000000..fe03bfa52 --- /dev/null +++ b/tests/resources/typing/declarations/results/main.sdstest @@ -0,0 +1,14 @@ +package tests.typing.declarations.results + +// $TEST$ equivalence_class functionResult +// $TEST$ equivalence_class functionResult +fun myFun() -> (»r«: »String«) + +segment mySegment( + // $TEST$ equivalence_class callableTypeResult + // $TEST$ equivalence_class callableTypeResult + p: () -> (»r«: »String«) + +// $TEST$ equivalence_class segmentResult +// $TEST$ equivalence_class segmentResult +) -> (»r«: »String«) {} diff --git a/tests/resources/typing/declarations/segments/main.sdstest b/tests/resources/typing/declarations/segments/main.sdstest new file mode 100644 index 000000000..7c1d4b10b --- /dev/null +++ b/tests/resources/typing/declarations/segments/main.sdstest @@ -0,0 +1,16 @@ +package tests.typing.declarations.segments + +// $TEST$ serialization () -> () +segment »mySegment1«() {} + +// $TEST$ serialization () -> (r1: Int, r2: String) +segment »mySegment2«() -> (r1: Int, r2: String) {} + +// $TEST$ serialization (p1: Int, p2: String) -> () +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) +segment »mySegment5«(p1) -> (r1) {} diff --git a/tests/resources/typing/expressions/arguments/main.sdstest b/tests/resources/typing/expressions/arguments/main.sdstest new file mode 100644 index 000000000..8efa9e45f --- /dev/null +++ b/tests/resources/typing/expressions/arguments/main.sdstest @@ -0,0 +1,40 @@ +package tests.typing.expressions.arguments + +fun f(p: Any?) + +pipeline myPipeline { + // $TEST$ equivalence_class boolean + // $TEST$ equivalence_class boolean + // $TEST$ equivalence_class boolean + »true«; + f(»true«); + f(»p = true«); + + // $TEST$ equivalence_class float + // $TEST$ equivalence_class float + // $TEST$ equivalence_class float + »1.0«; + f(»1.0«); + f(»p = 1.0«); + + // $TEST$ equivalence_class int + // $TEST$ equivalence_class int + // $TEST$ equivalence_class int + »1«; + f(»1«); + f(»p = 1«); + + // $TEST$ equivalence_class null + // $TEST$ equivalence_class null + // $TEST$ equivalence_class null + »null«; + f(»null«); + f(»p = null«); + + // $TEST$ equivalence_class string + // $TEST$ equivalence_class string + // $TEST$ equivalence_class string + »""«; + f(»""«); + f(»p = ""«); +} diff --git a/tests/resources/typing/expressions/block lambdas/skip-that are passed as argument.sdstest b/tests/resources/typing/expressions/block lambdas/skip-that are passed as argument.sdstest new file mode 100644 index 000000000..7c3f9096d --- /dev/null +++ b/tests/resources/typing/expressions/block lambdas/skip-that are passed as argument.sdstest @@ -0,0 +1,40 @@ +package tests.typing.expressions.blockLambdas + +fun higherOrderFunction1(param: (p: String) -> (r: Int, s: String)) +fun higherOrderFunction2(param: () -> ()) +fun normalFunction(param: Int) + +segment mySegment() { + // $TEST$ serialization (p: String) -> (r: Int, s: String) + higherOrderFunction1(»(p) { + yield r = 1; + yield s = ""; + }«); + + // $TEST$ serialization (p: String) -> (r: Int, s: $Unknown) + higherOrderFunction1(param = »(p) { + yield r, yield s = 1; + }«); + + // $TEST$ serialization (p: $Unknown) -> (r: Int, s: String) + higherOrderFunction2(»(p) { + yield r = 1; + yield s = ""; + }«); + + // $TEST$ serialization (p: $Unknown) -> (r: Int, s: $Unknown) + higherOrderFunction2(param = »(p) { + yield r, yield s = 1; + }«); + + // $TEST$ serialization (p: $Unknown) -> (r: Int, s: String) + normalFunction(»(p) { + yield r = 1; + yield s = ""; + }«); + + // $TEST$ serialization (p: $Unknown) -> (r: Int, s: $Unknown) + normalFunction(param = »(p) { + yield r, yield s = 1; + }«); +} diff --git a/tests/resources/typing/expressions/block lambdas/that are isolated.sdstest b/tests/resources/typing/expressions/block lambdas/that are isolated.sdstest new file mode 100644 index 000000000..79085cb78 --- /dev/null +++ b/tests/resources/typing/expressions/block lambdas/that are isolated.sdstest @@ -0,0 +1,13 @@ +package tests.typing.expressions.blockLambdas + +segment mySegment() { + // $TEST$ serialization (p: $Unknown) -> (r: Int, s: $Unknown) + »(p) { + yield r, yield s = 1; + }«; + + // $TEST$ serialization (p: $Unknown) -> (r: Int, s: $Unknown) + val f = »(p) { + yield r, yield s = 1; + }«; +} diff --git a/tests/resources/typing/expressions/block lambdas/that are yielded.sdstest b/tests/resources/typing/expressions/block lambdas/that are yielded.sdstest new file mode 100644 index 000000000..f331c8afc --- /dev/null +++ b/tests/resources/typing/expressions/block lambdas/that are yielded.sdstest @@ -0,0 +1,24 @@ +package tests.typing.expressions.blockLambdas + +segment mySegment() -> ( + r: (p: String) -> (r: Int, s: String), + s: () -> (), + t: Int, +) { + // $TEST$ serialization (p: String) -> (r: Int, s: String) + yield r = »(p) { + yield r = 1; + yield s = ""; + }«; + + // $TEST$ serialization (p: $Unknown) -> (r: Int, s: String) + yield s = »(p) { + yield r = 1; + yield s = ""; + }«; + + // $TEST$ serialization (p: $Unknown) -> (r: Int, s: $Unknown) + yield t = »(p) { + yield r, yield s = 1; + }«; +} diff --git a/tests/resources/typing/expressions/block lambdas/with manifest types.sdstest b/tests/resources/typing/expressions/block lambdas/with manifest types.sdstest new file mode 100644 index 000000000..09fdbed82 --- /dev/null +++ b/tests/resources/typing/expressions/block lambdas/with manifest types.sdstest @@ -0,0 +1,14 @@ +package tests.typing.expressions.blockLambdas + +segment mySegment() { + // $TEST$ serialization (p: Int) -> (r: Int, s: String) + »(p: Int) { + yield r = 1; + yield s = ""; + }«; + + // $TEST$ serialization (p: vararg) -> (r: Int, s: $Unknown) + »(vararg p: String) { + yield r, yield s = 1; + }«; +} diff --git a/tests/resources/typing/expressions/calls/of annotations/main.sdstest b/tests/resources/typing/expressions/calls/of annotations/main.sdstest new file mode 100644 index 000000000..a5f06c078 --- /dev/null +++ b/tests/resources/typing/expressions/calls/of annotations/main.sdstest @@ -0,0 +1,8 @@ +package tests.typing.expressions.calls.ofAnnotations + +annotation MyAnnotation + +pipeline myPipeline { + // $TEST$ serialization $Unknown + »MyAnnotation()«; +} diff --git a/tests/resources/typing/expressions/calls/of block lambdas/main.sdstest b/tests/resources/typing/expressions/calls/of block lambdas/main.sdstest new file mode 100644 index 000000000..c6c4b418c --- /dev/null +++ b/tests/resources/typing/expressions/calls/of block lambdas/main.sdstest @@ -0,0 +1,14 @@ +package tests.typing.expressions.calls.ofBlockLambdas + +pipeline myPipeline { + // $TEST$ serialization (r: String) + »(() { + yield r = ""; + })()«; + + // $TEST$ serialization (r: String, s: Int) + »(() { + yield r = ""; + yield s = 1; + })()«; +} diff --git a/tests/resources/typing/expressions/calls/of callable types/main.sdstest b/tests/resources/typing/expressions/calls/of callable types/main.sdstest new file mode 100644 index 000000000..032cbec00 --- /dev/null +++ b/tests/resources/typing/expressions/calls/of callable types/main.sdstest @@ -0,0 +1,12 @@ +package tests.typing.expressions.calls.ofCallableTypes + +segment mySegment( + p1: () -> r: String, + p2: () -> (r: String, s: Int) +) { + // $TEST$ serialization (r: String) + »p1()«; + + // $TEST$ serialization (r: String, s: Int) + »p2()«; +} diff --git a/tests/resources/typing/expressions/calls/of classes/main.sdstest b/tests/resources/typing/expressions/calls/of classes/main.sdstest new file mode 100644 index 000000000..c38dbc76d --- /dev/null +++ b/tests/resources/typing/expressions/calls/of classes/main.sdstest @@ -0,0 +1,11 @@ +package tests.typing.expressions.calls.ofClasses + +class C() + +pipeline myPipeline { + // $TEST$ serialization C + »C()«; + + // $TEST$ serialization $Unknown + »C()()«; +} diff --git a/tests/resources/typing/expressions/calls/of enum variants/main.sdstest b/tests/resources/typing/expressions/calls/of enum variants/main.sdstest new file mode 100644 index 000000000..860194613 --- /dev/null +++ b/tests/resources/typing/expressions/calls/of enum variants/main.sdstest @@ -0,0 +1,13 @@ +package tests.typing.expressions.calls.ofEnumVariants + +enum E { + V +} + +pipeline myPipeline { + // $TEST$ serialization V + »E.V()«; + + // $TEST$ serialization $Unknown + »E.V()()«; +} diff --git a/tests/resources/typing/expressions/calls/of expression lambdas/main.sdstest b/tests/resources/typing/expressions/calls/of expression lambdas/main.sdstest new file mode 100644 index 000000000..7866aa463 --- /dev/null +++ b/tests/resources/typing/expressions/calls/of expression lambdas/main.sdstest @@ -0,0 +1,6 @@ +package tests.typing.expressions.calls.ofExpressionLambdas + +pipeline myPipeline { + // $TEST$ serialization (result: Int) + »(() -> 1)()«; +} diff --git a/tests/resources/typing/expressions/calls/of functions/main.sdstest b/tests/resources/typing/expressions/calls/of functions/main.sdstest new file mode 100644 index 000000000..e044b7e0a --- /dev/null +++ b/tests/resources/typing/expressions/calls/of functions/main.sdstest @@ -0,0 +1,12 @@ +package tests.typing.expressions.calls.ofFunctions + +fun f1() -> r: String +fun f2() -> (r: String, s: Int) + +pipeline myPipeline { + // $TEST$ serialization (r: String) + »f1()«; + + // $TEST$ serialization (r: String, s: Int) + »f2()«; +} diff --git a/tests/resources/typing/expressions/calls/of non-callable/main.sdstest b/tests/resources/typing/expressions/calls/of non-callable/main.sdstest new file mode 100644 index 000000000..373cefb48 --- /dev/null +++ b/tests/resources/typing/expressions/calls/of non-callable/main.sdstest @@ -0,0 +1,8 @@ +package tests.typing.expressions.calls.ofNonCallables + +enum MyEnum + +pipeline myPipeline { + // $TEST$ serialization $Unknown + »MyEnum()«; +} diff --git a/tests/resources/typing/expressions/calls/of segments/main.sdstest b/tests/resources/typing/expressions/calls/of segments/main.sdstest new file mode 100644 index 000000000..99f805821 --- /dev/null +++ b/tests/resources/typing/expressions/calls/of segments/main.sdstest @@ -0,0 +1,17 @@ +package tests.typing.expressions.calls.ofSegments + +segment s1() -> r: String{ + yield r = ""; +} +segment s2() -> (r: String, s: Int) { + yield r = ""; + yield s = 1; +} + +pipeline myPipeline { + // $TEST$ serialization (r: String) + »s1()«; + + // $TEST$ serialization (r: String, s: Int) + »s2()«; +} diff --git a/tests/resources/typing/expressions/calls/unresolved/main.sdstest b/tests/resources/typing/expressions/calls/unresolved/main.sdstest new file mode 100644 index 000000000..a88fb7f02 --- /dev/null +++ b/tests/resources/typing/expressions/calls/unresolved/main.sdstest @@ -0,0 +1,6 @@ +package tests.typing.expressions.calls.ofUnresolved + +pipeline myPipeline { + // $TEST$ serialization $Unknown + »unresolved()«; +} diff --git a/tests/resources/typing/expressions/expression lambdas/skip-that are passed as argument.sdstest b/tests/resources/typing/expressions/expression lambdas/skip-that are passed as argument.sdstest new file mode 100644 index 000000000..462e259d9 --- /dev/null +++ b/tests/resources/typing/expressions/expression lambdas/skip-that are passed as argument.sdstest @@ -0,0 +1,25 @@ +package tests.typing.expressions.expressionLambdas + +fun higherOrderFunction1(param: (p: String) -> (r: Int)) +fun higherOrderFunction2(param: () -> ()) +fun normalFunction(param: Int) + +segment mySegment() { + // $TEST$ serialization (p: String) -> (result: Int) + higherOrderFunction1(»(p) -> 1«); + + // $TEST$ serialization (p: String) -> (result: Int) + higherOrderFunction1(param = »(p) -> 1«); + + // $TEST$ serialization (p: $Unknown) -> (result: Int) + higherOrderFunction2(»(p) -> 1«); + + // $TEST$ serialization (p: $Unknown) -> (result: Int) + higherOrderFunction2(param = »(p) -> 1«); + + // $TEST$ serialization (p: $Unknown) -> (result: Int) + normalFunction(»(p) -> 1«); + + // $TEST$ serialization (p: $Unknown) -> (result: Int) + normalFunction(param = »(p) -> 1«); +} diff --git a/tests/resources/typing/expressions/expression lambdas/that are isolated.sdstest b/tests/resources/typing/expressions/expression lambdas/that are isolated.sdstest new file mode 100644 index 000000000..cb801ba2b --- /dev/null +++ b/tests/resources/typing/expressions/expression lambdas/that are isolated.sdstest @@ -0,0 +1,9 @@ +package tests.typing.expressions.expressionLambdas + +segment mySegment() { + // $TEST$ serialization (p: $Unknown) -> (result: Int) + »(p) -> 1«; + + // $TEST$ serialization (p: $Unknown) -> (result: Int) + val f = »(p) -> 1«; +} diff --git a/tests/resources/typing/expressions/expression lambdas/that are yielded.sdstest b/tests/resources/typing/expressions/expression lambdas/that are yielded.sdstest new file mode 100644 index 000000000..d21d5f414 --- /dev/null +++ b/tests/resources/typing/expressions/expression lambdas/that are yielded.sdstest @@ -0,0 +1,16 @@ +package tests.typing.expressions.expressionLambdas + +segment mySegment() -> ( + r: (p: String) -> (), + s: () -> (), + t: Int, +) { + // $TEST$ serialization (p: String) -> (result: Int) + yield r = »(p) -> 1«; + + // $TEST$ serialization (p: $Unknown) -> (result: Int) + yield s = »(p) -> 1«; + + // $TEST$ serialization (p: $Unknown) -> (result: Int) + yield t = »(p) -> 1«; +} diff --git a/tests/resources/typing/expressions/expression lambdas/with manifest types.sdstest b/tests/resources/typing/expressions/expression lambdas/with manifest types.sdstest new file mode 100644 index 000000000..64f8355a7 --- /dev/null +++ b/tests/resources/typing/expressions/expression lambdas/with manifest types.sdstest @@ -0,0 +1,9 @@ +package tests.typing.expressions.expressionLambdas + +segment mySegment() { + // $TEST$ serialization (p: Int) -> (result: Int) + »(p: Int) -> 1«; + + // $TEST$ serialization (p: vararg) -> (result: Int) + »(vararg p: String) -> 1«; +} diff --git a/tests/resources/typing/expressions/indexed accesses/main.sdstest b/tests/resources/typing/expressions/indexed accesses/main.sdstest new file mode 100644 index 000000000..a4a495889 --- /dev/null +++ b/tests/resources/typing/expressions/indexed accesses/main.sdstest @@ -0,0 +1,17 @@ +package tests.typing.expressions.indexedAccesses + +// $TEST$ equivalence_class elementType +segment mySegment1(vararg params: »Int«) { + // $TEST$ equivalence_class elementType + »params[0]«; +} + +segment mySegment2(params: String) { + // $TEST$ serialization $Unknown + »params[0]«; +} + +segment mySegment3() { + // $TEST$ serialization $Unknown + »unresolved[0]«; +} diff --git a/tests/resources/typing/expressions/member accesses/to enum variants/main.sdstest b/tests/resources/typing/expressions/member accesses/to enum variants/main.sdstest new file mode 100644 index 000000000..160c880f0 --- /dev/null +++ b/tests/resources/typing/expressions/member accesses/to enum variants/main.sdstest @@ -0,0 +1,10 @@ +package tests.typing.expressions.references.toEnumVariants + +enum MyEnum { + MyEnumVariant +} + +pipeline myPipeline { + // $TEST$ serialization $type + »MyEnum.MyEnumVariant«; +} diff --git a/tests/resources/typing/expressions/member accesses/to nested classes/main.sdstest b/tests/resources/typing/expressions/member accesses/to nested classes/main.sdstest new file mode 100644 index 000000000..69ea9fcfa --- /dev/null +++ b/tests/resources/typing/expressions/member accesses/to nested classes/main.sdstest @@ -0,0 +1,10 @@ +package tests.typing.expressions.memberAccesses.toNestedClasses + +class MyClass { + class MyNestedClass +} + +pipeline myPipeline { + // $TEST$ serialization $type + »MyClass.MyNestedClass«; +} diff --git a/tests/resources/typing/expressions/member accesses/to nested enums/main.sdstest b/tests/resources/typing/expressions/member accesses/to nested enums/main.sdstest new file mode 100644 index 000000000..9c2ed18c8 --- /dev/null +++ b/tests/resources/typing/expressions/member accesses/to nested enums/main.sdstest @@ -0,0 +1,10 @@ +package tests.typing.expressions.memberAccesses.toNestedEnums + +class MyClass { + enum MyNestedEnum +} + +pipeline myPipeline { + // $TEST$ serialization $type + »MyClass.MyNestedEnum«; +} diff --git a/tests/resources/typing/expressions/member accesses/to other/main.sdstest b/tests/resources/typing/expressions/member accesses/to other/main.sdstest new file mode 100644 index 000000000..32058b9f4 --- /dev/null +++ b/tests/resources/typing/expressions/member accesses/to other/main.sdstest @@ -0,0 +1,21 @@ +package tests.typing.expressions.memberAccesses.toOther + +class C { + // $TEST$ equivalence_class nonNullableMember + static attr »nonNullableMember«: Int + + // $TEST$ equivalence_class nullableMember + static attr »nullableMember«: Any? +} + +pipeline myPipeline { + // $TEST$ equivalence_class nonNullableMember + »C.nonNullableMember«; + // $TEST$ equivalence_class nullableMember + »C.nullableMember«; + + // $TEST$ serialization Int? + »C?.nonNullableMember«; + // $TEST$ serialization Any? + »C?.nullableMember«; +} diff --git a/tests/resources/typing/expressions/member accesses/unresolved/main.sdstest b/tests/resources/typing/expressions/member accesses/unresolved/main.sdstest new file mode 100644 index 000000000..60aa0b8f2 --- /dev/null +++ b/tests/resources/typing/expressions/member accesses/unresolved/main.sdstest @@ -0,0 +1,11 @@ +package tests.typing.expressions.memberAccesses.unresolved + +class C + +pipeline myPipeline { + // $TEST$ serialization $Unknown + »C.unresolved«; + + // $TEST$ serialization $Unknown + »C?.unresolved«; +} diff --git a/tests/resources/typing/expressions/operations/arithmetic/main.sdstest b/tests/resources/typing/expressions/operations/arithmetic/main.sdstest new file mode 100644 index 000000000..99d6c7b8e --- /dev/null +++ b/tests/resources/typing/expressions/operations/arithmetic/main.sdstest @@ -0,0 +1,36 @@ +package tests.typing.operations.arithmetic + +pipeline myPipeline { + // $TEST$ serialization Int + val additionIntInt = »1 + 1«; + // $TEST$ serialization Int + val subtractionIntInt = »1 - 1«; + // $TEST$ serialization Int + val multiplicationIntInt = »1 * 1«; + // $TEST$ serialization Int + val divisionIntInt = »1 / 1«; + // $TEST$ serialization Int + val negationInt = »-1«; + + // $TEST$ serialization Float + val additionIntFloat = »1 + 1.0«; + // $TEST$ serialization Float + val subtractionIntFloat = »1 - 1.0«; + // $TEST$ serialization Float + val multiplicationIntFloat = »1 * 1.0«; + // $TEST$ serialization Float + val divisionIntFloat = »1 / 1.0«; + // $TEST$ serialization Float + val negationFloat = »-1.0«; + + // $TEST$ serialization Float + val additionInvalid = »true + true«; + // $TEST$ serialization Float + val subtractionInvalid = »true - true«; + // $TEST$ serialization Float + val multiplicationInvalid = »true * true«; + // $TEST$ serialization Float + val divisionInvalid = »true / true«; + // $TEST$ serialization Float + val negationInvalid = »-true«; +} diff --git a/tests/resources/typing/expressions/operations/comparison/main.sdstest b/tests/resources/typing/expressions/operations/comparison/main.sdstest new file mode 100644 index 000000000..50186b978 --- /dev/null +++ b/tests/resources/typing/expressions/operations/comparison/main.sdstest @@ -0,0 +1,21 @@ +package tests.typing.operations.comparison + +pipeline myPipeline { + // $TEST$ serialization Boolean + val lessThan = »1 < 1«; + // $TEST$ serialization Boolean + val lessThanOrEquals = »1 <= 1«; + // $TEST$ serialization Boolean + val greaterThanOrEquals = »1 >= 1«; + // $TEST$ serialization Boolean + val greaterThan = »1 > 1«; + + // $TEST$ serialization Boolean + val lessThanInvalid = »true < true«; + // $TEST$ serialization Boolean + val lessThanOrEqualsInvalid = »true <= true«; + // $TEST$ serialization Boolean + val greaterThanOrEqualsInvalid = »true >= true«; + // $TEST$ serialization Boolean + val greaterThanInvalid = »true > true«; +} diff --git a/tests/resources/typing/expressions/operations/elvis/non nullable left operand.sdstest b/tests/resources/typing/expressions/operations/elvis/non nullable left operand.sdstest new file mode 100644 index 000000000..a7d726e3c --- /dev/null +++ b/tests/resources/typing/expressions/operations/elvis/non nullable left operand.sdstest @@ -0,0 +1,18 @@ +package tests.typing.operations.elvis + +fun intOrNull() -> a: Int? + +pipeline elvisWithNonNullableLeftOperand { + // $TEST$ equivalence_class leftOperand + »1«; + // $TEST$ equivalence_class leftOperand + »1 ?: intOrNull()«; + // $TEST$ equivalence_class leftOperand + »1 ?: 1«; + // $TEST$ equivalence_class leftOperand + »1 ?: 1.0«; + // $TEST$ equivalence_class leftOperand + »1 ?: ""«; + // $TEST$ equivalence_class leftOperand + »1 ?: null«; +} diff --git a/tests/resources/typing/expressions/operations/elvis/skip-nullable left operand.sdstest b/tests/resources/typing/expressions/operations/elvis/skip-nullable left operand.sdstest new file mode 100644 index 000000000..16b711ee4 --- /dev/null +++ b/tests/resources/typing/expressions/operations/elvis/skip-nullable left operand.sdstest @@ -0,0 +1,19 @@ +package tests.typing.operations.elvis + +fun intOrNull() -> a: Int? +fun stringOrNull() -> s: String? + +pipeline elvisWithNullableLeftOperand { + // $TEST$ serialization Int? + »intOrNull() ?: intOrNull()«; + // $TEST$ serialization Int? + »intOrNull() ?: null«; + // $TEST$ serialization Int + »intOrNull() ?: 1«; + // $TEST$ serialization Number + »intOrNull() ?: 1.0«; + // $TEST$ serialization Any + »intOrNull() ?: ""«; + // $TEST$ serialization Any? + »intOrNull() ?: stringOrNull()«; +} diff --git a/tests/resources/typing/expressions/operations/equality/main.sdstest b/tests/resources/typing/expressions/operations/equality/main.sdstest new file mode 100644 index 000000000..5635da94f --- /dev/null +++ b/tests/resources/typing/expressions/operations/equality/main.sdstest @@ -0,0 +1,13 @@ +package tests.typing.operations.equality + +pipeline myPipeline { + // $TEST$ serialization Boolean + val equals = (»1 == 1«); + // $TEST$ serialization Boolean + val notEquals = (»1 != 1«); + + // $TEST$ serialization Boolean + val strictlyEquals = (»1 === 1«); + // $TEST$ serialization Boolean + val notStrictlyEquals = (»1 !== 1«); +} diff --git a/tests/resources/typing/expressions/operations/logical/main.sdstest b/tests/resources/typing/expressions/operations/logical/main.sdstest new file mode 100644 index 000000000..146f7e0c9 --- /dev/null +++ b/tests/resources/typing/expressions/operations/logical/main.sdstest @@ -0,0 +1,17 @@ +package tests.typing.operations.logical + +pipeline myPipeline { + // $TEST$ serialization Boolean + val conjunction = »true and true«; + // $TEST$ serialization Boolean + val disjunction = »true or true«; + // $TEST$ serialization Boolean + val negation = »not true«; + + // $TEST$ serialization Boolean + val conjunctionInvalid = »1 and 1«; + // $TEST$ serialization Boolean + val disjunctionInvalid = »1.0 or 1.0«; + // $TEST$ serialization Boolean + val negationInvalid = »not "true"«; +} diff --git a/tests/resources/typing/expressions/parenthesized expressions/main.sdstest b/tests/resources/typing/expressions/parenthesized expressions/main.sdstest new file mode 100644 index 000000000..7695ced74 --- /dev/null +++ b/tests/resources/typing/expressions/parenthesized expressions/main.sdstest @@ -0,0 +1,23 @@ +package tests.typing.expressions.parenthesizedExpressions + +pipeline myPipeline { + // $TEST$ equivalence_class boolean + // $TEST$ equivalence_class boolean + »(»true«)«; + + // $TEST$ equivalence_class float + // $TEST$ equivalence_class float + »(»1.0«)«; + + // $TEST$ equivalence_class int + // $TEST$ equivalence_class int + »(»1«)«; + + // $TEST$ equivalence_class null + // $TEST$ equivalence_class null + »(»null«)«; + + // $TEST$ equivalence_class string + // $TEST$ equivalence_class string + »(»""«)«; +} diff --git a/tests/resources/typing/expressions/references/to global classes/main.sdstest b/tests/resources/typing/expressions/references/to global classes/main.sdstest new file mode 100644 index 000000000..1d2be92c8 --- /dev/null +++ b/tests/resources/typing/expressions/references/to global classes/main.sdstest @@ -0,0 +1,8 @@ +package tests.typing.expressions.references.toGlobalClasses + +class MyClass + +pipeline myPipeline { + // $TEST$ serialization $type + »MyClass«; +} diff --git a/tests/resources/typing/expressions/references/to global enums/main.sdstest b/tests/resources/typing/expressions/references/to global enums/main.sdstest new file mode 100644 index 000000000..03b517568 --- /dev/null +++ b/tests/resources/typing/expressions/references/to global enums/main.sdstest @@ -0,0 +1,8 @@ +package tests.typing.expressions.references.toGlobalEnums + +enum MyEnum + +pipeline myPipeline { + // $TEST$ serialization $type + »MyEnum«; +} diff --git a/tests/resources/typing/expressions/references/to other/main.sdstest b/tests/resources/typing/expressions/references/to other/main.sdstest new file mode 100644 index 000000000..e284e171b --- /dev/null +++ b/tests/resources/typing/expressions/references/to other/main.sdstest @@ -0,0 +1,13 @@ +package tests.typing.expressions.references.toOther + +segment mySegment( + // $TEST$ equivalence_class p1 + »p1«: Int, + // $TEST$ equivalence_class p2 + »p2«: String +) { + // $TEST$ equivalence_class p1 + »p1«; + // $TEST$ equivalence_class p2 + »p2«; +} diff --git a/tests/resources/typing/expressions/references/unresolved/main.sdstest b/tests/resources/typing/expressions/references/unresolved/main.sdstest new file mode 100644 index 000000000..7d51fa351 --- /dev/null +++ b/tests/resources/typing/expressions/references/unresolved/main.sdstest @@ -0,0 +1,6 @@ +package tests.typing.expressions.references.unresolved + +pipeline myPipeline { + // $TEST$ serialization $Unknown + »unresolved«; +} diff --git a/tests/resources/typing/skip-assignees/blockLambdaResults.sdstest b/tests/resources/typing/skip-assignees/blockLambdaResults.sdstest deleted file mode 100644 index 06803536f..000000000 --- a/tests/resources/typing/skip-assignees/blockLambdaResults.sdstest +++ /dev/null @@ -1,10 +0,0 @@ -package tests.typingassignees.blockLambdaResults - -fun f(p: Any) - -pipeline myPipeline { - f(() { - yield r = 1; - yield s = ""; - }); -} diff --git a/tests/resources/typing/skip-assignees/placeholders.sdstest b/tests/resources/typing/skip-assignees/placeholders.sdstest deleted file mode 100644 index 52a255f6e..000000000 --- a/tests/resources/typing/skip-assignees/placeholders.sdstest +++ /dev/null @@ -1,8 +0,0 @@ -package tests.typingassignees.placeholders - -fun f(p: Any) - -pipeline myPipeline { - val a = 1; - val b = ""; -} diff --git a/tests/resources/typing/skip-assignees/yields.sdstest b/tests/resources/typing/skip-assignees/yields.sdstest deleted file mode 100644 index ca40b830b..000000000 --- a/tests/resources/typing/skip-assignees/yields.sdstest +++ /dev/null @@ -1,8 +0,0 @@ -package tests.typingassignees.yields - -fun f(p: Any) - -step myStep() -> (r: Int, s: String) { - yield r = 1; - yield s = ""; -} diff --git a/tests/resources/typing/skip-declarations/attributes.sdstest b/tests/resources/typing/skip-declarations/attributes.sdstest deleted file mode 100644 index ac6208b01..000000000 --- a/tests/resources/typing/skip-declarations/attributes.sdstest +++ /dev/null @@ -1,5 +0,0 @@ -package tests.typingdeclarations.attributes - -class C { - attr a: Int -} diff --git a/tests/resources/typing/skip-declarations/classes.sdstest b/tests/resources/typing/skip-declarations/classes.sdstest deleted file mode 100644 index 97d8dab9f..000000000 --- a/tests/resources/typing/skip-declarations/classes.sdstest +++ /dev/null @@ -1,3 +0,0 @@ -package tests.typingdeclarations.classes - -class C diff --git a/tests/resources/typing/skip-declarations/enumVariants.sdstest b/tests/resources/typing/skip-declarations/enumVariants.sdstest deleted file mode 100644 index 782f9c0f0..000000000 --- a/tests/resources/typing/skip-declarations/enumVariants.sdstest +++ /dev/null @@ -1,7 +0,0 @@ -package tests.typingdeclarations.enumVariants - -enum E { - V -} - -step myStep(v: E.V) {} diff --git a/tests/resources/typing/skip-declarations/enums.sdstest b/tests/resources/typing/skip-declarations/enums.sdstest deleted file mode 100644 index d8e049653..000000000 --- a/tests/resources/typing/skip-declarations/enums.sdstest +++ /dev/null @@ -1,5 +0,0 @@ -package tests.typingdeclarations.enums - -enum E - -step myStep(e: E) {} diff --git a/tests/resources/typing/skip-declarations/functions.sdstest b/tests/resources/typing/skip-declarations/functions.sdstest deleted file mode 100644 index e1440dff3..000000000 --- a/tests/resources/typing/skip-declarations/functions.sdstest +++ /dev/null @@ -1,3 +0,0 @@ -package tests.typingdeclarations.functions - -fun f(a: Int, b: String) -> (r: String, s: Int) diff --git a/tests/resources/typing/skip-declarations/parameters.sdstest b/tests/resources/typing/skip-declarations/parameters.sdstest deleted file mode 100644 index 4110f271d..000000000 --- a/tests/resources/typing/skip-declarations/parameters.sdstest +++ /dev/null @@ -1,17 +0,0 @@ -package tests.typingdeclarations.parameters - -fun f(parameter: (a: String) -> r: String) - -step myStepWithNormalParameter(a: Int, b: String) {} -step myStepWithVariadicParameter(vararg param: Int) {} - -step myStepWithLambdas() -> ( - r: (a: String) -> r: String, - s: (a: String) -> r: String -) { - f((a) -> ""); - f((b) { yield r = ""; }); - - yield r = (c) -> ""; - yield s = (d) { yield r = ""; }; -} diff --git a/tests/resources/typing/skip-declarations/results.sdstest b/tests/resources/typing/skip-declarations/results.sdstest deleted file mode 100644 index 04efa63f2..000000000 --- a/tests/resources/typing/skip-declarations/results.sdstest +++ /dev/null @@ -1,6 +0,0 @@ -package tests.typingdeclarations.results - -step myStep() -> (r: Int, s: String) { - yield r = 1; - yield s = ""; -} diff --git a/tests/resources/typing/skip-declarations/steps.sdstest b/tests/resources/typing/skip-declarations/steps.sdstest deleted file mode 100644 index 891a36fd2..000000000 --- a/tests/resources/typing/skip-declarations/steps.sdstest +++ /dev/null @@ -1,6 +0,0 @@ -package tests.typingdeclarations.steps - -step s(a: Int, b: String) -> (r: String, s: Int) { - yield r = ""; - yield s = 1; -} diff --git a/tests/resources/typing/skip-expressions/arguments.sdstest b/tests/resources/typing/skip-expressions/arguments.sdstest deleted file mode 100644 index 484674139..000000000 --- a/tests/resources/typing/skip-expressions/arguments.sdstest +++ /dev/null @@ -1,8 +0,0 @@ -package tests.typingexpressions.parenthesizedExpressions - -fun f(x: Any?) - -pipeline myPipeline { - f(1); - f(x = ""); -} diff --git a/tests/resources/typing/skip-expressions/blockLambdas.sdstest b/tests/resources/typing/skip-expressions/blockLambdas.sdstest deleted file mode 100644 index 0b48112d5..000000000 --- a/tests/resources/typing/skip-expressions/blockLambdas.sdstest +++ /dev/null @@ -1,49 +0,0 @@ -package tests.typingexpressions.blockLambdas - -fun f( - parameter: (a: String, b: Int) -> (r: String, s: Int) -) - -step lambdasWithExplicitParameterTypes() -> ( - result: (a: String, b: Int) -> (r: String, s: Int) -) { - val myLambda = (a: Int, b: String) { - yield r = 1; - yield s = ""; - }; - yield result = (a: Int, b: String) { - yield r = 1; - yield s = ""; - }; - f( - (a: Int, b: String) { - yield r = 1; - yield s = ""; - } - ); -} - -step lambdasWithExplicitVariadicType() { - val myLambda = (a: Int, vararg b: String) { - yield r = 1; - yield s = ""; - }; -} - -step yieldedLambda() -> ( - result: (a: String, b: Int) -> (r: String, s: Int) -) { - yield result = (a, b) { - yield r = 1; - yield s = ""; - }; -} - -step argumentLambda() { - f( - (a, b) { - yield r = 1; - yield s = ""; - } - ); -} diff --git a/tests/resources/typing/skip-expressions/calls.sdstest b/tests/resources/typing/skip-expressions/calls.sdstest deleted file mode 100644 index 2e323bf9d..000000000 --- a/tests/resources/typing/skip-expressions/calls.sdstest +++ /dev/null @@ -1,37 +0,0 @@ -package tests.typingexpressions.calls - -class C() -enum E { - V(a: Int) -} -fun f1() -> r: String -fun f2() -> (r: String, s: Int) -step s1() -> r: String{ - yield r = ""; -} -step s2() -> (r: String, s: Int) { - yield r = ""; - yield s = 1; -} - -step mySteps( - p1: () -> r: String, - p2: () -> (r: String, s: Int) -) { - val classCall = C(); - val callableTypeCall1 = p1(); - val callableTypeCall2 = p2(); - val enumVariantCall = E.V(1); - val functionCall1 = f1(); - val functionCall2 = f2(); - val blockLambdaCall1 = (() { - yield r = ""; - })(); - val blockLambdaCall2 = (() { - yield r = ""; - yield s = 1; - })(); - val expressionLambdaCall = (() -> 1)(); - val stepCall1 = s1(); - val stepCall2 = s2(); -} diff --git a/tests/resources/typing/skip-expressions/expressionLambdas.sdstest b/tests/resources/typing/skip-expressions/expressionLambdas.sdstest deleted file mode 100644 index 158bddc91..000000000 --- a/tests/resources/typing/skip-expressions/expressionLambdas.sdstest +++ /dev/null @@ -1,27 +0,0 @@ -package tests.typingexpressions.expressionLambdas - -fun f( - parameter: (a: String, b: Int) -> r: String -) - -step lambdasWithExplicitParameterTypes() -> ( - result: (a: String, b: Int) -> r: String -) { - val myLambda = (a: Int, b: String) -> 1; - yield result = (a: Int, b: String) -> 1; - f((a: Int, b: String) -> 1); -} - -step lambdasWithExplicitVariadicType() { - val myLambda = (a: Int, vararg b: String) -> 1; -} - -step yieldedLambda() -> ( - result: (a: String, b: Int) -> r: String -) { - yield result = (a, b) -> 1; -} - -step argumentLambda() { - f((a, b) -> 1); -} diff --git a/tests/resources/typing/skip-expressions/indexedAccesses.sdstest b/tests/resources/typing/skip-expressions/indexedAccesses.sdstest deleted file mode 100644 index 572a7b24d..000000000 --- a/tests/resources/typing/skip-expressions/indexedAccesses.sdstest +++ /dev/null @@ -1,17 +0,0 @@ -package tests.typingexpressions.indexedAccesses - -step myStep1(vararg params: Int) { - params[0]; -} - -step myStep2(vararg params: String) { - params[0]; -} - -step myStep3(params: String) { - params[0]; -} - -step myStep4() { - unresolved[0]; -} diff --git a/tests/resources/typing/skip-expressions/memberAccesses.sdstest b/tests/resources/typing/skip-expressions/memberAccesses.sdstest deleted file mode 100644 index 7d6d1da03..000000000 --- a/tests/resources/typing/skip-expressions/memberAccesses.sdstest +++ /dev/null @@ -1,19 +0,0 @@ -package tests.typingexpressions.memberAccesses - -class C { - static attr a: Int - static attr b: String - static attr c: Any? -} - -pipeline myPipeline { - C.a; - C.b; - C.c; - C.unresolved; - - C?.a; - C?.b; - C?.c; - C?.unresolved; -} diff --git a/tests/resources/typing/skip-expressions/operations/arithmetic.sdstest b/tests/resources/typing/skip-expressions/operations/arithmetic.sdstest deleted file mode 100644 index 8bc95c317..000000000 --- a/tests/resources/typing/skip-expressions/operations/arithmetic.sdstest +++ /dev/null @@ -1,21 +0,0 @@ -package tests.typingoperations.arithmetic - -pipeline myPipeline { - val additionIntInt = (1 + 1); - val subtractionIntInt = (1 - 1); - val multiplicationIntInt = (1 * 1); - val divisionIntInt = (1 / 1); - val negationInt = (-1); - - val additionIntFloat = (1 + 1.0); - val subtractionIntFloat = (1 - 1.0); - val multiplicationIntFloat = (1 * 1.0); - val divisionIntFloat = (1 / 1.0); - val negationFloat = (-1.0); - - val additionInvalid = (true + true); - val subtractionInvalid = (true - true); - val multiplicationInvalid = (true * true); - val divisionInvalid = (true / true); - val negationInvalid = (-true); -} diff --git a/tests/resources/typing/skip-expressions/operations/comparison.sdstest b/tests/resources/typing/skip-expressions/operations/comparison.sdstest deleted file mode 100644 index a4200c411..000000000 --- a/tests/resources/typing/skip-expressions/operations/comparison.sdstest +++ /dev/null @@ -1,12 +0,0 @@ -package tests.typingoperations.comparison - -pipeline myPipeline { - val lessThan = (1 < 1); - val lessThanOrEquals = (1 <= 1); - val greaterThanOrEquals = (1 >= 1); - val greaterThan = (1 > 1); - val lessThanInvalid = (true < true); - val lessThanOrEqualsInvalid = (true <= true); - val greaterThanOrEqualsInvalid = (true >= true); - val greaterThanInvalid = (true > true); -} diff --git a/tests/resources/typing/skip-expressions/operations/elvis.sdstest b/tests/resources/typing/skip-expressions/operations/elvis.sdstest deleted file mode 100644 index 9616e92fb..000000000 --- a/tests/resources/typing/skip-expressions/operations/elvis.sdstest +++ /dev/null @@ -1,21 +0,0 @@ -package tests.typingoperations.elvis - -fun intOrNull() -> a: Int? -fun stringOrNull() -> s: String? - -pipeline elvisWithNonNullableLeftOperand { - 1 ?: intOrNull(); - 1 ?: 1; - 1 ?: 1.0; - 1 ?: ""; - 1 ?: null; -} - -pipeline elvisWithNullableLeftOperand { - val intOrNullElseIntOrNull = intOrNull() ?: intOrNull(); - val intOrNullElseNull = intOrNull() ?: null; - val intOrNullElseInt = intOrNull() ?: 1; - val intOrNullElseFloat = intOrNull() ?: 1.0; - val intOrNullElseString = intOrNull() ?: ""; - val intOrNullElseStringOrNull = intOrNull() ?: stringOrNull(); -} diff --git a/tests/resources/typing/skip-expressions/operations/equality.sdstest b/tests/resources/typing/skip-expressions/operations/equality.sdstest deleted file mode 100644 index 6278e2b63..000000000 --- a/tests/resources/typing/skip-expressions/operations/equality.sdstest +++ /dev/null @@ -1,6 +0,0 @@ -package tests.typingoperations.equality - -pipeline myPipeline { - val equals = (1 == 1); - val notEquals = (1 != 1); -} diff --git a/tests/resources/typing/skip-expressions/operations/logical.sdstest b/tests/resources/typing/skip-expressions/operations/logical.sdstest deleted file mode 100644 index e789df0d9..000000000 --- a/tests/resources/typing/skip-expressions/operations/logical.sdstest +++ /dev/null @@ -1,10 +0,0 @@ -package tests.typingoperations.logical - -pipeline myPipeline { - val conjunction = (true and true); - val disjunction = (true or true); - val negation = (not true); - val conjunctionInvalid = (1 and 1); - val disjunctionInvalid = (1.0 or 1.0); - val negationInvalid = (not "true"); -} diff --git a/tests/resources/typing/skip-expressions/operations/strictEquality.sdstest b/tests/resources/typing/skip-expressions/operations/strictEquality.sdstest deleted file mode 100644 index 7663b79cb..000000000 --- a/tests/resources/typing/skip-expressions/operations/strictEquality.sdstest +++ /dev/null @@ -1,6 +0,0 @@ -package tests.typingoperations.strictEquality - -pipeline myPipeline { - val strictlyEquals = (1 === 1); - val notStrictlyEquals = (1 !== 1); -} diff --git a/tests/resources/typing/skip-expressions/parenthesizedExpressions.sdstest b/tests/resources/typing/skip-expressions/parenthesizedExpressions.sdstest deleted file mode 100644 index 435022bff..000000000 --- a/tests/resources/typing/skip-expressions/parenthesizedExpressions.sdstest +++ /dev/null @@ -1,6 +0,0 @@ -package tests.typingexpressions.parenthesizedExpressions - -pipeline myPipeline { - (1); - (""); -} diff --git a/tests/resources/typing/skip-expressions/references.sdstest b/tests/resources/typing/skip-expressions/references.sdstest deleted file mode 100644 index 7f3a1899f..000000000 --- a/tests/resources/typing/skip-expressions/references.sdstest +++ /dev/null @@ -1,10 +0,0 @@ -package tests.typingexpressions.references - -pipeline myPipeline { - val a = 1; - val b = ""; - - a; - b; - unresolved; -} diff --git a/tests/resources/typing/skip-types/callableTypes.sdstest b/tests/resources/typing/skip-types/callableTypes.sdstest deleted file mode 100644 index 46d98e37b..000000000 --- a/tests/resources/typing/skip-types/callableTypes.sdstest +++ /dev/null @@ -1,3 +0,0 @@ -package tests.typingtypes.callableTypes - -fun myFun(f: (p1: Int, p2: String) -> (r1: Int, r2: String)) diff --git a/tests/resources/typing/skip-types/memberTypes.sdstest b/tests/resources/typing/skip-types/memberTypes.sdstest deleted file mode 100644 index e4b635cd9..000000000 --- a/tests/resources/typing/skip-types/memberTypes.sdstest +++ /dev/null @@ -1,9 +0,0 @@ -package tests.typingtypes.memberTypes - -enum MyEnum { - MyVariant1 - MyVariant2 -} - -fun nonNullableMemberTypes(a: MyEnum.MyVariant1, b: MyEnum.MyVariant2) -fun nullableMemberTypes(a: MyEnum.MyVariant1?, b: MyEnum.MyVariant2?) diff --git a/tests/resources/typing/skip-types/namedTypes.sdstest b/tests/resources/typing/skip-types/namedTypes.sdstest deleted file mode 100644 index 8f64311b6..000000000 --- a/tests/resources/typing/skip-types/namedTypes.sdstest +++ /dev/null @@ -1,4 +0,0 @@ -package tests.typingtypes.namedTypes - -fun nonNullableNamedTypes(a: Int, b: String) -fun nullableNamedTypes(a: Int?, b: String?) diff --git a/tests/resources/typing/skip-types/parenthesizedTypes.sdstest b/tests/resources/typing/skip-types/parenthesizedTypes.sdstest deleted file mode 100644 index c35d55b35..000000000 --- a/tests/resources/typing/skip-types/parenthesizedTypes.sdstest +++ /dev/null @@ -1,3 +0,0 @@ -package tests.typingtypes.parenthesizedTypes - -fun myFun(a: (Int), b: (String)) diff --git a/tests/resources/typing/skip-types/unionTypes.sdstest b/tests/resources/typing/skip-types/unionTypes.sdstest deleted file mode 100644 index 9850bd075..000000000 --- a/tests/resources/typing/skip-types/unionTypes.sdstest +++ /dev/null @@ -1,3 +0,0 @@ -package tests.typingtypes.unionTypes - -fun myFun(a: union) diff --git a/tests/resources/typing/types/callable types/main.sdstest b/tests/resources/typing/types/callable types/main.sdstest new file mode 100644 index 000000000..d38058e65 --- /dev/null +++ b/tests/resources/typing/types/callable types/main.sdstest @@ -0,0 +1,16 @@ +package tests.typing.types.callableTypes + +// $TEST$ serialization () -> () +fun myFunction1(f: »() -> ()«) + +// $TEST$ serialization () -> (r1: Int, r2: String) +fun myFunction2(f: »() -> (r1: Int, r2: String)«) + +// $TEST$ serialization (p1: Int, p2: String) -> () +fun myFunction3(f: »(p1: Int, p2: String) -> ()«) + +// $TEST$ serialization (p1: Int, p2: String) -> (r1: Int, r2: String) +fun myFunction4(f: »(p1: Int, p2: String) -> (r1: Int, r2: String)«) + +// $TEST$ serialization (p1: $Unknown) -> (r1: $Unknown) +fun myFunction5(f: »(p1) -> (r1)«) diff --git a/tests/resources/typing/types/member types/main.sdstest b/tests/resources/typing/types/member types/main.sdstest new file mode 100644 index 000000000..637055d12 --- /dev/null +++ b/tests/resources/typing/types/member types/main.sdstest @@ -0,0 +1,36 @@ +package tests.typing.types.memberTypes + +class MyClass { + // $TEST$ equivalence_class myNestedClass + class »MyNestedClass« + + // $TEST$ equivalence_class myNestedEnum + enum »MyNestedEnum« +} + +enum MyEnum { + // $TEST$ equivalence_class myEnumVariant + »MyEnumVariant« +} + +fun nonNullableMemberTypes( + // $TEST$ equivalence_class myNestedClass + a: »MyClass.MyNestedClass«, + // $TEST$ equivalence_class myNestedEnum + b: »MyClass.MyNestedEnum«, + // $TEST$ equivalence_class myEnumVariant + d: »MyEnum.MyEnumVariant«, + // $TEST$ serialization $Unknown + e: »MyEnum.unresolved«, +) + +fun nullableMemberTypes( + // $TEST$ serialization MyNestedClass? + a: »MyClass.MyNestedClass?«, + // $TEST$ serialization MyNestedEnum? + b: »MyClass.MyNestedEnum?«, + // $TEST$ serialization MyEnumVariant? + d: »MyEnum.MyEnumVariant?«, + // $TEST$ serialization $Unknown + e: »MyEnum.unresolved?«, +) diff --git a/tests/resources/typing/types/named types/main.sdstest b/tests/resources/typing/types/named types/main.sdstest new file mode 100644 index 000000000..7447e4fcd --- /dev/null +++ b/tests/resources/typing/types/named types/main.sdstest @@ -0,0 +1,23 @@ +package tests.typing.types.namedTypes + +class MyClass + +enum MyEnum + +fun nonNullableNamedTypes( + // $TEST$ serialization MyClass + a: »MyClass«, + // $TEST$ serialization MyEnum + b: »MyEnum«, + // $TEST$ serialization $Unknown + c: »unresolved«, +) + +fun nullableNamedTypes( + // $TEST$ serialization MyClass? + a: »MyClass?«, + // $TEST$ serialization MyEnum? + b: »MyEnum?«, + // $TEST$ serialization $Unknown + c: »unresolved?«, +) diff --git a/tests/resources/typing/types/union types/main.sdstest b/tests/resources/typing/types/union types/main.sdstest new file mode 100644 index 000000000..92d0edb66 --- /dev/null +++ b/tests/resources/typing/types/union types/main.sdstest @@ -0,0 +1,7 @@ +package tests.typing.types.unionTypes + +// $TEST$ serialization union<> +fun myFunction(a: »union<>«) + +// $TEST$ serialization union +fun myFunction(a: »union«)