Skip to content

Commit

Permalink
fix: error in Python generator for assignments with class/enum varian…
Browse files Browse the repository at this point in the history
…t call as RHS (#977)

Closes #975

### Summary of Changes

The code generator no longer throws a `RangeError: Invalid array length`
for assignments that have a class or enum variant call as the
right-hand-side.
  • Loading branch information
lars-reimann authored Apr 3, 2024
1 parent 1003e6c commit 46b2bb2
Show file tree
Hide file tree
Showing 44 changed files with 144 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class SafeDsCallGraphComputer {
this.astNodeLocator = services.workspace.AstNodeLocator;
this.nodeMapper = services.helpers.NodeMapper;
this.partialEvaluator = services.evaluation.PartialEvaluator;
this.typeComputer = services.types.TypeComputer;
this.typeComputer = services.typing.TypeComputer;

this.callCache = new WorkspaceCache(services.shared);
this.callGraphCache = new WorkspaceCache(services.shared);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class SafeDsMarkdownGenerator {
constructor(services: SafeDsServices) {
this.builtinAnnotations = services.builtins.Annotations;
this.documentationProvider = services.documentation.DocumentationProvider;
this.typeComputer = services.types.TypeComputer;
this.typeComputer = services.typing.TypeComputer;
}

generate(documents: LangiumDocument[], options: GenerateOptions): TextDocument[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ import { SafeDsPartialEvaluator } from '../partialEvaluation/safe-ds-partial-eva
import { SafeDsServices } from '../safe-ds-module.js';
import { SafeDsPurityComputer } from '../purity/safe-ds-purity-computer.js';
import { FileRead, ImpurityReason } from '../purity/model.js';
import { SafeDsTypeComputer } from '../typing/safe-ds-type-computer.js';
import { NamedTupleType } from '../typing/model.js';

export const CODEGEN_PREFIX = '__gen_';
const BLOCK_LAMBDA_PREFIX = `${CODEGEN_PREFIX}block_lambda_`;
Expand Down Expand Up @@ -174,12 +176,14 @@ export class SafeDsPythonGenerator {
private readonly nodeMapper: SafeDsNodeMapper;
private readonly partialEvaluator: SafeDsPartialEvaluator;
private readonly purityComputer: SafeDsPurityComputer;
private readonly typeComputer: SafeDsTypeComputer;

constructor(services: SafeDsServices) {
this.builtinAnnotations = services.builtins.Annotations;
this.nodeMapper = services.helpers.NodeMapper;
this.partialEvaluator = services.evaluation.PartialEvaluator;
this.purityComputer = services.purity.PurityComputer;
this.typeComputer = services.typing.TypeComputer;
}

generate(document: LangiumDocument, options: GenerateOptions): TextDocument[] {
Expand Down Expand Up @@ -616,10 +620,9 @@ export class SafeDsPythonGenerator {
frame: GenerationInfoFrame,
generateLambda: boolean,
): CompositeGeneratorNode {
const requiredAssignees = isSdsCall(assignment.expression)
? getAbstractResults(this.nodeMapper.callToCallable(assignment.expression)).length
: /* c8 ignore next */
1;
const rhsType = this.typeComputer.computeType(assignment.expression);
const requiredAssignees = rhsType instanceof NamedTupleType ? rhsType.length : 1;

const assignees = getAssignees(assignment);
if (assignees.some((value) => !isSdsWildcard(value))) {
const actualAssignees = assignees.map(this.generateAssignee);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export class SafeDsNodeMapper {
private readonly typeComputer: () => SafeDsTypeComputer;

constructor(services: SafeDsServices) {
this.typeComputer = () => services.types.TypeComputer;
this.typeComputer = () => services.typing.TypeComputer;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class SafeDsInlayHintProvider extends AbstractInlayHintProvider {

this.documentationProvider = services.documentation.DocumentationProvider;
this.nodeMapper = services.helpers.NodeMapper;
this.typeComputer = services.types.TypeComputer;
this.typeComputer = services.typing.TypeComputer;
}

override computeInlayHint(node: AstNode, acceptor: InlayHintAcceptor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class SafeDsNodeInfoProvider {

constructor(services: SafeDsServices) {
this.builtinAnnotations = services.builtins.Annotations;
this.typeComputer = services.types.TypeComputer;
this.typeComputer = services.typing.TypeComputer;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class SafeDsSignatureHelpProvider implements SignatureHelpProvider {
constructor(services: SafeDsServices) {
this.documentationProvider = services.documentation.DocumentationProvider;
this.nodeMapper = services.helpers.NodeMapper;
this.typeComputer = services.types.TypeComputer;
this.typeComputer = services.typing.TypeComputer;
}

provideSignatureHelp(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class SafeDsTypeHierarchyProvider extends AbstractTypeHierarchyProvider {

constructor(services: SafeDsServices) {
super(services);
this.classHierarchy = services.types.ClassHierarchy;
this.classHierarchy = services.typing.ClassHierarchy;
this.nodeKindProvider = services.shared.lsp.NodeKindProvider;
this.nodeInfoProvider = services.lsp.NodeInfoProvider;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/safe-ds-lang/src/language/safe-ds-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export type SafeDsAddedServices = {
purity: {
PurityComputer: SafeDsPurityComputer;
};
types: {
typing: {
ClassHierarchy: SafeDsClassHierarchy;
CoreTypes: SafeDsCoreTypes;
TypeChecker: SafeDsTypeChecker;
Expand Down Expand Up @@ -148,7 +148,7 @@ export const SafeDsModule: Module<SafeDsServices, PartialLangiumServices & SafeD
ScopeComputation: (services) => new SafeDsScopeComputation(services),
ScopeProvider: (services) => new SafeDsScopeProvider(services),
},
types: {
typing: {
ClassHierarchy: (services) => new SafeDsClassHierarchy(services),
CoreTypes: (services) => new SafeDsCoreTypes(services),
TypeChecker: (services) => new SafeDsTypeChecker(services),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ export class SafeDsScopeProvider extends DefaultScopeProvider {
super(services);

this.astReflection = services.shared.AstReflection;
this.classHierarchy = services.types.ClassHierarchy;
this.classHierarchy = services.typing.ClassHierarchy;
this.nodeMapper = services.helpers.NodeMapper;
this.packageManager = services.workspace.PackageManager;
this.typeComputer = services.types.TypeComputer;
this.typeComputer = services.typing.TypeComputer;

this.coreDeclarationCache = new WorkspaceCache(services.shared);
}
Expand Down
16 changes: 8 additions & 8 deletions packages/safe-ds-lang/src/language/typing/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export class CallableType extends Type {
) {
super();

this.factory = services.types.TypeFactory;
this.factory = services.typing.TypeFactory;
}

override get isFullySubstituted(): boolean {
Expand Down Expand Up @@ -159,8 +159,8 @@ export class LiteralType extends Type {
) {
super();

this.coreTypes = services.types.CoreTypes;
this.factory = services.types.TypeFactory;
this.coreTypes = services.typing.CoreTypes;
this.factory = services.typing.TypeFactory;
}

override get isExplicitlyNullable(): boolean {
Expand Down Expand Up @@ -242,7 +242,7 @@ export class NamedTupleType<T extends SdsDeclaration> extends Type {
constructor(services: SafeDsServices, entries: NamedTupleEntry<T>[]) {
super();

this.factory = services.types.TypeFactory;
this.factory = services.typing.TypeFactory;
this.entries = entries;
}

Expand Down Expand Up @@ -588,7 +588,7 @@ export class StaticType extends Type {
) {
super();

this.factory = services.types.TypeFactory;
this.factory = services.typing.TypeFactory;
}

override equals(other: unknown): boolean {
Expand Down Expand Up @@ -636,9 +636,9 @@ export class UnionType extends Type {
constructor(services: SafeDsServices, types: Type[]) {
super();

this.coreTypes = services.types.CoreTypes;
this.factory = services.types.TypeFactory;
this.typeChecker = services.types.TypeChecker;
this.coreTypes = services.typing.CoreTypes;
this.factory = services.typing.TypeFactory;
this.typeChecker = services.typing.TypeChecker;

this.types = types;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ export class SafeDsTypeChecker {

constructor(services: SafeDsServices) {
this.builtinClasses = services.builtins.Classes;
this.classHierarchy = services.types.ClassHierarchy;
this.coreTypes = services.types.CoreTypes;
this.typeComputer = () => services.types.TypeComputer;
this.classHierarchy = services.typing.ClassHierarchy;
this.coreTypes = services.typing.CoreTypes;
this.typeComputer = () => services.typing.TypeComputer;
}

// -----------------------------------------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@ export class SafeDsTypeComputer {
constructor(services: SafeDsServices) {
this.astNodeLocator = services.workspace.AstNodeLocator;
this.coreClasses = services.builtins.Classes;
this.coreTypes = services.types.CoreTypes;
this.factory = services.types.TypeFactory;
this.coreTypes = services.typing.CoreTypes;
this.factory = services.typing.TypeFactory;
this.nodeMapper = services.helpers.NodeMapper;
this.partialEvaluator = services.evaluation.PartialEvaluator;
this.typeChecker = services.types.TypeChecker;
this.typeChecker = services.typing.TypeChecker;

this.nodeTypeCache = new WorkspaceCache(services.shared);
}
Expand Down
10 changes: 5 additions & 5 deletions packages/safe-ds-lang/src/language/validation/inheritance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ export const CODE_INHERITANCE_NULLABLE = 'inheritance/nullable';

export const classMemberMustMatchOverriddenMemberAndShouldBeNeeded = (services: SafeDsServices) => {
const builtinAnnotations = services.builtins.Annotations;
const classHierarchy = services.types.ClassHierarchy;
const typeChecker = services.types.TypeChecker;
const typeComputer = services.types.TypeComputer;
const classHierarchy = services.typing.ClassHierarchy;
const typeChecker = services.typing.TypeChecker;
const typeComputer = services.typing.TypeComputer;

return (node: SdsClassMember, accept: ValidationAcceptor): void => {
// Check whether the member overrides something
Expand Down Expand Up @@ -151,7 +151,7 @@ const isInSafedsLangAnyClass = (services: SafeDsServices, node: SdsClassMember):
};

export const classMustOnlyInheritASingleClass = (services: SafeDsServices) => {
const typeComputer = services.types.TypeComputer;
const typeComputer = services.typing.TypeComputer;
const computeType = typeComputer.computeType.bind(typeComputer);

return (node: SdsClass, accept: ValidationAcceptor): void => {
Expand Down Expand Up @@ -189,7 +189,7 @@ export const classMustOnlyInheritASingleClass = (services: SafeDsServices) => {
};

export const classMustNotInheritItself = (services: SafeDsServices) => {
const classHierarchy = services.types.ClassHierarchy;
const classHierarchy = services.typing.ClassHierarchy;

return (node: SdsClass, accept: ValidationAcceptor): void => {
const superClasses = classHierarchy.streamProperSuperclasses(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ const checkBound = (
};

export const parameterBoundParameterMustBeConstFloatOrInt = (services: SafeDsServices) => {
const coreTypes = services.types.CoreTypes;
const typeChecker = services.types.TypeChecker;
const typeComputer = services.types.TypeComputer;
const coreTypes = services.typing.CoreTypes;
const typeChecker = services.typing.TypeChecker;
const typeComputer = services.typing.TypeComputer;

return (node: SdsParameterBound, accept: ValidationAcceptor) => {
const parameter = node.leftOperand?.ref;
Expand Down Expand Up @@ -161,9 +161,9 @@ export const parameterBoundParameterMustBeConstFloatOrInt = (services: SafeDsSer
};

export const parameterBoundRightOperandMustEvaluateToFloatConstantOrIntConstant = (services: SafeDsServices) => {
const coreTypes = services.types.CoreTypes;
const typeChecker = services.types.TypeChecker;
const typeComputer = services.types.TypeComputer;
const coreTypes = services.typing.CoreTypes;
const typeChecker = services.typing.TypeChecker;
const typeComputer = services.typing.TypeComputer;
const partialEvaluator = services.evaluation.PartialEvaluator;
const one = new IntConstant(1n);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export const constantParameterMustHaveConstantDefaultValue = (services: SafeDsSe
};

export const constantParameterMustHaveTypeThatCanBeEvaluatedToConstant = (services: SafeDsServices) => {
const typeChecker = services.types.TypeChecker;
const typeComputer = services.types.TypeComputer;
const typeChecker = services.typing.TypeChecker;
const typeComputer = services.typing.TypeComputer;

return (node: SdsParameter, accept: ValidationAcceptor) => {
if (!Parameter.isConstant(node) || !node.type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const typeParameterMustHaveSufficientContext = (node: SdsTypeParameter, a
};

export const typeParameterUpperBoundMustBeNamedType = (services: SafeDsServices) => {
const typeComputer = services.types.TypeComputer;
const typeComputer = services.typing.TypeComputer;

return (node: SdsTypeParameter, accept: ValidationAcceptor) => {
const boundType = typeComputer.computeType(node.upperBound);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { UnknownType } from '../../../typing/model.js';
export const CODE_CHAINED_EXPRESSION_MISSING_NULL_SAFETY = 'chained-expression/missing-null-safety';

export const chainedExpressionsMustBeNullSafeIfReceiverIsNullable = (services: SafeDsServices) => {
const typeChecker = services.types.TypeChecker;
const typeComputer = services.types.TypeComputer;
const typeChecker = services.typing.TypeChecker;
const typeComputer = services.typing.TypeComputer;

return (node: SdsChainedExpression, accept: ValidationAcceptor): void => {
if (node.isNullSafe) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export const CODE_INDEXED_ACCESS_INVALID_INDEX = 'indexed-access/invalid-index';

export const indexedAccessIndexMustBeValid = (services: SafeDsServices) => {
const partialEvaluator = services.evaluation.PartialEvaluator;
const typeChecker = services.types.TypeChecker;
const typeComputer = services.types.TypeComputer;
const typeChecker = services.typing.TypeChecker;
const typeComputer = services.typing.TypeComputer;

return (node: SdsIndexedAccess, accept: ValidationAcceptor): void => {
const indexValue = partialEvaluator.evaluate(node.index);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { UnknownType } from '../../../typing/model.js';
export const CODE_INFIX_OPERATION_DIVISION_BY_ZERO = 'infix-operation/division-by-zero';

export const divisionDivisorMustNotBeZero = (services: SafeDsServices) => {
const coreTypes = services.types.CoreTypes;
const coreTypes = services.typing.CoreTypes;
const partialEvaluator = services.evaluation.PartialEvaluator;
const typeChecker = services.types.TypeChecker;
const typeComputer = services.types.TypeComputer;
const typeChecker = services.typing.TypeChecker;
const typeComputer = services.typing.TypeComputer;

const zeroInt = new IntConstant(0n);
const zeroFloat = new FloatConstant(0.0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const unionTypeMustHaveTypes = (node: SdsUnionType, accept: ValidationAcc
};

export const unionTypeShouldNotHaveDuplicateTypes = (services: SafeDsServices) => {
const typeComputer = services.types.TypeComputer;
const typeComputer = services.typing.TypeComputer;

return (node: SdsUnionType, accept: ValidationAcceptor): void => {
const typeArguments = getTypeArguments(node.typeArgumentList);
Expand Down
8 changes: 4 additions & 4 deletions packages/safe-ds-lang/src/language/validation/purity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const functionPurityMustBeSpecified = (services: SafeDsServices) => {

export const impurityReasonsOfOverridingMethodMustBeSubsetOfOverriddenMethod = (services: SafeDsServices) => {
const builtinAnnotations = services.builtins.Annotations;
const classHierarchy = services.types.ClassHierarchy;
const classHierarchy = services.typing.ClassHierarchy;

return (node: SdsFunction, accept: ValidationAcceptor): void => {
const overriddenMember = classHierarchy.getOverriddenMember(node);
Expand Down Expand Up @@ -93,7 +93,7 @@ export const impurityReasonParameterNameMustBelongToParameterOfCorrectType = (se
const impurityReasons = services.builtins.ImpurityReasons;
const nodeMapper = services.helpers.NodeMapper;
const partialEvaluator = services.evaluation.PartialEvaluator;
const typeComputer = services.types.TypeComputer;
const typeComputer = services.typing.TypeComputer;

return (node: SdsFunction, accept: ValidationAcceptor) => {
const annotationCall = findFirstAnnotationCallOf(node, builtinAnnotations.Impure);
Expand Down Expand Up @@ -219,7 +219,7 @@ export const impurityReasonShouldNotBeSetMultipleTimes = (services: SafeDsServic

export const pureParameterDefaultValueMustBePure = (services: SafeDsServices) => {
const purityComputer = services.purity.PurityComputer;
const typeComputer = services.types.TypeComputer;
const typeComputer = services.typing.TypeComputer;

return (node: SdsParameter, accept: ValidationAcceptor) => {
if (!node.defaultValue) {
Expand Down Expand Up @@ -248,7 +248,7 @@ export const pureParameterDefaultValueMustBePure = (services: SafeDsServices) =>
export const callArgumentAssignedToPureParameterMustBePure = (services: SafeDsServices) => {
const nodeMapper = services.helpers.NodeMapper;
const purityComputer = services.purity.PurityComputer;
const typeComputer = services.types.TypeComputer;
const typeComputer = services.typing.TypeComputer;

return (node: SdsCall, accept: ValidationAcceptor) => {
for (const argument of getArguments(node)) {
Expand Down
8 changes: 4 additions & 4 deletions packages/safe-ds-lang/src/language/validation/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ export const constraintListShouldNotBeEmpty = (services: SafeDsServices) => {
export const elvisOperatorShouldBeNeeded = (services: SafeDsServices) => {
const partialEvaluator = services.evaluation.PartialEvaluator;
const settingsProvider = services.workspace.SettingsProvider;
const typeChecker = services.types.TypeChecker;
const typeComputer = services.types.TypeComputer;
const typeChecker = services.typing.TypeChecker;
const typeComputer = services.typing.TypeComputer;

return async (node: SdsInfixOperation, accept: ValidationAcceptor) => {
if (!(await settingsProvider.shouldValidateCodeStyle())) {
Expand Down Expand Up @@ -309,8 +309,8 @@ export const importedDeclarationAliasShouldDifferFromDeclarationName = (services

export const chainedExpressionNullSafetyShouldBeNeeded = (services: SafeDsServices) => {
const settingsProvider = services.workspace.SettingsProvider;
const typeChecker = services.types.TypeChecker;
const typeComputer = services.types.TypeComputer;
const typeChecker = services.typing.TypeChecker;
const typeComputer = services.typing.TypeComputer;

return async (node: SdsChainedExpression, accept: ValidationAcceptor) => {
if (!(await settingsProvider.shouldValidateCodeStyle())) {
Expand Down
Loading

0 comments on commit 46b2bb2

Please sign in to comment.