diff --git a/legend-engine-core/legend-engine-core-base/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/CompileContext.java b/legend-engine-core/legend-engine-core-base/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/CompileContext.java index 3b4bfa7f02..99c4b3a1fe 100644 --- a/legend-engine-core/legend-engine-core-base/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/CompileContext.java +++ b/legend-engine-core/legend-engine-core-base/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/CompileContext.java @@ -76,6 +76,7 @@ public class CompileContext "meta::pure::metamodel::function::property", "meta::pure::metamodel::extension", "meta::pure::metamodel::import", + "meta::pure::metamodel::relation", "meta::pure::functions::date", "meta::pure::functions::string", "meta::pure::functions::collection", diff --git a/legend-engine-core/legend-engine-core-base/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/PackageableElementFifthPassBuilder.java b/legend-engine-core/legend-engine-core-base/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/PackageableElementFifthPassBuilder.java index b6cc52923c..a64a1794b0 100644 --- a/legend-engine-core/legend-engine-core-base/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/PackageableElementFifthPassBuilder.java +++ b/legend-engine-core/legend-engine-core-base/legend-engine-core-language-pure/legend-engine-language-pure-compiler/src/main/java/org/finos/legend/engine/language/pure/compiler/toPureGraph/PackageableElementFifthPassBuilder.java @@ -37,6 +37,7 @@ import org.finos.legend.pure.m3.coreinstance.meta.pure.mapping.AssociationImplementation; import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.PackageableElement; 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.valuespecification.ValueSpecification; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -100,7 +101,14 @@ public PackageableElement visit(Function function) throw e; } FunctionType fType = ((FunctionType) targetFunc._classifierGenericType()._typeArguments().getFirst()._rawType()); - HelperModelBuilder.checkCompatibility(this.context, body.getLast()._genericType()._rawType(), body.getLast()._multiplicity(), fType._returnType()._rawType(), fType._returnMultiplicity(), "Error in function '" + packageString + "'", function.body.get(function.body.size() - 1).sourceInformation); + GenericType lastStatementReturnType = body.getLast()._genericType(); + HelperModelBuilder.checkCompatibility(this.context, lastStatementReturnType._rawType(), body.getLast()._multiplicity(), fType._returnType()._rawType(), fType._returnMultiplicity(), "Error in function '" + packageString + "'", function.body.get(function.body.size() - 1).sourceInformation); + + // concrete functions do not support type arguments (ie. the diamond within Pair) in Engine grammar + // Hence, if the raw types are compatible per above checks, we will infer the type arguments from the expression itself + // and propagate it on the function return type to ensure the Pure graph is constructed correctly + fType._returnType()._typeArguments(lastStatementReturnType._typeArguments()); + ctx.pop(); targetFunc._expressionSequence(body); HelperFunctionBuilder.processFunctionSuites(function, targetFunc, this.context, ctx); diff --git a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-grammar/src/test/java/org/finos/legend/engine/language/pure/compiler/test/TestRelationStoreAccessorFromGrammar.java b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-grammar/src/test/java/org/finos/legend/engine/language/pure/compiler/test/TestRelationStoreAccessorFromGrammar.java index 179b7a77d3..f162941c3b 100644 --- a/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-grammar/src/test/java/org/finos/legend/engine/language/pure/compiler/test/TestRelationStoreAccessorFromGrammar.java +++ b/legend-engine-xts-relationalStore/legend-engine-xt-relationalStore-generation/legend-engine-xt-relationalStore-grammar/src/test/java/org/finos/legend/engine/language/pure/compiler/test/TestRelationStoreAccessorFromGrammar.java @@ -14,7 +14,18 @@ package org.finos.legend.engine.language.pure.compiler.test; +import java.util.List; +import java.util.stream.Collectors; +import org.eclipse.collections.api.factory.Lists; +import org.eclipse.collections.api.tuple.Pair; +import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel; +import org.finos.legend.engine.language.pure.compiler.toPureGraph.RelationTypeHelper; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData; +import org.finos.legend.engine.protocol.pure.v1.model.relationType.Column; import org.finos.legend.engine.shared.core.operational.errorManagement.EngineException; +import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.function.ConcreteFunctionDefinition; +import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.relation.RelationType; +import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.FunctionType; import org.junit.Assert; import org.junit.Test; @@ -24,7 +35,7 @@ public class TestRelationStoreAccessorFromGrammar extends TestCompilationFromGra @Test public void testCompilationOfRelationStoreAccessor() { - test("###Relational\n" + + Pair pureModelPair = test("###Relational\n" + "Database my::Store" + "(" + " Table myTable" + @@ -34,10 +45,15 @@ public void testCompilationOfRelationStoreAccessor() " )" + ")\n" + "###Pure\n" + - "function my::func():Any[*]" + + "function my::func():Relation[1]" + "{" + " #>{my::Store.myTable}#->filter(c|$c.name == 'ok');" + "}"); + + ConcreteFunctionDefinition func = pureModelPair.getTwo().getConcreteFunctionDefinition("my::func__Relation_1_", null); + FunctionType fType = (FunctionType) func._classifierGenericType()._typeArguments().getOnly()._rawType(); + List columns = RelationTypeHelper.convert((RelationType) fType._returnType()._typeArguments().getAny()._rawType()).columns; + Assert.assertEquals(Lists.mutable.with("id: Integer", "name: String"), columns.stream().map(x -> x.name + ": " + x.type).collect(Collectors.toList())); } @Test @@ -91,7 +107,7 @@ public void testCompilationWithSchema() "\n" + "\n" + "###Pure\n" + - "function my::func(): Any[*]\n" + + "function my::func(): Relation[1]\n" + "{\n" + " #>{my::Store.mySchema.myTable}#->filter(\n" + " c|$c.name == 'ok'\n" + @@ -118,7 +134,7 @@ public void testCompilationWithSchemaError() "\n" + "\n" + "###Pure\n" + - "function my::func(): Any[*]\n" + + "function my::func(): Relation[1]\n" + "{\n" + " #>{my::Store.SchemaMissing.myTable}#->filter(\n" + " c|$c.name == 'ok'\n" + @@ -141,7 +157,7 @@ public void testCompilationOfRelationStoreAccessorUnknownTable() " )" + ")\n" + "###Pure\n" + - "function my::func():Any[*]" + + "function my::func():Relation[1]" + "{" + " #>{my::Store.myTabe}#->filter(c|$c.name == 'ok');" + "}"); @@ -149,7 +165,7 @@ public void testCompilationOfRelationStoreAccessorUnknownTable() } catch (EngineException e) { - Assert.assertEquals("COMPILATION error at [4:31-51]: The table myTabe can't be found in the store Store", e.toPretty()); + Assert.assertEquals("COMPILATION error at [4:36-56]: The table myTabe can't be found in the store Store", e.toPretty()); } } @@ -168,7 +184,7 @@ public void testCompilationOfRelationStoreAccessorUnknownColumn() " )" + ")\n" + "###Pure\n" + - "function my::func():Any[*]" + + "function my::func():Relation[1]" + "{" + " #>{my::Store.myTable}#->filter(c|$c.naeme == 'ok');" + "}"); @@ -176,7 +192,7 @@ public void testCompilationOfRelationStoreAccessorUnknownColumn() } catch (EngineException e) { - Assert.assertEquals("COMPILATION error at [4:67-71]: The column 'naeme' can't be found in the relation (id:Integer, name:String)", e.toPretty()); + Assert.assertEquals("COMPILATION error at [4:72-76]: The column 'naeme' can't be found in the relation (id:Integer, name:String)", e.toPretty()); } } @@ -196,7 +212,7 @@ public void testCompilationErrorMissingTable() " )" + ")\n" + "###Pure\n" + - "function my::func():Any[*]" + + "function my::func():Relation[1]" + "{" + " #>{my::Store}#->filter(c|$c.naeme == 'ok');" + "}"); @@ -204,7 +220,7 @@ public void testCompilationErrorMissingTable() } catch (EngineException e) { - Assert.assertEquals("COMPILATION error at [4:31-44]: Error in the accessor definition. Please provide a table.", e.toPretty()); + Assert.assertEquals("COMPILATION error at [4:36-49]: Error in the accessor definition. Please provide a table.", e.toPretty()); } }