Skip to content

Commit

Permalink
[NF] Enforce function purity rules better.
Browse files Browse the repository at this point in the history
- Give a warning if any pure function contains calls to impure
  functions, and mark such functions as impure themselves to make sure
  they're not constant evaluated.
  • Loading branch information
perost committed Jun 24, 2020
1 parent d65e6e3 commit 0c5b95d
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 0 deletions.
29 changes: 29 additions & 0 deletions OMCompiler/Compiler/NFFrontEnd/NFExpression.mo
Expand Up @@ -299,6 +299,16 @@ public
end match;
end isCall;

function isImpureCall
input Expression exp;
output Boolean isImpure;
algorithm
isImpure := match exp
case CALL() then Call.isImpure(exp.call);
else false;
end match;
end isImpureCall;

function isTrue
input Expression exp;
output Boolean isTrue;
Expand Down Expand Up @@ -1801,6 +1811,25 @@ public
end match;
end isNonAssociativeExp;

function getName
"Returns the 'name' of an Expression, for example the function name of a
call or the record class name of a record expression."
input Expression exp;
output String name;
algorithm
name := match exp
case RECORD() then AbsynUtil.pathString(exp.path);
case CALL() then AbsynUtil.pathString(Call.functionName(exp.call));
case CAST() then getName(exp.exp);
case BOX() then getName(exp.exp);
case UNBOX() then getName(exp.exp);
case MUTABLE() then getName(Mutable.access(exp.exp));
case PARTIAL_FUNCTION_APPLICATION() then ComponentRef.toString(exp.fn);
case BINDING_EXP() then getName(exp.exp);
else toString(exp);
end match;
end getName;

function toDAEOpt
input Option<Expression> exp;
output Option<DAE.Exp> dexp;
Expand Down
35 changes: 35 additions & 0 deletions OMCompiler/Compiler/NFFrontEnd/NFFunction.mo
Expand Up @@ -1314,6 +1314,9 @@ uniontype Function
"Types the body of a function, along with any bindings of local variables
and outputs."
input output Function fn;
protected
Boolean pure;
DAE.FunctionAttributes attr;
algorithm
// Type the bindings of the outputs and local variables.
for c in fn.outputs loop
Expand All @@ -1331,8 +1334,40 @@ uniontype Function
for fn_der in fn.derivatives loop
FunctionDerivative.typeDerivative(fn_der);
end for;

// If the function is pure, check that it doesn't contain any impure calls.
if not isImpure(fn) then
pure := foldExp(fn, function checkPureCall(fn = fn), true);

// The function does contain impure calls, mark the function as impure.
if not pure then
attr := fn.attributes;
attr.isImpure := true;
fn.attributes := attr;
end if;
end if;
end typeFunctionBody;

function checkPureCall
input Expression exp;
input Function fn;
input output Boolean pure;
algorithm
if not pure then
return;
end if;

if Expression.isImpureCall(exp) then
pure := false;

if Config.languageStandardAtLeast(Config.LanguageStandard.'3.3') then
Error.addSourceMessage(Error.PURE_FUNCTION_WITH_IMPURE_CALLS,
{AbsynUtil.pathString(Function.name(fn)), Expression.getName(exp)},
InstNode.info(fn.node));
end if;
end if;
end checkPureCall;

function boxFunctionParameter
input InstNode component;
protected
Expand Down
2 changes: 2 additions & 0 deletions OMCompiler/Compiler/Util/Error.mo
Expand Up @@ -804,6 +804,8 @@ public constant ErrorTypes.Message EXPERIMENTAL_REQUIRED = ErrorTypes.MESSAGE(36
Gettext.gettext("%s is an experimental feature and requires the --std=experimental flag."));
public constant ErrorTypes.Message INVALID_NUMBER_OF_DIMENSIONS_FOR_PROMOTE = ErrorTypes.MESSAGE(366, ErrorTypes.TRANSLATION(), ErrorTypes.ERROR(),
Gettext.gettext("The second argument ‘%s‘ of promote may not be smaller than the number of dimensions (%s) of the first argument."));
public constant ErrorTypes.Message PURE_FUNCTION_WITH_IMPURE_CALLS = ErrorTypes.MESSAGE(367, ErrorTypes.TRANSLATION(), ErrorTypes.WARNING(),
Gettext.gettext("Pure function ‘%s‘ contains a call to impure function ‘%s‘."));

public constant ErrorTypes.Message INITIALIZATION_NOT_FULLY_SPECIFIED = ErrorTypes.MESSAGE(496, ErrorTypes.TRANSLATION(), ErrorTypes.WARNING(),
Gettext.gettext("The initial conditions are not fully specified. %s."));
Expand Down
42 changes: 42 additions & 0 deletions testsuite/flattening/modelica/scodeinst/ImpureCall1.mo
@@ -0,0 +1,42 @@
// name: ImpureCall1
// keywords:
// status: correct
// cflags: -d=newInst
//
//

impure function f
input Real x;
output Real y = x;
end f;

function f2
input Real x;
output Real y;
algorithm
y := f(x);
end f2;

model ImpureCall1
Real x = f2(1.0);
end ImpureCall1;

// Result:
// impure function f
// input Real x;
// output Real y = x;
// end f;
//
// impure function f2
// input Real x;
// output Real y;
// algorithm
// y := f(x);
// end f2;
//
// class ImpureCall1
// Real x = f2(1.0);
// end ImpureCall1;
// [flattening/modelica/scodeinst/ImpureCall1.mo:13:1-18:7:writable] Warning: Pure function ‘f2‘ contains a call to impure function ‘f‘.
//
// endResult
1 change: 1 addition & 0 deletions testsuite/flattening/modelica/scodeinst/Makefile
Expand Up @@ -587,6 +587,7 @@ ImportSubPackage2.mo \
ImportUnqualified1.mo \
ImportUnqualified2.mo \
ImportUnqualified3.mo \
ImpureCall1.mo \
Inline1.mo \
InnerOuter1.mo \
InnerOuter2.mo \
Expand Down

0 comments on commit 0c5b95d

Please sign in to comment.