From e5617af7eefdb8d8218064656b3423883020654b Mon Sep 17 00:00:00 2001 From: Kevin Knight <57677197+kevin-m-knight-gs@users.noreply.github.com> Date: Thu, 30 Jan 2025 09:10:44 -0500 Subject: [PATCH] Fix an issue with resolved type parameters for function expressions (#927) * Improve compiled state integrity test failure messages * Clean up GenericTypeWithXArguments * Add additional methods to TestTypeInferenceObserver * Fix issue with resolved type parameters for function expressions --- .../inference/PrintTypeInferenceObserver.java | 2 +- .../inference/TestTypeInferenceObserver.java | 25 ++ .../inference/TypeInference.java | 19 +- .../inference/TypeInferenceContext.java | 356 +++++++++--------- .../pure/m3/navigation/_package/_Package.java | 16 + .../navigation/generictype/GenericType.java | 74 ++-- .../GenericTypeWithXArguments.java | 120 +++--- .../navigation/graph/GraphPathIterable.java | 20 + .../legend/pure/m3/tools/GraphStatistics.java | 82 ---- .../legend/pure/m3/tools/GraphTools.java | 273 ++++++++++++++ .../AbstractCompiledStateIntegrityTest.java | 30 +- .../CompiledStateIntegrityTestTools.java | 179 +-------- .../inference/TestFunctionTypeInference.java | 39 ++ .../pure/m4/tools/GraphWalkFilterResult.java | 24 +- 14 files changed, 694 insertions(+), 565 deletions(-) create mode 100644 legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/tools/GraphTools.java diff --git a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/PrintTypeInferenceObserver.java b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/PrintTypeInferenceObserver.java index d4f8c2fa58..41cedac9ec 100644 --- a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/PrintTypeInferenceObserver.java +++ b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/PrintTypeInferenceObserver.java @@ -346,7 +346,7 @@ public TypeInferenceObserver tryingRegistration(org.finos.legend.pure.m3.coreins GenericType.print(this.appendable, templateGenType, this.processorState.getProcessorSupport()); print(" <-> "); GenericType.print(this.appendable, genericType, this.processorState.getProcessorSupport()); - print(" in ").print(typeInferenceContext.getId()).print("/").print(targetGenericsContext.getId()).print(" "); + print(" in ").print(typeInferenceContext.getId()).print("/").print(targetGenericsContext.getId()); return printNewline(); } diff --git a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/TestTypeInferenceObserver.java b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/TestTypeInferenceObserver.java index 5f6fb1176c..72d13b4fb1 100644 --- a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/TestTypeInferenceObserver.java +++ b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/TestTypeInferenceObserver.java @@ -17,6 +17,10 @@ import org.eclipse.collections.api.factory.Stacks; import org.eclipse.collections.api.stack.MutableStack; import org.finos.legend.pure.m3.compiler.postprocessing.ProcessorState; +import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.function.Function; +import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.FunctionType; +import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.generics.GenericType; +import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.generics.TypeParameter; import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.valuespecification.ValueSpecification; import org.finos.legend.pure.m3.navigation.ProcessorSupport; import org.finos.legend.pure.m4.coreinstance.CoreInstance; @@ -150,6 +154,13 @@ public TypeInferenceObserver register(CoreInstance templateGenType, CoreInstance return this; } + @Override + public TypeInferenceObserver tryingRegistration(GenericType templateGenType, GenericType genericType, TypeInferenceContext typeInferenceContext, TypeInferenceContext targetGenericsContext) + { + activeObserver().tryingRegistration(templateGenType, genericType, typeInferenceContext, targetGenericsContext); + return this; + } + @Override public TypeInferenceObserver registerMul(CoreInstance templateMul, CoreInstance valueMul, TypeInferenceContext context, TypeInferenceContext targetGenericsContext) { @@ -233,4 +244,18 @@ public TypeInferenceObserver finishedProcessingFunctionExpression(CoreInstance f activeObserver().finishedProcessingFunctionExpression(functionExpression); return this; } + + @Override + public TypeInferenceObserver updateFunctionResolvedTypeParameters(Function foundFunction, FunctionType functionType) + { + activeObserver().updateFunctionResolvedTypeParameters(foundFunction, functionType); + return this; + } + + @Override + public TypeInferenceObserver updateFunctionResolvedTypeParameterValue(TypeParameter typeParameter, CoreInstance value) + { + activeObserver().updateFunctionResolvedTypeParameterValue(typeParameter, value); + return this; + } } diff --git a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/TypeInference.java b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/TypeInference.java index d03dcd929b..64a8c1a374 100644 --- a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/TypeInference.java +++ b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/TypeInference.java @@ -57,18 +57,12 @@ public class TypeInference public static boolean canProcessLambda(FunctionDefinition lambda, ProcessorState processorState, ProcessorSupport processorSupport) { FunctionType functionType = lambda._classifierGenericType()._typeArguments().notEmpty() ? (FunctionType) ImportStub.withImportStubByPass(lambda._classifierGenericType()._typeArguments().getFirst()._rawType(), processorSupport) : null; - if (functionType != null) + return (functionType == null) || functionType._parameters().noneSatisfy(parameter -> { - for (VariableExpression parameter : functionType._parameters()) - { - GenericType genericType = parameter._genericType(); - if (genericType == null || (!org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(genericType) && !processorState.getTypeInferenceContext().isTypeParameterResolved(genericType))) - { - return false; - } - } - } - return true; + GenericType genericType = parameter._genericType(); + return (genericType == null) || + (!org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(genericType) && !processorState.getTypeInferenceContext().isTypeParameterResolved(genericType)); + }); } public static void storeInferredTypeParametersInFunctionExpression(FunctionExpression functionExpression, ProcessorState state, ProcessorSupport processorSupport, Function foundFunction, TypeInferenceObserver observer) throws PureCompilationException @@ -85,7 +79,8 @@ public static void storeInferredTypeParametersInFunctionExpression(FunctionExpre observer.updateFunctionResolvedTypeParameterValue(typeParameter, value); if (value != null) { - functionExpression._resolvedTypeParametersAdd((GenericType) value); + CoreInstance copy = org.finos.legend.pure.m3.navigation.generictype.GenericType.copyGenericType(value, functionExpression.getSourceInformation(), processorSupport); + functionExpression._resolvedTypeParametersAdd((GenericType) copy); } else if (typeInferenceContext.getParent() == null) { diff --git a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/TypeInferenceContext.java b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/TypeInferenceContext.java index c8e8e4a3bc..8e3e0f783b 100644 --- a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/TypeInferenceContext.java +++ b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/compiler/postprocessing/inference/TypeInferenceContext.java @@ -103,7 +103,7 @@ public void addStateForCollectionElement() this.states.add(this.states.getFirst().copy()); } - public MutableList drop(int size) + MutableList drop(int size) { MutableList dropped = Lists.mutable.ofInitialCapacity(Math.min(size, this.states.size())); int newSize = this.states.size() - size; @@ -207,8 +207,8 @@ else if (this.scope instanceof GenericType) else { String functionName = (this.scope instanceof FunctionExpression) ? - ((FunctionExpression) this.scope)._functionName() : - ((this.scope instanceof Function) ? ((Function) this.scope)._functionName() : null); + ((FunctionExpression) this.scope)._functionName() : + ((this.scope instanceof Function) ? ((Function) this.scope)._functionName() : null); appendable.append(functionName); FunctionType.print(appendable, this.processorSupport.function_getFunctionType(this.scope), this.processorSupport); } @@ -333,226 +333,246 @@ public void register(GenericType templateGenType, GenericType genericType, TypeI Objects.requireNonNull(targetGenericsContext); - if (genericType != null) + if (genericType == null) { - GenericType genericTypeCopy = (GenericType) org.finos.legend.pure.m3.navigation.generictype.GenericType.copyGenericType(genericType, true, this.processorSupport); + // nothing to do + return; + } - if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeOperationEqual(genericTypeCopy)) - { - if (targetGenericsContext.getParent() != null) - { - GenericTypeOperation gto = (GenericTypeOperation) genericTypeCopy; - getParent().register(gto._left(), gto._right(), targetGenericsContext.getParent(), merge, observer); - } - } + GenericType genericTypeCopy = (GenericType) org.finos.legend.pure.m3.navigation.generictype.GenericType.copyGenericType(genericType, true, this.processorSupport); - if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeOperationEqual(templateGenType)) - { - register(((GenericTypeOperation) templateGenType)._left(), genericTypeCopy, targetGenericsContext, merge, observer); - } + if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeOperationEqual(genericTypeCopy) && (targetGenericsContext.getParent() != null)) + { + GenericTypeOperation gto = (GenericTypeOperation) genericTypeCopy; + observer.shiftTab(); + getParent().register(gto._left(), gto._right(), targetGenericsContext.getParent(), merge, observer); + observer.unShiftTab(); + } + + if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeOperationEqual(templateGenType)) + { + observer.shiftTab(); + register(((GenericTypeOperation) templateGenType)._left(), genericTypeCopy, targetGenericsContext, merge, observer); + observer.unShiftTab(); + } - if ((templateGenType instanceof GenericTypeOperation) && (genericTypeCopy instanceof GenericTypeOperation)) + if ((templateGenType instanceof GenericTypeOperation) && (genericTypeCopy instanceof GenericTypeOperation)) + { + GenericTypeOperation templateGenTypeOperation = (GenericTypeOperation) templateGenType; + GenericTypeOperation genericTypeOperationCopy = (GenericTypeOperation) genericTypeCopy; + if (templateGenTypeOperation._type().getName().equals(genericTypeOperationCopy._type().getName())) { - GenericTypeOperation templateGenTypeOperation = (GenericTypeOperation) templateGenType; - GenericTypeOperation genericTypeOperationCopy = (GenericTypeOperation) genericTypeCopy; - if (templateGenTypeOperation._type().getName().equals(genericTypeOperationCopy._type().getName())) - { - register(templateGenTypeOperation._left(), genericTypeOperationCopy._left(), targetGenericsContext, merge, observer); - register(templateGenTypeOperation._right(), genericTypeOperationCopy._right(), targetGenericsContext, merge, observer); - } + observer.shiftTab(); + register(templateGenTypeOperation._left(), genericTypeOperationCopy._left(), targetGenericsContext, merge, observer); + register(templateGenTypeOperation._right(), genericTypeOperationCopy._right(), targetGenericsContext, merge, observer); + observer.unShiftTab(); } + } - String name = org.finos.legend.pure.m3.navigation.generictype.GenericType.getTypeParameterName(templateGenType); - if (name != null) - { - ParameterValueWithFlag existing = this.states.getLast().getTypeParameterValueWithFlag(name); + String name = org.finos.legend.pure.m3.navigation.generictype.GenericType.getTypeParameterName(templateGenType); + if (name != null) + { + ParameterValueWithFlag existing = this.states.getLast().getTypeParameterValueWithFlag(name); - List forwards = Lists.mutable.empty(); + List forwards = Lists.mutable.empty(); - if (existing == null) - { - // New registration - this.states.getLast().putTypeParameterValue(name, genericTypeCopy, targetGenericsContext, false); - } - else if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(existing.getParameterValue()) && org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(genericTypeCopy)) + if (existing == null) + { + // New registration + this.states.getLast().putTypeParameterValue(name, genericTypeCopy, targetGenericsContext, false); + } + else if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(existing.getParameterValue()) && org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(genericTypeCopy)) + { + GenericType existingGenericType = (GenericType) existing.getParameterValue(); + boolean isCovariant = TypeParameter.isCovariant(templateGenType); + if (_RelationType.isRelationType(existingGenericType.getValueForMetaPropertyToOne(M3Properties.rawType), this.processorSupport) && + _RelationType.isRelationType(genericTypeCopy.getValueForMetaPropertyToOne(M3Properties.rawType), this.processorSupport)) { - GenericType existingGenericType = (GenericType) existing.getParameterValue(); - boolean isCovariant = TypeParameter.isCovariant(templateGenType); - if (_RelationType.isRelationType(existingGenericType.getValueForMetaPropertyToOne(M3Properties.rawType), this.processorSupport) && - _RelationType.isRelationType(genericTypeCopy.getValueForMetaPropertyToOne(M3Properties.rawType), this.processorSupport)) + GenericType res; + if (_RelationType.canConcatenate(existingGenericType, genericTypeCopy, this.processorSupport)) { - GenericType res; - if (_RelationType.canConcatenate(existingGenericType, genericTypeCopy, this.processorSupport)) - { - res = _RelationType.merge(existingGenericType, genericTypeCopy, isCovariant, this.processorSupport); - } - else - { - res = (GenericType) this.processorSupport.type_wrapGenericType(this.processorSupport.type_TopType()); - } - this.states.getLast().putTypeParameterValue(name, res, targetGenericsContext, false); + res = _RelationType.merge(existingGenericType, genericTypeCopy, isCovariant, this.processorSupport); } else { - if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(existingGenericType) && Type.subTypeOf(existingGenericType._rawType(), processorSupport.package_getByUserPath(M3Paths.Function), processorSupport) && - org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(genericTypeCopy) && Type.subTypeOf(genericTypeCopy._rawType(), processorSupport.package_getByUserPath(M3Paths.Function), processorSupport)) + res = (GenericType) this.processorSupport.type_wrapGenericType(this.processorSupport.type_TopType()); + } + this.states.getLast().putTypeParameterValue(name, res, targetGenericsContext, false); + } + else + { + if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(existingGenericType) && Type.subTypeOf(existingGenericType._rawType(), this.processorSupport.package_getByUserPath(M3Paths.Function), this.processorSupport) && + org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(genericTypeCopy) && Type.subTypeOf(genericTypeCopy._rawType(), this.processorSupport.package_getByUserPath(M3Paths.Function), this.processorSupport)) + { + RichIterable existingTA = ((GenericType) existing.getParameterValue())._typeArguments(); + RichIterable replacementTA = genericTypeCopy._typeArguments(); + if (existingTA.size() == replacementTA.size()) { - RichIterable existingTA = ((GenericType) existing.getParameterValue())._typeArguments(); - RichIterable replacementTA = genericTypeCopy._typeArguments(); - if (existingTA.size() == replacementTA.size()) + Iterator existingTypeArguments = existingTA.iterator(); + Iterator replacementTypeArguments = replacementTA.iterator(); + while (existingTypeArguments.hasNext()) { - Iterator existingTypeArguments = existingTA.iterator(); - Iterator replacementTypeArguments = replacementTA.iterator(); - while (existingTypeArguments.hasNext()) - { - GenericType existingArgument = existingTypeArguments.next(); - GenericType replacementArgument = replacementTypeArguments.next(); - forwards.add(new RegistrationRequest(existing.getTargetGenericsContext(), existingArgument, replacementArgument)); - } + GenericType existingArgument = existingTypeArguments.next(); + GenericType replacementArgument = replacementTypeArguments.next(); + forwards.add(new RegistrationRequest(existing.getTargetGenericsContext(), existingArgument, replacementArgument)); } } + } - // Merge two concrete types - GenericType merged = (GenericType) org.finos.legend.pure.m3.navigation.generictype.GenericType.findBestCommonGenericType(Lists.mutable.with(existing.getParameterValue(), genericTypeCopy), TypeParameter.isCovariant(templateGenType), false, genericType.getSourceInformation(), this.processorSupport); - this.states.getLast().putTypeParameterValue(name, merged, targetGenericsContext, false); + // Merge two concrete types + GenericType merged = (GenericType) org.finos.legend.pure.m3.navigation.generictype.GenericType.findBestCommonGenericType(Lists.mutable.with(existing.getParameterValue(), genericTypeCopy), TypeParameter.isCovariant(templateGenType), false, genericType.getSourceInformation(), this.processorSupport); + this.states.getLast().putTypeParameterValue(name, merged, targetGenericsContext, false); - // See if the replacement is the more concrete version of a previously semi-concrete type (List replaced by List) - CoreInstance existingRawType = ((GenericType) existing.getParameterValue())._rawType(); - CoreInstance replacementRawType = merged._rawType(); - if (existingRawType.equals(replacementRawType)) + // See if the replacement is the more concrete version of a previously semi-concrete type (List replaced by List) + CoreInstance existingRawType = ((GenericType) existing.getParameterValue())._rawType(); + CoreInstance replacementRawType = merged._rawType(); + if (existingRawType.equals(replacementRawType)) + { + Iterator existingTypeArguments = ((GenericType) existing.getParameterValue())._typeArguments().iterator(); + Iterator replacementTypeArguments = merged._typeArguments().iterator(); + while (existingTypeArguments.hasNext()) { - Iterator existingTypeArguments = ((GenericType) existing.getParameterValue())._typeArguments().iterator(); - Iterator replacementTypeArguments = merged._typeArguments().iterator(); - while (existingTypeArguments.hasNext()) + GenericType existingArgument = existingTypeArguments.next(); + GenericType replacementArgument = replacementTypeArguments.next(); + if (!org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(existingArgument) && org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(replacementArgument)) { - GenericType existingArgument = existingTypeArguments.next(); - GenericType replacementArgument = replacementTypeArguments.next(); - if (!org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(existingArgument) && org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(replacementArgument)) - { - forwards.add(new RegistrationRequest(existing.getTargetGenericsContext(), existingArgument, replacementArgument)); - } + forwards.add(new RegistrationRequest(existing.getTargetGenericsContext(), existingArgument, replacementArgument)); } } } } - else if (this.states.size() > 1) + } + else if (this.states.size() > 1) + { + // We are processing elements of a collection, record what we learn for the element which will later + // be processed by TypeInference.potentiallyUpdateParentTypeParamForInstanceValueWithManyElements later + this.states.getLast().putTypeParameterValue(name, genericTypeCopy, targetGenericsContext, false); + } + else if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(existing.getParameterValue())) + { + // Replace the existing concrete registration with a generic one and move the concrete one to the referenced type + this.states.getLast().putTypeParameterValue(name, genericTypeCopy, targetGenericsContext, false); + forwards.add(new RegistrationRequest(targetGenericsContext, genericTypeCopy, existing.getParameterValue())); + } + else if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(genericTypeCopy)) + { + if (existing.getParameterValue() instanceof GenericTypeOperation) { - // We are processing elements of a collection, record what we learn for the element which will later - // be processed by TypeInference.potentiallyUpdateParentTypeParamForInstanceValueWithManyElements later this.states.getLast().putTypeParameterValue(name, genericTypeCopy, targetGenericsContext, false); } - else if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(existing.getParameterValue())) + else if (!existing.getTargetGenericsContext().equals(this)) { - // Replace the existing concrete registration with a generic one and move the concrete one to the referenced type + forwards.add(new RegistrationRequest(existing.getTargetGenericsContext(), existing.getParameterValue(), genericTypeCopy)); + } + else if (!merge) + { + // We are in the right context and not in 'merge' mode, so it means we are propagating the inference UP. this.states.getLast().putTypeParameterValue(name, genericTypeCopy, targetGenericsContext, false); - forwards.add(new RegistrationRequest(targetGenericsContext, genericTypeCopy, existing.getParameterValue())); } - else if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(genericTypeCopy)) + else { - if (existing.getParameterValue() instanceof GenericTypeOperation) - { - this.states.getLast().putTypeParameterValue(name, genericTypeCopy, targetGenericsContext, false); - } - else if (!existing.getTargetGenericsContext().equals(this)) - { - forwards.add(new RegistrationRequest(existing.getTargetGenericsContext(), existing.getParameterValue(), genericTypeCopy)); - } - else if (!merge) - { - // We are in the right context and not in 'merge' mode, so it means we are propagating the inference UP. - this.states.getLast().putTypeParameterValue(name, genericTypeCopy, targetGenericsContext, false); - } - else - { - // 'Merge' means that we are looping through function parameters after parameter inference is true. In this case finding multiple values means that we have to take the most common type! - // It is the case for if(test:Boolean[1], valid:Function<{->T[m]}>[1], invalid:Function<{->T[m]}>[1]):T[m]; - // When we find multiple values for T (one is a type Parameter (K) and the other one is concrete), we should return Any after a merge.. - // We are not currently doing it, but it should eventually be fixed here + // 'Merge' means that we are looping through function parameters after parameter inference is true. In this case finding multiple values means that we have to take the most common type! + // It is the case for if(test:Boolean[1], valid:Function<{->T[m]}>[1], invalid:Function<{->T[m]}>[1]):T[m]; + // When we find multiple values for T (one is a type Parameter (K) and the other one is concrete), we should return Any after a merge.. + // We are not currently doing it, but it should eventually be fixed here // GenericType res = (GenericType) org.finos.legend.pure.m3.navigation.generictype.GenericType.findBestCommonGenericType(Lists.mutable.with(existing.getParameterValue(), genericTypeCopy), TypeParameter.isCovariant(templateGenType), false, genericType.getSourceInformation(), this.processorSupport); // this.states.getLast().putTypeParameterValue(name, res, targetGenericsContext, true); - } } - else + } + else + { + if (!existing.getTargetGenericsContext().equals(this)) { - if (!existing.getTargetGenericsContext().equals(this)) - { - // forward the registration of this generic type to the already referenced type - forwards.add(new RegistrationRequest(existing.getTargetGenericsContext(), existing.getParameterValue(), genericTypeCopy)); - } + // forward the registration of this generic type to the already referenced type + forwards.add(new RegistrationRequest(existing.getTargetGenericsContext(), existing.getParameterValue(), genericTypeCopy)); } - observer.register(templateGenType, genericTypeCopy, this, targetGenericsContext); - - observer.shiftTab(); - forwards.forEach(request -> request.context.register((GenericType) request.template, (GenericType) request.value, targetGenericsContext, merge, observer)); - observer.unShiftTab(); } + observer.register(templateGenType, genericTypeCopy, this, targetGenericsContext); - if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(templateGenType) && org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(genericTypeCopy)) + observer.shiftTab(); + forwards.forEach(request -> request.context.register((GenericType) request.template, (GenericType) request.value, targetGenericsContext, merge, observer)); + observer.unShiftTab(); + } + + if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(templateGenType) && org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(genericTypeCopy)) + { + CoreInstance templateRawType = ImportStub.withImportStubByPass(templateGenType._rawTypeCoreInstance(), this.processorSupport); + CoreInstance copyRawType = ImportStub.withImportStubByPass(genericTypeCopy._rawTypeCoreInstance(), this.processorSupport); + if (!Type.isBottomType(templateRawType, this.processorSupport) && + !Type.isBottomType(copyRawType, this.processorSupport) && + !Type.isTopType(templateRawType, this.processorSupport) && + !Type.isTopType(copyRawType, this.processorSupport)) { - if (!Type.isBottomType(ImportStub.withImportStubByPass(templateGenType._rawTypeCoreInstance(), this.processorSupport), this.processorSupport) && - !Type.isBottomType(ImportStub.withImportStubByPass(genericTypeCopy._rawTypeCoreInstance(), this.processorSupport), this.processorSupport) && - !Type.isTopType(ImportStub.withImportStubByPass(templateGenType._rawTypeCoreInstance(), this.processorSupport), this.processorSupport) && - !Type.isTopType(ImportStub.withImportStubByPass(genericTypeCopy._rawTypeCoreInstance(), this.processorSupport), this.processorSupport)) + if (_RelationType.isRelationType(templateRawType, this.processorSupport) && _RelationType.isRelationType(copyRawType, this.processorSupport)) { - ListIterable typeValues = null; - ListIterable mulValues = null; - ListIterable typeTemplates = null; - ListIterable mulTemplates = null; + RichIterable> valColumns = ((RelationType) copyRawType)._columns(); + RichIterable> templateColumns = ((RelationType) templateRawType)._columns(); + Pair>, ListIterable>> res = _RelationType.alignColumnSets(valColumns, templateColumns, this.processorSupport); + observer.shiftTab(); + res.getTwo().zip(res.getOne()).forEach(c -> + register(ListHelper.wrapListIterable(c.getOne()._classifierGenericType()._typeArguments()).get(1), ListHelper.wrapListIterable(c.getTwo()._classifierGenericType()._typeArguments()).get(1), targetGenericsContext, merge, observer) + ); + observer.unShiftTab(); + } + else if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(templateGenType) && + FunctionType.isFunctionType(templateRawType, this.processorSupport) && + org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(genericTypeCopy) && + FunctionType.isFunctionType(copyRawType, this.processorSupport)) + { + processFunctionType(targetGenericsContext, false, observer, templateGenType, genericTypeCopy); + } + else + { + ListIterable typeTemplates; + ListIterable mulTemplates; + ListIterable typeValues; + ListIterable mulValues; - if (_RelationType.isRelationType(templateGenType._rawType(), this.processorSupport) && _RelationType.isRelationType(genericTypeCopy._rawType(), this.processorSupport)) + if (Type.subTypeOf(templateRawType, copyRawType, this.processorSupport)) { - RichIterable> valColumns = ((RelationType) genericTypeCopy._rawType())._columns(); - RichIterable> templateColumns = ((RelationType) templateGenType._rawType())._columns(); - Pair>, ListIterable>> res = _RelationType.alignColumnSets(valColumns, templateColumns, this.processorSupport); - res.getTwo().zip(res.getOne()).forEach(c -> - register(c.getOne()._classifierGenericType()._typeArguments().toList().get(1), c.getTwo()._classifierGenericType()._typeArguments().toList().get(1), targetGenericsContext, merge, observer) - ); + typeTemplates = extractTypes(org.finos.legend.pure.m3.navigation.generictype.GenericType.resolveClassTypeParameterUsingInheritance(templateGenType, genericTypeCopy, this.processorSupport)); + mulTemplates = extractMuls(org.finos.legend.pure.m3.navigation.generictype.GenericType.resolveClassMultiplicityParameterUsingInheritance(templateGenType, copyRawType, this.processorSupport)); + typeValues = ListHelper.wrapListIterable(genericTypeCopy._typeArguments()); + mulValues = ListHelper.wrapListIterable(genericTypeCopy._multiplicityArguments()); } - else if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(templateGenType) && templateGenType._rawTypeCoreInstance() instanceof org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.FunctionType && - org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(genericTypeCopy) && genericTypeCopy._rawTypeCoreInstance() instanceof org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.FunctionType) + else if (!org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeFullyConcrete(genericTypeCopy, this.processorSupport) || + !org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeFullyConcrete(templateGenType, this.processorSupport)) { - processFunctionType(targetGenericsContext, false, observer, templateGenType, genericTypeCopy); + typeTemplates = ListHelper.wrapListIterable(templateGenType._typeArguments()); + mulTemplates = ListHelper.wrapListIterable(templateGenType._multiplicityArguments()); + typeValues = extractTypes(org.finos.legend.pure.m3.navigation.generictype.GenericType.resolveClassTypeParameterUsingInheritance(genericTypeCopy, templateGenType, this.processorSupport)); + mulValues = extractMuls(org.finos.legend.pure.m3.navigation.generictype.GenericType.resolveClassMultiplicityParameterUsingInheritance(genericTypeCopy, templateRawType, this.processorSupport)); } else { - if (Type.subTypeOf(ImportStub.withImportStubByPass(templateGenType._rawTypeCoreInstance(), this.processorSupport), ImportStub.withImportStubByPass(genericTypeCopy._rawTypeCoreInstance(), this.processorSupport), this.processorSupport)) - { - typeTemplates = extractTypes(org.finos.legend.pure.m3.navigation.generictype.GenericType.resolveClassTypeParameterUsingInheritance(templateGenType, genericTypeCopy, this.processorSupport)); - mulTemplates = extractMuls(org.finos.legend.pure.m3.navigation.generictype.GenericType.resolveClassMultiplicityParameterUsingInheritance(templateGenType, ImportStub.withImportStubByPass(genericTypeCopy._rawTypeCoreInstance(), this.processorSupport), this.processorSupport)); - typeValues = ListHelper.wrapListIterable(genericTypeCopy._typeArguments()); - mulValues = ListHelper.wrapListIterable(genericTypeCopy._multiplicityArguments()); - } - else if (!org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeFullyConcrete(genericTypeCopy, processorSupport) || !org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeFullyConcrete(templateGenType, processorSupport)) - { - typeTemplates = ListHelper.wrapListIterable(templateGenType._typeArguments()); - mulTemplates = ListHelper.wrapListIterable(templateGenType._multiplicityArguments()); - typeValues = extractTypes(org.finos.legend.pure.m3.navigation.generictype.GenericType.resolveClassTypeParameterUsingInheritance(genericTypeCopy, templateGenType, this.processorSupport)); - mulValues = extractMuls(org.finos.legend.pure.m3.navigation.generictype.GenericType.resolveClassMultiplicityParameterUsingInheritance(genericTypeCopy, ImportStub.withImportStubByPass(templateGenType._rawTypeCoreInstance(), this.processorSupport), this.processorSupport)); - } + typeTemplates = null; + mulTemplates = null; + typeValues = null; + mulValues = null; + } - if (mulValues != null) + if (mulValues != null) + { + mulTemplates.forEachWithIndex((mulTemplate, z) -> registerMul((Multiplicity) mulTemplate, (Multiplicity) mulValues.get(z), targetGenericsContext, observer)); + typeTemplates.forEachWithIndex((typeTemplate, z) -> { - for (int z = 0; z < mulValues.size(); z++) + GenericType template = (GenericType) typeTemplate; + GenericType value = (GenericType) typeValues.get(z); + + if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(template) && + FunctionType.isFunctionType(template._rawTypeCoreInstance(), this.processorSupport) && + org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(value) && + FunctionType.isFunctionType(value._rawTypeCoreInstance(), this.processorSupport)) { - registerMul((Multiplicity) mulTemplates.get(z), (Multiplicity) mulValues.get(z), targetGenericsContext, observer); + processFunctionType(targetGenericsContext, merge, observer, template, value); } - - for (int z = 0; z < typeValues.size(); z++) + else { - GenericType first = (GenericType) typeTemplates.get(z); - GenericType second = (GenericType) typeValues.get(z); - - if (org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(first) && first._rawTypeCoreInstance() instanceof org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.FunctionType && - org.finos.legend.pure.m3.navigation.generictype.GenericType.isGenericTypeConcrete(second) && second._rawTypeCoreInstance() instanceof org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.FunctionType) - { - processFunctionType(targetGenericsContext, merge, observer, first, second); - } - else - { - register(first, second, targetGenericsContext, merge, observer); - } + observer.shiftTab(); + register(template, value, targetGenericsContext, merge, observer); + observer.unShiftTab(); } - } + }); } } } diff --git a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/_package/_Package.java b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/_package/_Package.java index 9842c4be98..eb5946ebbf 100644 --- a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/_package/_Package.java +++ b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/_package/_Package.java @@ -19,6 +19,8 @@ import org.eclipse.collections.api.list.MutableList; import org.eclipse.collections.api.set.ImmutableSet; import org.eclipse.collections.impl.list.fixed.ArrayAdapter; +import org.finos.legend.pure.m3.coreinstance.Package; +import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.Any; import org.finos.legend.pure.m3.navigation.M3Paths; import org.finos.legend.pure.m3.navigation.M3Properties; import org.finos.legend.pure.m3.navigation.M3PropertyPaths; @@ -26,12 +28,26 @@ import org.finos.legend.pure.m3.navigation.PrimitiveUtilities; import org.finos.legend.pure.m3.navigation.ProcessorSupport; import org.finos.legend.pure.m4.ModelRepository; +import org.finos.legend.pure.m4.coreinstance.AbstractCoreInstanceWrapper; import org.finos.legend.pure.m4.coreinstance.CoreInstance; public class _Package { public static final ImmutableSet SPECIAL_TYPES = PrimitiveUtilities.getPrimitiveTypeNames().newWith(M3Paths.Package); + public static boolean isPackage(CoreInstance instance, ProcessorSupport processorSupport) + { + if (instance == null) + { + return false; + } + if (instance instanceof Package) + { + return true; + } + return (!(instance instanceof Any) || (instance instanceof AbstractCoreInstanceWrapper)) && processorSupport.instance_instanceOf(instance, M3Paths.Package); + } + public static CoreInstance getByUserPath(String path, ProcessorSupport processorSupport) { if (path.isEmpty() || PackageableElement.DEFAULT_PATH_SEPARATOR.equals(path)) diff --git a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/generictype/GenericType.java b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/generictype/GenericType.java index 8ad0be2f9f..51fe65dad4 100644 --- a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/generictype/GenericType.java +++ b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/generictype/GenericType.java @@ -23,6 +23,7 @@ import org.eclipse.collections.api.map.MapIterable; import org.eclipse.collections.api.map.MutableMap; import org.eclipse.collections.api.set.MutableSet; +import org.eclipse.collections.impl.utility.LazyIterate; import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel._import.ImportStub; import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.relation.Column; import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.relation.GenericTypeOperation; @@ -68,7 +69,7 @@ public static GenericTypeWithXArguments resolveClassTypeParameterUsingInheritanc if (results.size() > 1) { - results.toSet().cartesianProduct(results.toSet()) + LazyIterate.cartesianProduct(results, results) .select(pair -> pair.getOne() != pair.getTwo()) .forEach(pair -> testFoundGenerics(pair.getOne(), pair.getTwo(), genericTypeSource, genericTypeToFind, processorSupport)); } @@ -77,15 +78,16 @@ public static GenericTypeWithXArguments resolveClassTypeParameterUsingInheritanc private static void testFoundGenerics(GenericTypeWithXArguments first, GenericTypeWithXArguments second, CoreInstance genericTypeSource, CoreInstance genericTypeToFind, ProcessorSupport processorSupport) { - if (!first.getArgumentsByParameterName().keysView().toSet().equals(second.getArgumentsByParameterName().keysView().toSet())) + ImmutableMap firstArgsByParam = first.getArgumentsByParameterName(); + ImmutableMap secondArgsByParam = second.getArgumentsByParameterName(); + if (!firstArgsByParam.castToMap().keySet().equals(secondArgsByParam.castToMap().keySet())) { - throw new RuntimeException("Error: " + first.getArgumentsByParameterName().keysView().toSet() + " / " + second.getArgumentsByParameterName().keysView().toSet()); + throw new RuntimeException("Error: " + firstArgsByParam.castToMap().keySet() + " / " + secondArgsByParam.castToMap().keySet()); } - first.getArgumentsByParameterName().forEachKey(typeParam -> + firstArgsByParam.forEachKeyValue((typeParam, g1) -> { - CoreInstance g1 = first.getArgumentsByParameterName().get(typeParam); - CoreInstance g2 = second.getArgumentsByParameterName().get(typeParam); + CoreInstance g2 = secondArgsByParam.get(typeParam); if (!isGenericCompatibleWith(g1, g2, processorSupport)) { StringBuilder message = new StringBuilder("Diamond inheritance error! '"); @@ -93,7 +95,7 @@ private static void testFoundGenerics(GenericTypeWithXArguments first, GenericTy print(message.append("' is not compatible with '"), g2, processorSupport); print(message.append("' going from '"), genericTypeSource, processorSupport); print(message.append("' to '"), genericTypeToFind, processorSupport).append('\''); - throw new RuntimeException(new PureCompilationException(genericTypeSource.getSourceInformation(), message.toString())); + throw new PureCompilationException(genericTypeSource.getSourceInformation(), message.toString()); } }); } @@ -124,7 +126,7 @@ public static CoreInstance makeTypeArgumentAsConcreteAsPossible(CoreInstance typ return null; } - MapIterable filteredGenericTypeByTypeParameterNames = genericTypeByTypeParameterNames.reject((s, coreInstance) -> typeArgument.equals(coreInstance)); + MapIterable filteredGenericTypeByTypeParameterNames = genericTypeByTypeParameterNames.reject((tp, gt) -> typeArgument.equals(gt)); if (isGenericTypeOperation(typeArgument, processorSupport)) { return resolveOperation(typeArgument, filteredGenericTypeByTypeParameterNames, sourceMulBinding, processorSupport); @@ -143,14 +145,14 @@ public static CoreInstance makeTypeArgumentAsConcreteAsPossible(CoreInstance typ { RelationType rel = (RelationType) typeArgument.getValueForMetaPropertyToOne(M3Properties.rawType); return processorSupport.type_wrapGenericType(_RelationType.build(rel._columns().collect(c -> - (CoreInstance) _Column.getColumnInstance( - c._name(), - c._nameWildCard(), - (org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.generics.GenericType) makeTypeArgumentAsConcreteAsPossible(_Column.getColumnType(c), filteredGenericTypeByTypeParameterNames, sourceMulBinding, processorSupport), - _Column.getColumnMultiplicity(c), - c.getSourceInformation(), - processorSupport - ), + _Column.getColumnInstance( + c._name(), + c._nameWildCard(), + (org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.generics.GenericType) makeTypeArgumentAsConcreteAsPossible(_Column.getColumnType(c), filteredGenericTypeByTypeParameterNames, sourceMulBinding, processorSupport), + _Column.getColumnMultiplicity(c), + c.getSourceInformation(), + processorSupport + ), Lists.mutable.empty() ), null, processorSupport)); } @@ -247,16 +249,16 @@ private static org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.ge org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.generics.GenericType gLeft = resolveOperation((GenericTypeOperation) left, genericTypeByTypeParameterNames, sourceMulBinding, processorSupport); org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.generics.GenericType gRight = (org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.generics.GenericType) copyGenericType(makeTypeArgumentAsConcreteAsPossible(operation._right(), genericTypeByTypeParameterNames, sourceMulBinding, processorSupport), false, processorSupport); return (isGenericTypeConcrete(gLeft) && isGenericTypeConcrete(gRight)) ? - merge(operation, processorSupport, gLeft, gRight) : - ((GenericTypeOperation) processorSupport.newAnonymousCoreInstance(null, M3Paths.GenericTypeOperation))._left(gLeft)._right(gRight)._type(operation._type()); + merge(operation, processorSupport, gLeft, gRight) : + ((GenericTypeOperation) processorSupport.newAnonymousCoreInstance(null, M3Paths.GenericTypeOperation))._left(gLeft)._right(gRight)._type(operation._type()); } else { org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.generics.GenericType gLeft = (org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.generics.GenericType) copyGenericType(resolveTypeParameter(left, genericTypeByTypeParameterNames, processorSupport), false, processorSupport); org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.generics.GenericType gRight = (org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.generics.GenericType) copyGenericType(makeTypeArgumentAsConcreteAsPossible(operation._right(), genericTypeByTypeParameterNames, sourceMulBinding, processorSupport), false, processorSupport); return (isGenericTypeConcrete(gLeft) && isGenericTypeConcrete(gRight)) ? - merge(operation, processorSupport, gLeft, gRight) : - ((GenericTypeOperation) processorSupport.newAnonymousCoreInstance(null, M3Paths.GenericTypeOperation))._left(gLeft)._right(gRight)._type(operation._type()); + merge(operation, processorSupport, gLeft, gRight) : + ((GenericTypeOperation) processorSupport.newAnonymousCoreInstance(null, M3Paths.GenericTypeOperation))._left(gLeft)._right(gRight)._type(operation._type()); } } @@ -344,24 +346,20 @@ public static ImmutableMap bindTypeParametersToTypeArgumen return Maps.immutable.empty(); } - MutableMap result = Maps.mutable.empty(); CoreInstance rawType = Instance.getValueForMetaPropertyToOneResolved(genericType, M3Properties.rawType, processorSupport); ListIterable typeParameters = Instance.getValueForMetaPropertyToManyResolved(rawType, M3Properties.typeParameters, processorSupport); ListIterable typeArguments = Instance.getValueForMetaPropertyToManyResolved(genericType, M3Properties.typeArguments, processorSupport); - int size = typeParameters.size(); - if (typeArguments.size() != size) + if (typeParameters.size() != typeArguments.size()) { StringBuilder message = _Class.print(new StringBuilder("Type argument mismatch for "), rawType); print(message.append("; got: "), genericType, processorSupport); throw new RuntimeException(message.toString()); } - for (int i = 0; i < size; i++) - { - CoreInstance typeParameter = typeParameters.get(i); - CoreInstance typeArgument = typeArguments.get(i); - result.put(Instance.getValueForMetaPropertyToOneResolved(typeParameter, M3Properties.name, processorSupport).getName(), makeTypeArgumentAsConcreteAsPossible(typeArgument, sourceBinding, sourceMulBinding, processorSupport)); - } + MutableMap result = Maps.mutable.ofInitialCapacity(typeParameters.size()); + typeParameters.forEachWithIndex((typeParameter, i) -> result.put( + PrimitiveUtilities.getStringValue(typeParameter.getValueForMetaPropertyToOne(M3Properties.name)), + makeTypeArgumentAsConcreteAsPossible(typeArguments.get(i), sourceBinding, sourceMulBinding, processorSupport))); return result.toImmutable(); } @@ -372,25 +370,21 @@ public static ImmutableMap bindMultiplicityParametersToMul return Maps.immutable.empty(); } - MutableMap result = Maps.mutable.empty(); CoreInstance rawType = Instance.getValueForMetaPropertyToOneResolved(genericType, M3Properties.rawType, processorSupport); ListIterable multiplicityParameters = Instance.getValueForMetaPropertyToManyResolved(rawType, M3Properties.multiplicityParameters, processorSupport); ListIterable multiplicityArguments = Instance.getValueForMetaPropertyToManyResolved(genericType, M3Properties.multiplicityArguments, processorSupport); - int size = multiplicityParameters.size(); - if (multiplicityArguments.size() != size) + if (multiplicityParameters.size() != multiplicityArguments.size()) { StringBuilder message = _Class.print(new StringBuilder("Multiplicity argument mismatch for "), rawType); print(message.append("; got: "), genericType, processorSupport); throw new RuntimeException(message.toString()); } - for (int i = 0; i < size; i++) - { - CoreInstance multiplicityParameter = multiplicityParameters.get(i); - CoreInstance multiplicityArgument = multiplicityArguments.get(i); - result.put(Instance.getValueForMetaPropertyToOneResolved(multiplicityParameter, M3Properties.values, processorSupport).getName(), Multiplicity.makeMultiplicityAsConcreteAsPossible(multiplicityArgument, sourceBinding)); - } + MutableMap result = Maps.mutable.ofInitialCapacity(multiplicityArguments.size()); + multiplicityParameters.forEachWithIndex((multiplicityParameter, i) -> result.put( + PrimitiveUtilities.getStringValue(multiplicityParameter.getValueForMetaPropertyToOne(M3Properties.values)), + Multiplicity.makeMultiplicityAsConcreteAsPossible(multiplicityArguments.get(i), sourceBinding))); return result.toImmutable(); } @@ -1303,8 +1297,8 @@ private static CoreInstance findBestCommonGenericType(ListIterable else { return covariant ? - Support.getBestGenericTypeUsingCovariance(genericTypeSet, knownMostGeneralGenericTypeBound, replaceSourceInfo, newSourceInfo, processorSupport) : - Support.getBestGenericTypeUsingContravariance(genericTypeSet, replaceSourceInfo, newSourceInfo, processorSupport); + Support.getBestGenericTypeUsingCovariance(genericTypeSet, knownMostGeneralGenericTypeBound, replaceSourceInfo, newSourceInfo, processorSupport) : + Support.getBestGenericTypeUsingContravariance(genericTypeSet, replaceSourceInfo, newSourceInfo, processorSupport); } } diff --git a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/generictype/GenericTypeWithXArguments.java b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/generictype/GenericTypeWithXArguments.java index e310a0782a..a7e71a7d0e 100644 --- a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/generictype/GenericTypeWithXArguments.java +++ b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/generictype/GenericTypeWithXArguments.java @@ -14,23 +14,19 @@ package org.finos.legend.pure.m3.navigation.generictype; +import org.eclipse.collections.api.factory.Lists; +import org.eclipse.collections.api.factory.Maps; import org.eclipse.collections.api.list.ListIterable; -import org.eclipse.collections.api.list.MutableList; import org.eclipse.collections.api.map.ImmutableMap; -import org.eclipse.collections.api.map.MapIterable; -import org.eclipse.collections.api.tuple.Pair; -import org.eclipse.collections.impl.factory.Lists; -import org.eclipse.collections.impl.factory.Maps; -import org.eclipse.collections.impl.list.mutable.FastList; import org.finos.legend.pure.m3.navigation.Instance; import org.finos.legend.pure.m3.navigation.M3Paths; import org.finos.legend.pure.m3.navigation.M3ProcessorSupport; import org.finos.legend.pure.m3.navigation.M3Properties; +import org.finos.legend.pure.m3.navigation.PrimitiveUtilities; import org.finos.legend.pure.m3.navigation.ProcessorSupport; import org.finos.legend.pure.m3.navigation.multiplicity.Multiplicity; import org.finos.legend.pure.m4.coreinstance.CoreInstance; - -import java.io.IOException; +import org.finos.legend.pure.m4.tools.SafeAppendable; public class GenericTypeWithXArguments { @@ -40,7 +36,7 @@ public class GenericTypeWithXArguments public GenericTypeWithXArguments(CoreInstance genericType, ImmutableMap argumentsByParameterName) { this.genericType = genericType; - this.argumentsByParameterName = (argumentsByParameterName == null) ? Maps.immutable.empty() : argumentsByParameterName; + this.argumentsByParameterName = (argumentsByParameterName == null) ? Maps.immutable.empty() : argumentsByParameterName; } public CoreInstance getGenericType() @@ -62,88 +58,64 @@ public ListIterable extractArgumentsAsTypeParameters(ProcessorSupp { CoreInstance rawType = Instance.getValueForMetaPropertyToOneResolved(this.genericType, M3Properties.rawType, processorSupport); ListIterable typeParams = rawType.getValueForMetaPropertyToMany(M3Properties.typeParameters); - if (typeParams.isEmpty()) - { - return Lists.immutable.empty(); - } - - MutableList result = FastList.newList(typeParams.size()); - for (CoreInstance typeParam : typeParams) - { - result.add(this.argumentsByParameterName.get(typeParam.getValueForMetaPropertyToOne(M3Properties.name).getName())); - } - return result; + return typeParams.isEmpty() ? + Lists.immutable.empty() : + typeParams.collect(tp -> this.argumentsByParameterName.get(PrimitiveUtilities.getStringValue(tp.getValueForMetaPropertyToOne(M3Properties.name)))); } public ListIterable extractArgumentsAsMultiplicityParameters(ProcessorSupport processorSupport) { CoreInstance rawType = Instance.getValueForMetaPropertyToOneResolved(this.genericType, M3Properties.rawType, processorSupport); ListIterable multParams = rawType.getValueForMetaPropertyToMany(M3Properties.multiplicityParameters); - if (multParams.isEmpty()) - { - return Lists.immutable.empty(); - } + return multParams.isEmpty() ? + Lists.immutable.empty() : + multParams.collect(mp -> this.argumentsByParameterName.get(PrimitiveUtilities.getStringValue(mp.getValueForMetaPropertyToOne(M3Properties.values)))); + } - MapIterable multArgumentsByName = this.argumentsByParameterName; - MutableList result = FastList.newList(multParams.size()); - for (CoreInstance multParam : multParams) - { - result.add(multArgumentsByName.get(multParam.getValueForMetaPropertyToOne(M3Properties.values).getName())); - } - return result; + public T print(T appendable, ProcessorSupport processorSupport) + { + print(SafeAppendable.wrap(appendable), processorSupport); + return appendable; } - public void print(Appendable appendable, ProcessorSupport processorSupport) + private void print(SafeAppendable appendable, ProcessorSupport processorSupport) { - try + GenericType.print(appendable, this.genericType, processorSupport).append(" / {"); + boolean[] first = {true}; + this.argumentsByParameterName.forEachKeyValue((var, value) -> { - GenericType.print(appendable, this.genericType, processorSupport); - appendable.append(" / {"); - boolean first = true; - for (Pair varValue : this.argumentsByParameterName.keyValuesView()) + if (first[0]) { - if (first) - { - first = false; - } - else - { - appendable.append(", "); - } - String var = varValue.getOne(); - CoreInstance value = varValue.getTwo(); - appendable.append(var); - appendable.append(" = "); - if (value == null) - { - appendable.append("null"); - } - else if (Instance.instanceOf(value, M3Paths.GenericType, processorSupport)) - { - GenericType.print(appendable, value, processorSupport); - } - else if (Instance.instanceOf(value, M3Paths.Multiplicity, processorSupport)) - { - Multiplicity.print(appendable, value, true); - } - else - { - appendable.append(value.toString()); - } + first[0] = false; } - appendable.append('}'); - } - catch (IOException e) - { - throw new RuntimeException(e); - } + else + { + appendable.append(", "); + } + appendable.append(var).append(" = "); + if (value == null) + { + appendable.append("null"); + } + else if (Instance.instanceOf(value, M3Paths.GenericType, processorSupport)) + { + GenericType.print(appendable, value, processorSupport); + } + else if (Instance.instanceOf(value, M3Paths.Multiplicity, processorSupport)) + { + Multiplicity.print(appendable, value, true); + } + else + { + appendable.append(value.toString()); + } + }); + appendable.append('}'); } @Override public String toString() { - StringBuilder builder = new StringBuilder(64); - print(builder, new M3ProcessorSupport(this.genericType.getRepository())); - return builder.toString(); + return print(new StringBuilder(64), new M3ProcessorSupport(this.genericType.getRepository())).toString(); } } diff --git a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/graph/GraphPathIterable.java b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/graph/GraphPathIterable.java index 9cd2b7debe..cd92e9a53f 100644 --- a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/graph/GraphPathIterable.java +++ b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/navigation/graph/GraphPathIterable.java @@ -56,6 +56,26 @@ public boolean isEmpty() return this.startPaths.isEmpty() || ((this.pathFilter != null) && super.isEmpty()); } + @Override + public ResolvedGraphPath getAny() + { + if (this.startPaths.isEmpty()) + { + return null; + } + if (this.pathFilter == null) + { + return this.startPaths.getAny(); + } + return super.getAny(); + } + + @Override + public ResolvedGraphPath getFirst() + { + return this.startPaths.isEmpty() ? null : super.getFirst(); + } + @Override public boolean contains(Object object) { diff --git a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/tools/GraphStatistics.java b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/tools/GraphStatistics.java index 719d6e994d..ba3e120f18 100644 --- a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/tools/GraphStatistics.java +++ b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/tools/GraphStatistics.java @@ -14,7 +14,6 @@ package org.finos.legend.pure.m3.tools; -import org.eclipse.collections.api.LazyIterable; import org.eclipse.collections.api.RichIterable; import org.eclipse.collections.api.bag.Bag; import org.eclipse.collections.api.factory.Bags; @@ -27,23 +26,14 @@ import org.eclipse.collections.api.set.SetIterable; import org.eclipse.collections.impl.factory.Multimaps; import org.eclipse.collections.impl.factory.primitive.ObjectIntMaps; -import org.finos.legend.pure.m3.coreinstance.Package; import org.finos.legend.pure.m3.coreinstance.helper.AnyStubHelper; import org.finos.legend.pure.m3.navigation.PackageableElement.PackageableElement; -import org.finos.legend.pure.m3.navigation.ProcessorSupport; -import org.finos.legend.pure.m3.navigation._package._Package; -import org.finos.legend.pure.m3.navigation.graph.GraphPath; -import org.finos.legend.pure.m3.navigation.graph.GraphPathIterable; -import org.finos.legend.pure.m3.navigation.graph.ResolvedGraphPath; import org.finos.legend.pure.m4.ModelRepository; import org.finos.legend.pure.m4.coreinstance.CoreInstance; import org.finos.legend.pure.m4.tools.GraphNodeIterable; -import org.finos.legend.pure.m4.tools.GraphWalkFilterResult; import java.util.Formatter; -import java.util.Set; import java.util.function.Function; -import java.util.function.Predicate; import java.util.function.ToIntFunction; public class GraphStatistics @@ -149,76 +139,4 @@ private static void writeInstanceCountsByClassifierPathDeltas(Appendable appenda }); } } - - public static LazyIterable allPathsBetween(String startNodePath, CoreInstance endNode, ProcessorSupport processorSupport) - { - return allPathsBetween(Sets.immutable.with(startNodePath), endNode, processorSupport); - } - - public static LazyIterable allPathsBetween(String startNodePath, CoreInstance endNode, int maxPathLength, ProcessorSupport processorSupport) - { - return allPathsBetween(Sets.immutable.with(startNodePath), endNode, maxPathLength, processorSupport); - } - - public static LazyIterable allPathsBetween(Iterable startNodePaths, CoreInstance endNode, ProcessorSupport processorSupport) - { - return allPathsBetween(startNodePaths, endNode, -1, processorSupport); - } - - public static LazyIterable allPathsBetween(Iterable startNodePaths, CoreInstance endNode, int maxPathLength, ProcessorSupport processorSupport) - { - return allPathsBetween(startNodePaths, endNode::equals, maxPathLength, processorSupport); - } - - public static LazyIterable allPathsBetween(String startNodePath, Iterable endNodes, ProcessorSupport processorSupport) - { - return allPathsBetween(Sets.immutable.with(startNodePath), endNodes, processorSupport); - } - - public static LazyIterable allPathsBetween(String startNodePath, Iterable endNodes, int maxPathLength, ProcessorSupport processorSupport) - { - return allPathsBetween(Sets.immutable.with(startNodePath), endNodes, maxPathLength, processorSupport); - } - - public static LazyIterable allPathsBetween(Iterable startNodePaths, Iterable endNodes, ProcessorSupport processorSupport) - { - return allPathsBetween(startNodePaths, endNodes, -1, processorSupport); - } - - public static LazyIterable allPathsBetween(Iterable startNodePaths, Iterable endNodes, int maxPathLength, ProcessorSupport processorSupport) - { - Set endNodesSet = (endNodes instanceof Set) ? (Set) endNodes : Sets.mutable.withAll(endNodes); - return allPathsBetween(startNodePaths, endNodesSet::contains, maxPathLength, processorSupport); - } - - private static LazyIterable allPathsBetween(Iterable startNodePaths, Predicate isEndNode, int maxPathLength, ProcessorSupport processorSupport) - { - return GraphPathIterable.builder(processorSupport) - .withStartNodePaths(startNodePaths) - .withPathFilter(rgp -> - { - int len = rgp.getGraphPath().getEdgeCount(); - if (len > maxPathLength) - { - return GraphWalkFilterResult.REJECT_AND_STOP; - } - if ((len == maxPathLength) || isEndNode.test(rgp.getLastResolvedNode())) - { - return GraphWalkFilterResult.ACCEPT_AND_STOP; - } - return GraphWalkFilterResult.ACCEPT_AND_CONTINUE; - }) - .build() - .select(path -> isEndNode.test(path.getLastResolvedNode())) - .collect(ResolvedGraphPath::getGraphPath); - } - - public static LazyIterable allTopLevelAndPackagedElementPaths(ProcessorSupport processorSupport) - { - LazyIterable packagedElements = PackageTreeIterable.newRootPackageTreeIterable(processorSupport) - .flatCollect(Package::_children) - .reject(c -> c instanceof Package) - .collect(PackageableElement::getUserPathForPackageableElement); - return _Package.SPECIAL_TYPES.asLazy().concatenate(packagedElements); - } } diff --git a/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/tools/GraphTools.java b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/tools/GraphTools.java new file mode 100644 index 0000000000..2e9f20ef5f --- /dev/null +++ b/legend-pure-core/legend-pure-m3-core/src/main/java/org/finos/legend/pure/m3/tools/GraphTools.java @@ -0,0 +1,273 @@ +// Copyright 2025 Goldman Sachs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.finos.legend.pure.m3.tools; + +import org.eclipse.collections.api.LazyIterable; +import org.eclipse.collections.api.factory.Lists; +import org.eclipse.collections.api.factory.Sets; +import org.eclipse.collections.api.set.MutableSet; +import org.eclipse.collections.impl.utility.LazyIterate; +import org.finos.legend.pure.m3.coreinstance.PackageAccessor; +import org.finos.legend.pure.m3.navigation.M3Paths; +import org.finos.legend.pure.m3.navigation.M3Properties; +import org.finos.legend.pure.m3.navigation.M3PropertyPaths; +import org.finos.legend.pure.m3.navigation.PackageableElement.PackageableElement; +import org.finos.legend.pure.m3.navigation.PrimitiveUtilities; +import org.finos.legend.pure.m3.navigation.ProcessorSupport; +import org.finos.legend.pure.m3.navigation._package._Package; +import org.finos.legend.pure.m3.navigation.graph.GraphPathIterable; +import org.finos.legend.pure.m3.navigation.graph.ResolvedGraphPath; +import org.finos.legend.pure.m4.ModelRepository; +import org.finos.legend.pure.m4.coreinstance.CoreInstance; +import org.finos.legend.pure.m4.coreinstance.SourceInformation; +import org.finos.legend.pure.m4.tools.GraphNodeIterable; +import org.finos.legend.pure.m4.tools.GraphWalkFilterResult; + +import java.util.Objects; +import java.util.Set; +import java.util.function.Predicate; + +public class GraphTools +{ + public static LazyIterable getTopLevelAndPackagedElementPaths(ModelRepository repository) + { + return repository.getTopLevels().asLazy().collect(CoreInstance::getName) + .concatenate(PackageTreeIterable.newRootPackageTreeIterable(repository) + .flatCollect(PackageAccessor::_children) + .collect(PackageableElement::getUserPathForPackageableElement)); + } + + @SuppressWarnings("unchecked") + public static LazyIterable getTopLevelAndPackagedElementPaths(ProcessorSupport processorSupport) + { + return LazyIterate.concatenate( + PrimitiveUtilities.getPrimitiveTypeNames().asLazy(), + Lists.immutable.with(M3Paths.Package, M3Paths.Root), + PackageTreeIterable.newRootPackageTreeIterable(processorSupport).flatCollect(PackageAccessor::_children).collect(PackageableElement::getUserPathForPackageableElement)); + } + + public static LazyIterable getTopLevelAndPackagedElements(ModelRepository repository) + { + return repository.getTopLevels() + .asLazy() + .concatenate(PackageTreeIterable.newRootPackageTreeIterable(repository) + .flatCollect(PackageAccessor::_children) + .collect(e -> (CoreInstance) e)); + } + + public static LazyIterable getTopLevelAndPackagedElements(ProcessorSupport processorSupport) + { + return PrimitiveUtilities.getPrimitiveTypes(processorSupport, Lists.mutable.ofInitialCapacity(PrimitiveUtilities.getPrimitiveTypeNames().size() + 2)) + .with(processorSupport.repository_getTopLevel(M3Paths.Package)) + .with(processorSupport.repository_getTopLevel(M3Paths.Root)) + .asLazy() + .concatenate(PackageTreeIterable.newRootPackageTreeIterable(processorSupport) + .flatCollect(PackageAccessor::_children) + .collect(e -> (CoreInstance) e)); + } + + public static LazyIterable getComponentInstances(CoreInstance element, ProcessorSupport processorSupport) + { + Objects.requireNonNull(element.getSourceInformation(), "element source information may not be null"); + return GraphNodeIterable.builder() + .withStartingNode(element) + .withKeyFilter(GraphTools::internalPropertyFilter) + .withNodeFilter(node -> internalNodeFilter(element, node, processorSupport)) + .build(); + } + + public static GraphPathIterable internalGraphPaths(CoreInstance element, ProcessorSupport processorSupport) + { + Objects.requireNonNull(element.getSourceInformation(), "element source information may not be null"); + return GraphPathIterable.builder(processorSupport) + .withStartNode(element) + .withPropertyFilter(GraphTools::internalPropertyFilter) + .withPathFilter(rgp -> internalPathFilter(element, rgp, processorSupport)) + .build(); + } + + public static ResolvedGraphPath findPathToInstance(CoreInstance instance, ProcessorSupport processorSupport) + { + return findPathToInstance(instance, processorSupport, false); + } + + public static ResolvedGraphPath findPathToInstance(CoreInstance instance, ProcessorSupport processorSupport, boolean allowPathToExternalInstance) + { + return getTopLevelAndPackagedElements(processorSupport) + .flatCollect(e -> Lists.immutable.with(findPathToInstanceWithinElement(e, instance, processorSupport, allowPathToExternalInstance))) + .detect(Objects::nonNull); + } + + public static ResolvedGraphPath findPathToInstanceWithinElement(CoreInstance element, CoreInstance instance, ProcessorSupport processorSupport) + { + return findPathToInstanceWithinElement(element, instance, processorSupport, false); + } + + public static ResolvedGraphPath findPathToInstanceWithinElement(CoreInstance element, CoreInstance instance, ProcessorSupport processorSupport, boolean allowPathToExternalInstance) + { + if (element == instance) + { + return GraphPathIterable.builder(processorSupport) + .withStartNode(element) + .withPropertyFilter((rgp, prop) -> false) + .build() + .getAny(); + } + if ((element.getSourceInformation() == null) || (!allowPathToExternalInstance && isExternalNode(element, instance, processorSupport))) + { + return null; + } + MutableSet visited = Sets.mutable.empty(); + return GraphPathIterable.builder(processorSupport) + .withStartNode(element) + .withPropertyFilter(GraphTools::internalPropertyFilter) + .withPathFilter(rgp -> + { + CoreInstance node = rgp.getLastResolvedNode(); + return (node == instance) ? + GraphWalkFilterResult.ACCEPT_AND_STOP : + GraphWalkFilterResult.reject(visited.add(node) && !isExternalNode(element, node, processorSupport)); + }) + .build() + .getAny(); + } + + public static GraphPathIterable getPathsToInstanceWithinElement(CoreInstance element, CoreInstance instance, ProcessorSupport processorSupport) + { + return getPathsToInstanceWithinElement(element, instance, processorSupport, false); + } + + public static GraphPathIterable getPathsToInstanceWithinElement(CoreInstance element, CoreInstance instance, ProcessorSupport processorSupport, boolean allowPathsToExternalInstance) + { + if (element == instance) + { + return GraphPathIterable.builder(processorSupport) + .withStartNode(element) + .withPropertyFilter((rgp, prop) -> false) + .build(); + } + if ((element.getSourceInformation() == null) || (!allowPathsToExternalInstance && isExternalNode(element, instance, processorSupport))) + { + return GraphPathIterable.builder(processorSupport).build(); + } + return GraphPathIterable.builder(processorSupport) + .withStartNode(element) + .withPropertyFilter(GraphTools::internalPropertyFilter) + .withPathFilter(rgp -> + { + CoreInstance node = rgp.getLastResolvedNode(); + return (node == instance) ? + GraphWalkFilterResult.ACCEPT_AND_STOP : + GraphWalkFilterResult.reject(!isExternalNode(element, node, processorSupport)); + }) + .build(); + } + + private static boolean isExternalNode(CoreInstance element, CoreInstance node, ProcessorSupport processorSupport) + { + if (node == element) + { + return false; + } + + SourceInformation nodeSourceInfo = node.getSourceInformation(); + return ((nodeSourceInfo == null) ? _Package.isPackage(node, processorSupport) : !element.getSourceInformation().subsumes(nodeSourceInfo)); + } + + private static boolean internalPropertyFilter(CoreInstance node, String key) + { + switch (key) + { + case M3Properties._package: + { + return !M3PropertyPaths._package.equals(node.getRealKeyByName(key)); + } + case M3Properties.children: + { + return !M3PropertyPaths.children.equals(node.getRealKeyByName(key)); + } + default: + { + return true; + } + } + } + + private static boolean internalPropertyFilter(ResolvedGraphPath resolvedGraphPath, String property) + { + return internalPropertyFilter(resolvedGraphPath.getLastResolvedNode(), property); + } + + private static GraphWalkFilterResult internalNodeFilter(CoreInstance element, CoreInstance node, ProcessorSupport processorSupport) + { + return isExternalNode(element, node, processorSupport) ? GraphWalkFilterResult.REJECT_AND_STOP : GraphWalkFilterResult.ACCEPT_AND_CONTINUE; + } + + private static GraphWalkFilterResult internalPathFilter(CoreInstance element, ResolvedGraphPath resolvedGraphPath, ProcessorSupport processorSupport) + { + return internalNodeFilter(element, resolvedGraphPath.getLastResolvedNode(), processorSupport); + } + + public static GraphPathIterable allPathsBetween(String startNodePath, CoreInstance endNode, ProcessorSupport processorSupport) + { + return allPathsBetween(Sets.immutable.with(startNodePath), endNode, processorSupport); + } + + public static GraphPathIterable allPathsBetween(String startNodePath, CoreInstance endNode, int maxPathLength, ProcessorSupport processorSupport) + { + return allPathsBetween(Sets.immutable.with(startNodePath), endNode, maxPathLength, processorSupport); + } + + public static GraphPathIterable allPathsBetween(Iterable startNodePaths, CoreInstance endNode, ProcessorSupport processorSupport) + { + return allPathsBetween(startNodePaths, endNode, -1, processorSupport); + } + + public static GraphPathIterable allPathsBetween(Iterable startNodePaths, CoreInstance endNode, int maxPathLength, ProcessorSupport processorSupport) + { + return allPathsBetween(startNodePaths, endNode::equals, maxPathLength, processorSupport); + } + + public static GraphPathIterable allPathsBetween(String startNodePath, Iterable endNodes, ProcessorSupport processorSupport) + { + return allPathsBetween(Sets.immutable.with(startNodePath), endNodes, processorSupport); + } + + public static GraphPathIterable allPathsBetween(String startNodePath, Iterable endNodes, int maxPathLength, ProcessorSupport processorSupport) + { + return allPathsBetween(Sets.immutable.with(startNodePath), endNodes, maxPathLength, processorSupport); + } + + public static GraphPathIterable allPathsBetween(Iterable startNodePaths, Iterable endNodes, ProcessorSupport processorSupport) + { + return allPathsBetween(startNodePaths, endNodes, -1, processorSupport); + } + + public static GraphPathIterable allPathsBetween(Iterable startNodePaths, Iterable endNodes, int maxPathLength, ProcessorSupport processorSupport) + { + Set endNodesSet = (endNodes instanceof Set) ? (Set) endNodes : Sets.mutable.withAll(endNodes); + return allPathsBetween(startNodePaths, endNodesSet::contains, maxPathLength, processorSupport); + } + + private static GraphPathIterable allPathsBetween(Iterable startNodePaths, Predicate isEndNode, int maxPathLength, ProcessorSupport processorSupport) + { + return GraphPathIterable.builder(processorSupport) + .withStartNodePaths(startNodePaths) + .withPathFilter(rgp -> isEndNode.test(rgp.getLastResolvedNode()) ? + GraphWalkFilterResult.ACCEPT_AND_STOP : + GraphWalkFilterResult.reject(rgp.getGraphPath().getEdgeCount() < maxPathLength)) + .build(); + } +} diff --git a/legend-pure-core/legend-pure-m3-core/src/test/java/org/finos/legend/pure/m3/tests/AbstractCompiledStateIntegrityTest.java b/legend-pure-core/legend-pure-m3-core/src/test/java/org/finos/legend/pure/m3/tests/AbstractCompiledStateIntegrityTest.java index c9a158893b..e29a882bd1 100644 --- a/legend-pure-core/legend-pure-m3-core/src/test/java/org/finos/legend/pure/m3/tests/AbstractCompiledStateIntegrityTest.java +++ b/legend-pure-core/legend-pure-m3-core/src/test/java/org/finos/legend/pure/m3/tests/AbstractCompiledStateIntegrityTest.java @@ -78,6 +78,7 @@ import org.finos.legend.pure.m3.serialization.runtime.binary.PureRepositoryJar; import org.finos.legend.pure.m3.serialization.runtime.binary.SimplePureRepositoryJarLibrary; import org.finos.legend.pure.m3.serialization.runtime.binary.reference.ExternalReferenceSerializerLibrary; +import org.finos.legend.pure.m3.tools.GraphTools; import org.finos.legend.pure.m3.tools.PackageTreeIterable; import org.finos.legend.pure.m4.ModelRepository; import org.finos.legend.pure.m4.coreinstance.CoreInstance; @@ -413,12 +414,12 @@ public void testPackagedElementsHaveNonOverlappingSourceInfo() public void testPackagedElementsContainAllOthers() { MutableMap>>> elementsBySource = Maps.mutable.empty(); - CompiledStateIntegrityTestTools.getTopLevelAndPackagedIterable(repository).forEach(e -> + GraphTools.getTopLevelAndPackagedElements(repository).forEach(e -> { SourceInformation sourceInfo = e.getSourceInformation(); if (sourceInfo != null) { - MutableSet componentInstances = CompiledStateIntegrityTestTools.componentInstances(e).reject(n -> n.getSourceInformation() == null).toSet(); + MutableSet componentInstances = GraphTools.getComponentInstances(e, processorSupport).reject(n -> n.getSourceInformation() == null).toSet(); elementsBySource.getIfAbsentPut(sourceInfo.getSourceId(), Lists.mutable::empty).add(Tuples.pair(e, componentInstances)); } }); @@ -428,7 +429,7 @@ public void testPackagedElementsContainAllOthers() GraphNodeIterable.builder() .withStartingNodes(repository.getTopLevels()) .withKeyFilter((instance, key) -> !M3Properties.referenceUsages.equals(key) || !M3PropertyPaths.referenceUsages.equals(instance.getRealKeyByName(key))) - .withNodeFilter(n -> GraphWalkFilterResult.get((n.getSourceInformation() != null) && !ReferenceUsage.isReferenceUsage(n, processorSupport), true)) + .withNodeFilter(n -> GraphWalkFilterResult.cont((n.getSourceInformation() != null) && !ReferenceUsage.isReferenceUsage(n, processorSupport))) .build() .forEach(node -> { @@ -502,9 +503,18 @@ else if (!found.getTwo().contains(node)) sourceMiscontained.sortThisBy(p -> p.getOne().getSourceInformation()).forEach(pair -> { builder.append("\n\t\t").append(pair.getOne()); - pair.getOne().getSourceInformation().appendIntervalMessage(builder.append(" (")).append(") -> "); - PackageableElement.writeUserPathForPackageableElement(builder, pair.getTwo()); - pair.getTwo().getSourceInformation().appendIntervalMessage(builder.append(" (")).append(')'); + CoreInstance instance = pair.getOne(); + instance.getSourceInformation().appendIntervalMessage(builder.append(" (")).append(") is not contained in "); + + CoreInstance incorrectContainingElement = pair.getTwo(); + PackageableElement.writeUserPathForPackageableElement(builder, incorrectContainingElement); + incorrectContainingElement.getSourceInformation().appendIntervalMessage(builder.append(" (")).append(')'); + + ResolvedGraphPath path = GraphTools.findPathToInstance(instance, processorSupport, true); + if (path != null) + { + path.getGraphPath().writeDescription(builder.append("\n\t\t\tfound at: ")); + } }); }); } @@ -1207,7 +1217,7 @@ else if (Instance.instanceOf(instance, functionExpressionClass, processorSupport SourceInformation sourceInfo = function.getSourceInformation(); if (sourceInfo == null) { - ResolvedGraphPath path = CompiledStateIntegrityTestTools.findPathToInstance(function, processorSupport); + ResolvedGraphPath path = GraphTools.findPathToInstance(function, processorSupport); if (path != null) { path.getGraphPath().writeDescription(builder.append(" (")).append(')'); @@ -1495,7 +1505,7 @@ public void testSourceSerialization() public void testAnnotationModelElements() { MutableList errorMessages = Lists.mutable.empty(); - CompiledStateIntegrityTestTools.getTopLevelAndPackagedIterable(processorSupport).selectInstancesOf(Profile.class).forEach(profile -> + GraphTools.getTopLevelAndPackagedElements(processorSupport).selectInstancesOf(Profile.class).forEach(profile -> { profile._p_stereotypes().forEach(st -> { @@ -1594,7 +1604,7 @@ public void testAnnotationModelElements() public void testReferencerUsageOwners() { MutableList errorMessages = Lists.mutable.empty(); - CompiledStateIntegrityTestTools.getTopLevelAndPackagedIterable(processorSupport).forEach(element -> + GraphTools.getTopLevelAndPackagedElements(processorSupport).forEach(element -> { SourceInformation elementSourceInfo = element.getSourceInformation(); MutableSet internalNodes = GraphNodeIterable.builder() @@ -1632,7 +1642,7 @@ public void testReferencerUsageOwners() } else { - ResolvedGraphPath pathToNode = CompiledStateIntegrityTestTools.findPathToInstance(node, processorSupport); + ResolvedGraphPath pathToNode = GraphTools.findPathToInstance(node, processorSupport); if (pathToNode == null) { builder.append(node); diff --git a/legend-pure-core/legend-pure-m3-core/src/test/java/org/finos/legend/pure/m3/tests/CompiledStateIntegrityTestTools.java b/legend-pure-core/legend-pure-m3-core/src/test/java/org/finos/legend/pure/m3/tests/CompiledStateIntegrityTestTools.java index 16f443c4b9..7a59ce6c48 100644 --- a/legend-pure-core/legend-pure-m3-core/src/test/java/org/finos/legend/pure/m3/tests/CompiledStateIntegrityTestTools.java +++ b/legend-pure-core/legend-pure-m3-core/src/test/java/org/finos/legend/pure/m3/tests/CompiledStateIntegrityTestTools.java @@ -14,7 +14,6 @@ package org.finos.legend.pure.m3.tests; -import org.eclipse.collections.api.LazyIterable; import org.eclipse.collections.api.factory.Lists; import org.eclipse.collections.api.factory.Sets; import org.eclipse.collections.api.list.ListIterable; @@ -22,31 +21,21 @@ import org.eclipse.collections.api.map.MapIterable; import org.eclipse.collections.api.set.MutableSet; import org.eclipse.collections.impl.utility.Iterate; -import org.finos.legend.pure.m3.coreinstance.Package; -import org.finos.legend.pure.m3.coreinstance.PackageAccessor; import org.finos.legend.pure.m3.navigation.Instance; -import org.finos.legend.pure.m3.navigation.M3Paths; import org.finos.legend.pure.m3.navigation.M3Properties; -import org.finos.legend.pure.m3.navigation.M3PropertyPaths; import org.finos.legend.pure.m3.navigation.PackageableElement.PackageableElement; -import org.finos.legend.pure.m3.navigation.PrimitiveUtilities; import org.finos.legend.pure.m3.navigation.ProcessorSupport; import org.finos.legend.pure.m3.navigation._class._Class; import org.finos.legend.pure.m3.navigation.generictype.GenericType; -import org.finos.legend.pure.m3.navigation.graph.GraphPathIterable; import org.finos.legend.pure.m3.navigation.graph.ResolvedGraphPath; import org.finos.legend.pure.m3.navigation.multiplicity.Multiplicity; import org.finos.legend.pure.m3.navigation.property.Property; -import org.finos.legend.pure.m3.tools.PackageTreeIterable; -import org.finos.legend.pure.m4.ModelRepository; +import org.finos.legend.pure.m3.tools.GraphTools; import org.finos.legend.pure.m4.coreinstance.CoreInstance; import org.finos.legend.pure.m4.coreinstance.SourceInformation; -import org.finos.legend.pure.m4.tools.GraphNodeIterable; -import org.finos.legend.pure.m4.tools.GraphWalkFilterResult; import org.junit.Assert; import java.util.Formatter; -import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; @@ -240,9 +229,9 @@ static MutableSet forEachInstancePath(Iterable in static MutableSet forEachInstancePath(Iterable instances, Iterable startNodes, ProcessorSupport processorSupport, BiConsumer consumer) { MutableSet remaining = Sets.mutable.withAll(instances); - for (CoreInstance startNode : (startNodes == null) ? getTopLevelAndPackagedIterable(processorSupport).select(n -> n.getSourceInformation() != null) : startNodes) + for (CoreInstance startNode : (startNodes == null) ? GraphTools.getTopLevelAndPackagedElements(processorSupport).select(n -> n.getSourceInformation() != null) : startNodes) { - for (ResolvedGraphPath rgp : internalGraphPaths(startNode, processorSupport)) + for (ResolvedGraphPath rgp : GraphTools.internalGraphPaths(startNode, processorSupport)) { CoreInstance node = rgp.getLastResolvedNode(); if (remaining.remove(node)) @@ -258,166 +247,6 @@ static MutableSet forEachInstancePath(Iterable in return remaining; } - static LazyIterable getTopLevelAndPackagedIterable(ModelRepository repository) - { - return repository.getTopLevels() - .asLazy() - .concatenate(PackageTreeIterable.newRootPackageTreeIterable(repository) - .flatCollect(PackageAccessor::_children) - .collect(e -> (CoreInstance) e)); - } - - static LazyIterable getTopLevelAndPackagedIterable(ProcessorSupport processorSupport) - { - return PrimitiveUtilities.getPrimitiveTypes(processorSupport, Lists.mutable.ofInitialCapacity(PrimitiveUtilities.getPrimitiveTypeNames().size() + 2)) - .with(processorSupport.repository_getTopLevel(M3Paths.Package)) - .with(processorSupport.repository_getTopLevel(M3Paths.Root)) - .asLazy() - .concatenate(PackageTreeIterable.newRootPackageTreeIterable(processorSupport) - .flatCollect(PackageAccessor::_children) - .collect(e -> (CoreInstance) e)); - } - - static LazyIterable componentInstances(CoreInstance element) - { - SourceInformation sourceInfo = Objects.requireNonNull(element.getSourceInformation(), "element source information may not be null"); - return GraphNodeIterable.builder() - .withStartingNode(element) - .withKeyFilter((node, key) -> - { - switch (key) - { - case M3Properties._package: - { - return !M3PropertyPaths._package.equals(node.getRealKeyByName(key)); - } - case M3Properties.children: - { - return !M3PropertyPaths.children.equals(node.getRealKeyByName(key)); - } - default: - { - return true; - } - } - }) - .withNodeFilter(node -> - { - if (node == element) - { - return GraphWalkFilterResult.ACCEPT_AND_CONTINUE; - } - SourceInformation nodeSourceInfo = node.getSourceInformation(); - boolean internal = (nodeSourceInfo == null) ? !(node instanceof Package) : sourceInfo.subsumes(nodeSourceInfo); - return internal ? GraphWalkFilterResult.ACCEPT_AND_CONTINUE : GraphWalkFilterResult.REJECT_AND_STOP; - }) - .build(); - } - - static GraphPathIterable internalGraphPaths(CoreInstance element, ProcessorSupport processorSupport) - { - SourceInformation sourceInfo = Objects.requireNonNull(element.getSourceInformation(), "element source information may not be null"); - return GraphPathIterable.builder(processorSupport) - .withStartNode(element) - .withPropertyFilter((rgp, property) -> - { - switch (property) - { - case M3Properties._package: - { - return !M3PropertyPaths._package.equals(rgp.getLastResolvedNode().getRealKeyByName(property)); - } - case M3Properties.children: - { - return !M3PropertyPaths.children.equals(rgp.getLastResolvedNode().getRealKeyByName(property)); - } - default: - { - return true; - } - } - }) - .withPathFilter(rgp -> - { - CoreInstance node = rgp.getLastResolvedNode(); - if (node == element) - { - // all graph paths start from element and cannot contain loops: so this must be the starting path - return GraphWalkFilterResult.ACCEPT_AND_CONTINUE; - } - SourceInformation nodeSourceInfo = node.getSourceInformation(); - boolean internal = (nodeSourceInfo == null) ? !(node instanceof Package) : sourceInfo.subsumes(nodeSourceInfo); - return internal ? GraphWalkFilterResult.ACCEPT_AND_CONTINUE : GraphWalkFilterResult.REJECT_AND_STOP; - }) - .build(); - } - - static ResolvedGraphPath findPathToInstance(CoreInstance instance, ProcessorSupport processorSupport) - { - return getTopLevelAndPackagedIterable(processorSupport) - .flatCollect(e -> Lists.immutable.with(findPathToInstanceWithinElement(e, instance, processorSupport))) - .detect(Objects::nonNull); - } - - static ResolvedGraphPath findPathToInstanceWithinElement(CoreInstance element, CoreInstance instance, ProcessorSupport processorSupport) - { - SourceInformation sourceInfo = element.getSourceInformation(); - if (sourceInfo == null) - { - return null; - } - SourceInformation instanceSourceInfo = instance.getSourceInformation(); - if ((instanceSourceInfo != null) && !sourceInfo.subsumes(instanceSourceInfo)) - { - return null; - } - MutableSet visited = Sets.mutable.empty(); - return GraphPathIterable.builder(processorSupport) - .withStartNode(element) - .withPropertyFilter((rgp, property) -> - { - switch (property) - { - case M3Properties._package: - { - return !M3PropertyPaths._package.equals(rgp.getLastResolvedNode().getRealKeyByName(property)); - } - case M3Properties.children: - { - return !M3PropertyPaths.children.equals(rgp.getLastResolvedNode().getRealKeyByName(property)); - } - default: - { - return true; - } - } - }) - .withPathFilter(rgp -> - { - CoreInstance node = rgp.getLastResolvedNode(); - if (node == instance) - { - return GraphWalkFilterResult.ACCEPT_AND_STOP; - } - if (node == element) - { - // all graph paths start from element and cannot contain loops: so this must be the starting path - return GraphWalkFilterResult.REJECT_AND_CONTINUE; - } - if (visited.add(node)) - { - SourceInformation nodeSourceInfo = node.getSourceInformation(); - if ((nodeSourceInfo == null) ? !(node instanceof Package) : sourceInfo.subsumes(nodeSourceInfo)) - { - return GraphWalkFilterResult.REJECT_AND_CONTINUE; - } - } - return GraphWalkFilterResult.REJECT_AND_STOP; - }) - .build() - .getAny(); - } - private static void runIntegrityTest(Iterable instances, String violationDescription, BiConsumer> test) { MutableList errorMessages = Lists.mutable.empty(); @@ -660,7 +489,7 @@ else if (!GenericType.isGenericCompatibleWith(valueGenericType, genericType, pro { sourceInfo.appendMessage(message.append(" (")).append(')'); } - ResolvedGraphPath path = findPathToInstance(instance, processorSupport); + ResolvedGraphPath path = GraphTools.findPathToInstance(instance, processorSupport); if (path != null) { if (sourceInfo == null) diff --git a/legend-pure-core/legend-pure-m3-core/src/test/java/org/finos/legend/pure/m3/tests/elements/function/inference/TestFunctionTypeInference.java b/legend-pure-core/legend-pure-m3-core/src/test/java/org/finos/legend/pure/m3/tests/elements/function/inference/TestFunctionTypeInference.java index 00b11307d0..72422d76dc 100644 --- a/legend-pure-core/legend-pure-m3-core/src/test/java/org/finos/legend/pure/m3/tests/elements/function/inference/TestFunctionTypeInference.java +++ b/legend-pure-core/legend-pure-m3-core/src/test/java/org/finos/legend/pure/m3/tests/elements/function/inference/TestFunctionTypeInference.java @@ -1010,6 +1010,45 @@ public void testIfWithFuncTypesAndTypeParams() assertGenericTypeEquals("meta::pure::metamodel::function::LambdaFunction<{T[1]->String[1]}>", mapFn._classifierGenericType()); } + @Test + public void testGeneralizationWithTypeArguments() + { + compileInferenceTest( + "import test::*;\n" + + "\n" + + "Class test::IntStringPair extends Pair\n" + + "{\n" + + "}\n" + + "\n" + + "function test::getValue(pairs:Pair[*], key:X[1]):Y[1]\n" + + "{\n" + + " $pairs->find(p | $key == $p.first)->toOne().second\n" + + "}\n" + + "\n" + + "function test::process(key:Integer[1]):String[1]\n" + + "{\n" + + " [\n" + + " ^IntStringPair(first=1, second='the quick brown fox'),\n" + + " ^IntStringPair(first=2, second='jumped over the'),\n" + + " ^IntStringPair(first=3, second='lazy dog')\n" + + " ]->getValue($key)\n" + + "}\n"); + ConcreteFunctionDefinition processFn = (ConcreteFunctionDefinition) runtime.getFunction("test::process(Integer[1]):String[1]"); + SimpleFunctionExpression getValueExpr = (SimpleFunctionExpression) processFn._expressionSequence().getOnly(); + assertGenericTypeEquals("String", getValueExpr._genericType()); + + ListIterable getValueArgs = ListHelper.wrapListIterable(getValueExpr._parametersValues()); + Assert.assertEquals(2, getValueArgs.size()); + assertGenericTypeEquals("test::IntStringPair", getValueArgs.get(0)._genericType()); + assertGenericTypeEquals("Integer", getValueArgs.get(1)._genericType()); + + ListIterable resolvedTypeParams = ListHelper.wrapListIterable(getValueExpr._resolvedTypeParameters()); + assertGenericTypeEquals("Integer", resolvedTypeParams.get(0)); + Assert.assertEquals(getValueExpr.getSourceInformation(), resolvedTypeParams.get(0).getSourceInformation()); + assertGenericTypeEquals("String", resolvedTypeParams.get(1)); + Assert.assertEquals(getValueExpr.getSourceInformation(), resolvedTypeParams.get(1).getSourceInformation()); + } + @Test public void testChainedFilters() { diff --git a/legend-pure-core/legend-pure-m4/src/main/java/org/finos/legend/pure/m4/tools/GraphWalkFilterResult.java b/legend-pure-core/legend-pure-m4/src/main/java/org/finos/legend/pure/m4/tools/GraphWalkFilterResult.java index 025a0cb3db..8274e23472 100644 --- a/legend-pure-core/legend-pure-m4/src/main/java/org/finos/legend/pure/m4/tools/GraphWalkFilterResult.java +++ b/legend-pure-core/legend-pure-m4/src/main/java/org/finos/legend/pure/m4/tools/GraphWalkFilterResult.java @@ -83,8 +83,26 @@ public GraphWalkFilterResult disjoin(GraphWalkFilterResult other) public static GraphWalkFilterResult get(boolean accept, boolean cont) { - return accept ? - (cont ? ACCEPT_AND_CONTINUE : ACCEPT_AND_STOP) : - (cont ? REJECT_AND_CONTINUE : REJECT_AND_STOP); + return accept ? accept(cont) : reject(cont); + } + + public static GraphWalkFilterResult accept(boolean cont) + { + return cont ? ACCEPT_AND_CONTINUE : ACCEPT_AND_STOP; + } + + public static GraphWalkFilterResult reject(boolean cont) + { + return cont ? REJECT_AND_CONTINUE : REJECT_AND_STOP; + } + + public static GraphWalkFilterResult cont(boolean accept) + { + return accept ? ACCEPT_AND_CONTINUE : REJECT_AND_CONTINUE; + } + + public static GraphWalkFilterResult stop(boolean accept) + { + return accept ? ACCEPT_AND_STOP : REJECT_AND_STOP; } }