Skip to content

Commit

Permalink
propagate type arguments from expression to function return type (#3124)
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelbey authored Sep 26, 2024
1 parent dbda39a commit 97e1de8
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String, String>) 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -24,7 +35,7 @@ public class TestRelationStoreAccessorFromGrammar extends TestCompilationFromGra
@Test
public void testCompilationOfRelationStoreAccessor()
{
test("###Relational\n" +
Pair<PureModelContextData, PureModel> pureModelPair = test("###Relational\n" +
"Database my::Store" +
"(" +
" Table myTable" +
Expand All @@ -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<Column> 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
Expand Down Expand Up @@ -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" +
Expand All @@ -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" +
Expand All @@ -141,15 +157,15 @@ public void testCompilationOfRelationStoreAccessorUnknownTable()
" )" +
")\n" +
"###Pure\n" +
"function my::func():Any[*]" +
"function my::func():Relation[1]" +
"{" +
" #>{my::Store.myTabe}#->filter(c|$c.name == 'ok');" +
"}");
Assert.fail();
}
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());
}
}

Expand All @@ -168,15 +184,15 @@ public void testCompilationOfRelationStoreAccessorUnknownColumn()
" )" +
")\n" +
"###Pure\n" +
"function my::func():Any[*]" +
"function my::func():Relation[1]" +
"{" +
" #>{my::Store.myTable}#->filter(c|$c.naeme == 'ok');" +
"}");
Assert.fail();
}
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());
}
}

Expand All @@ -196,15 +212,15 @@ public void testCompilationErrorMissingTable()
" )" +
")\n" +
"###Pure\n" +
"function my::func():Any[*]" +
"function my::func():Relation[1]" +
"{" +
" #>{my::Store}#->filter(c|$c.naeme == 'ok');" +
"}");
Assert.fail();
}
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());
}
}

Expand Down

0 comments on commit 97e1de8

Please sign in to comment.