Skip to content

Commit

Permalink
Handle default external calls with array arguments
Browse files Browse the repository at this point in the history
This fixes ticket:4009 and also should add support for handling
protected variables as they should be handled.
  • Loading branch information
sjoelund committed Jul 27, 2016
1 parent c709213 commit 75fffde
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 175 deletions.
157 changes: 99 additions & 58 deletions Compiler/FrontEnd/InstFunction.mo
Expand Up @@ -54,6 +54,7 @@ public import UnitAbsyn;

protected import Lookup;
protected import Inst;
import DAEDump;
protected import InstUtil;
protected import UnitAbsynBuilder;
protected import ElementSource;
Expand Down Expand Up @@ -322,6 +323,7 @@ algorithm
SCode.Visibility vis;
SCode.Partial partialPrefix;
SCode.Encapsulated encapsulatedPrefix;
SCode.ExternalDecl scExtdecl;
DAE.ExternalDecl extdecl;
SCode.Restriction restr;
SCode.ClassDef parts;
Expand Down Expand Up @@ -385,7 +387,7 @@ algorithm

// External functions should also have their type in env, but no dae.
case (cache,env,ih,mod,pre,(c as SCode.CLASS(partialPrefix=partialPrefix, prefixes=SCode.PREFIXES(visibility=visibility), name = n,restriction = (restr as SCode.R_FUNCTION(SCode.FR_EXTERNAL_FUNCTION(isImpure))),
classDef = cd as (parts as SCode.PARTS()), cmt=cmt, info=info, encapsulatedPrefix = encapsulatedPrefix)),inst_dims,_)
classDef = cd as (parts as SCode.PARTS(externalDecl=SOME(scExtdecl))), cmt=cmt, info=info, encapsulatedPrefix = encapsulatedPrefix)),inst_dims,_)
equation
(cache,cenv,ih,_,DAE.DAE(daeElts),_,ty,_,_,_) =
Inst.instClass(cache,env,ih, UnitAbsynBuilder.emptyInstStore(),mod, pre,
Expand All @@ -410,7 +412,7 @@ algorithm
ClassInf.FUNCTION(fpath,isImpure), n,parts, restr, vis, partialPrefix,
encapsulatedPrefix, inst_dims, true, InstTypes.INNER_CALL(),
ConnectionGraph.EMPTY, Connect.emptySet, NONE(), cmt, info) "how to get this? impl" ;
(cache,ih,extdecl) = instExtDecl(cache, tempenv,ih, n, parts, true, pre,info) "impl" ;
(cache,ih,extdecl) = instExtDecl(cache, tempenv, ih, n, scExtdecl, daeElts, ty1, true, pre,info) "impl" ;

// set the source of this element
source = ElementSource.createElementSource(info, FGraph.getScopePath(env), pre);
Expand Down Expand Up @@ -679,66 +681,105 @@ protected function instExtDecl
that type. If no explicit call and only one output parameter exists, then
this will be the return type of the function, otherwise the return type
will be void."
input FCore.Cache inCache;
input FCore.Graph inEnv;
input InnerOuter.InstHierarchy inIH;
input String inIdent;
input SCode.ClassDef inClassDef;
input Boolean inBoolean;
input Prefix.Prefix inPrefix;
input output FCore.Cache cache;
input FCore.Graph env;
input output InnerOuter.InstHierarchy iH;
input String name;
input SCode.ExternalDecl inScExtDecl;
input list<DAE.Element> inElements;
input DAE.Type funcType;
input Boolean impl;
input Prefix.Prefix pre;
input SourceInfo info;
output FCore.Cache outCache;
output InnerOuter.InstHierarchy outIH;
output DAE.ExternalDecl outExternalDecl;
output DAE.ExternalDecl daeextdecl;
protected
String fname,lang;
list<DAE.ExtArg> fargs;
DAE.ExtArg rettype;
Option<SCode.Annotation> ann;
SCode.ExternalDecl extdecl=inScExtDecl;
algorithm
(outCache,outIH,outExternalDecl) := matchcontinue (inCache,inEnv,inIH,inIdent,inClassDef,inBoolean,inPrefix,info)
local
String fname,lang,n;
list<DAE.ExtArg> fargs;
DAE.ExtArg rettype;
Option<SCode.Annotation> ann;
DAE.ExternalDecl daeextdecl;
FCore.Graph env;
SCode.ExternalDecl extdecl,orgextdecl;
Boolean impl;
list<SCode.Element> els;
FCore.Cache cache;
InstanceHierarchy ih;
Prefix.Prefix pre;

case (cache,env,ih,n,SCode.PARTS(externalDecl = SOME(extdecl)),impl,pre,_) /* impl */
equation
InstUtil.isExtExplicitCall(extdecl);
fname = InstUtil.instExtGetFname(extdecl, n);
(cache,fargs) = InstUtil.instExtGetFargs(cache,env, extdecl, impl,pre,info);
(cache,rettype) = InstUtil.instExtGetRettype(cache,env, extdecl, impl,pre,info);
lang = InstUtil.instExtGetLang(extdecl);
ann = InstUtil.instExtGetAnnotation(extdecl);
daeextdecl = DAE.EXTERNALDECL(fname,fargs,rettype,lang,ann);
then
(cache,ih,daeextdecl);
ann := InstUtil.instExtGetAnnotation(extdecl);
lang := InstUtil.instExtGetLang(extdecl);
fname := InstUtil.instExtGetFname(extdecl, name);
if not InstUtil.isExtExplicitCall(extdecl) then
(fargs,rettype) := instExtMakeDefaultExternalCall(inElements, funcType, lang, info);
else
(cache,fargs) := InstUtil.instExtGetFargs(cache,env,extdecl,impl,pre,info);
(cache,rettype) := InstUtil.instExtGetRettype(cache,env,extdecl,impl,pre,info);
end if;
daeextdecl := DAE.EXTERNALDECL(fname,fargs,rettype,lang,ann);
end instExtDecl;

case (cache,env,ih,n,SCode.PARTS(elementLst = els,externalDecl = SOME(orgextdecl)),impl,pre,_)
equation
failure(InstUtil.isExtExplicitCall(orgextdecl));
extdecl = InstUtil.instExtMakeExternaldecl(n, els, orgextdecl);
(fname) = InstUtil.instExtGetFname(extdecl, n);
(cache,fargs) = InstUtil.instExtGetFargs(cache,env, extdecl, impl,pre,info);
(cache,rettype) = InstUtil.instExtGetRettype(cache,env, extdecl, impl,pre,info);
lang = InstUtil.instExtGetLang(extdecl);
ann = InstUtil.instExtGetAnnotation(orgextdecl);
daeextdecl = DAE.EXTERNALDECL(fname,fargs,rettype,lang,ann);
then
(cache,ih,daeextdecl);
protected function instExtMakeDefaultExternalCall
" This function generates a default explicit function call,
when it is omitted. If only one output variable exists,
the implicit call is equivalent to:
external \"C\" output_var=func(input_var1, input_var2,...)
with the input_vars in their declaration order. If several output
variables exists, the implicit call is equivalent to:
external \"C\" func(var1, var2, ...)
where each var can be input or output."
input list<DAE.Element> elements;
input DAE.Type funcType;
input String lang;
input SourceInfo info;
output list<DAE.ExtArg> fargs;
output DAE.ExtArg rettype;
protected
DAE.Type ty;
Boolean singleOutput;
DAE.ComponentRef cr;
DAE.Element e;
algorithm
fargs := {};
if lang=="builtin" then
rettype := DAE.NOEXTARG();
return;
end if;
(rettype,singleOutput) := match funcType
case DAE.T_FUNCTION(funcResultType=DAE.T_ARRAY())
algorithm
if lang<>"builtin" then
Error.addSourceMessage(Error.EXT_FN_SINGLE_RETURN_ARRAY, {lang}, info);
end if;
then (DAE.NOEXTARG(),false);
case DAE.T_FUNCTION(funcResultType=DAE.T_TUPLE())
then (DAE.NOEXTARG(),false);
case DAE.T_FUNCTION(funcResultType=DAE.T_NORETCALL())
then (DAE.NOEXTARG(),false);
case DAE.T_FUNCTION(funcResultType=ty)
then (DAE.EXTARG(DAEUtil.varCref(List.find(elements, DAEUtil.isOutputVar)), Absyn.OUTPUT(), ty), true);
else
equation
true = Flags.isSet(Flags.FAILTRACE);
Debug.trace("#-- Inst.instExtDecl failed\n");
then
fail();

end matchcontinue;
end instExtDecl;
algorithm
Error.addInternalError("instExtMakeDefaultExternalCall failed for " + Types.unparseType(funcType), info);
then fail();
end match;
for elt in elements loop
fargs := match elt
case DAE.VAR(direction=DAE.OUTPUT()) guard not singleOutput
then addExtVarToCall(elt.componentRef, Absyn.OUTPUT(), elt.dims, fargs);
case DAE.VAR(direction=DAE.INPUT())
then addExtVarToCall(elt.componentRef, Absyn.INPUT(), elt.dims, fargs);
case DAE.VAR(direction=DAE.BIDIR())
then addExtVarToCall(elt.componentRef, Absyn.OUTPUT(), elt.dims, fargs);
else fargs;
end match;
end for;
fargs := listReverse(fargs);
end instExtMakeDefaultExternalCall;

protected function addExtVarToCall
input DAE.ComponentRef cr;
input Absyn.Direction dir;
input DAE.Dimensions dims;
input output list<DAE.ExtArg> fargs;
algorithm
fargs := DAE.EXTARG(cr, dir, ComponentReference.crefTypeFull(cr))::fargs;
for dim in 1:listLength(dims) loop
fargs := DAE.EXTARGSIZE(cr, ComponentReference.crefTypeFull(cr), DAE.ICONST(dim))::fargs;
end for;
end addExtVarToCall;

public function getRecordConstructorFunction
input FCore.Cache inCache;
Expand Down
124 changes: 7 additions & 117 deletions Compiler/FrontEnd/InstUtil.mo
Expand Up @@ -4801,6 +4801,9 @@ public function checkExternalFunction "
protected
Integer i;
algorithm
if decl.language == "builtin" then
return;
end if;
List.map2_0(els,checkExternalFunctionOutputAssigned,decl,name);
checkFunctionInputUsed(els,SOME(decl),name);
end checkExternalFunction;
Expand Down Expand Up @@ -5005,69 +5008,14 @@ public function isExtExplicitCall
"If the external function id is present, then a function call must
exist, i.e. explicit call was written in the external clause."
input SCode.ExternalDecl inExternalDecl;
output Boolean isExplicit;
algorithm
_ := match (inExternalDecl)
local String id;
case SCode.EXTERNALDECL(funcName = SOME(_)) then ();
isExplicit := match (inExternalDecl)
case SCode.EXTERNALDECL(funcName = SOME(_)) then true;
else false;
end match;
end isExtExplicitCall;

public function instExtMakeExternaldecl
"author: LS
This function generates a default explicit function call,
when it is omitted. If only one output variable exists,
the implicit call is equivalent to:
external \"C\" output_var=func(input_var1, input_var2,...)
with the input_vars in their declaration order. If several output
variables exists, the implicit call is equivalent to:
external \"C\" func(var1, var2, ...)
where each var can be input or output."
input String inIdent;
input list<SCode.Element> inSCodeElementLst;
input SCode.ExternalDecl inExternalDecl;
output SCode.ExternalDecl outExternalDecl;
algorithm
outExternalDecl := matchcontinue (inIdent,inSCodeElementLst,inExternalDecl)
local
SCode.Element outvar;
list<SCode.Element> invars,els,inoutvars;
list<list<Absyn.Exp>> explists;
list<Absyn.Exp> exps;
Absyn.ComponentRef retcref;
SCode.ExternalDecl extdecl;
String id;
Option<String> lang;

/* the case with only one output var, and that cannot be
* array, otherwise instExtMakeCrefs outvar will fail
*/
case (id,els,SCode.EXTERNALDECL(lang = lang))
equation
(outvar :: {}) = List.filterOnTrue(els, isOutputVar);
invars = List.filterOnTrue(els, isInputVar);
explists = List.map(invars, instExtMakeCrefs);
exps = List.flatten(explists);
{Absyn.CREF(retcref)} = instExtMakeCrefs(outvar);
extdecl = SCode.EXTERNALDECL(SOME(id),lang,SOME(retcref),exps,NONE());
then
extdecl;
case (id,els,SCode.EXTERNALDECL(lang = lang))
equation
inoutvars = List.filterOnTrue(els, isInoutVar);
explists = List.map(inoutvars, instExtMakeCrefs);
exps = List.flatten(explists);
extdecl = SCode.EXTERNALDECL(SOME(id),lang,NONE(),exps,NONE());
then
extdecl;
else
equation
true = Flags.isSet(Flags.FAILTRACE);
Debug.traceln("#-- Inst.instExtMakeExternaldecl failed");
then
fail();
end matchcontinue;
end instExtMakeExternaldecl;

protected function isInoutVar
"Succeds for Elements that are input or output components"
input SCode.Element inElement;
Expand Down Expand Up @@ -5098,64 +5046,6 @@ algorithm
end match;
end isInputVar;

protected function instExtMakeCrefs
"author: LS
This function is used in external function declarations.
It collects the component identifier and the dimension
sizes and returns as a Absyn.Exp list"
input SCode.Element inElement;
output list<Absyn.Exp> outAbsynExpLst;
algorithm
outAbsynExpLst := match (inElement)
local
list<Absyn.Exp> sizelist,crlist;
String id;
SCode.Final fi;
SCode.Replaceable re;
SCode.Visibility pr;
list<Absyn.Subscript> dims;
Absyn.TypeSpec path;
SCode.Mod mod;

case SCode.COMPONENT(name = id, attributes = SCode.ATTR(arrayDims = dims))
equation
sizelist = instExtMakeCrefs2(id, dims, 1);
crlist = (Absyn.CREF(Absyn.CREF_IDENT(id,{})) :: sizelist);
then
crlist;
end match;
end instExtMakeCrefs;

protected function instExtMakeCrefs2
"Helper function to instExtMakeCrefs, collects array dimension sizes."
input SCode.Ident inIdent;
input Absyn.ArrayDim inArrayDim;
input Integer inInteger;
output list<Absyn.Exp> outAbsynExpLst;
algorithm
outAbsynExpLst := match (inIdent,inArrayDim,inInteger)
local
String id;
Integer nextdimno,dimno;
list<Absyn.Exp> restlist,exps;
Absyn.Subscript dim;
list<Absyn.Subscript> restdim;

case (_,{},_) then {};

case (id,(_ :: restdim),dimno)
equation
nextdimno = dimno + 1;
restlist = instExtMakeCrefs2(id, restdim, nextdimno);
exps = (Absyn.CALL(Absyn.CREF_IDENT("size",{}),
Absyn.FUNCTIONARGS({Absyn.CREF(Absyn.CREF_IDENT(id,{})),
Absyn.INTEGER(dimno)},{})) :: restlist);
then
exps;

end match;
end instExtMakeCrefs2;

public function instExtGetFname
"Returns the function name of the externally defined function."
input SCode.ExternalDecl inExternalDecl;
Expand Down
2 changes: 2 additions & 0 deletions Compiler/Util/Error.mo
Expand Up @@ -704,6 +704,8 @@ public constant Message INVALID_TIME_SCOPE = MESSAGE(288, TRANSLATION(), ERROR()
Util.gettext("Built-in variable 'time' may only be used in a model or block."));
public constant Message NO_JACONIAN_TORNLINEAR_SYSTEM = MESSAGE(289, SYMBOLIC(), ERROR(),
Util.gettext("A torn linear system has no symbolic jacobian and currently there are no means to solve that numerically. Please compile with the module \"calculateStrongComponentJacobians\" to provide symbolic jacobians for torn linear systems."));
public constant Message EXT_FN_SINGLE_RETURN_ARRAY = MESSAGE(290, TRANSLATION(), WARNING(),
Util.gettext("An external declaration with a single output without explicit mapping is defined as having the output as the lhs, but language %s does not support this for array variables. OpenModelica will put the output as an input (as is done when there is more than 1 output), but this is not according to the Modelica Specification. Use an explicit mapping instead of the implicit one to suppress this warning."));

public constant Message UNBOUND_PARAMETER_WITH_START_VALUE_WARNING = MESSAGE(499, TRANSLATION(), WARNING(),
Util.gettext("Parameter %s has no value, and is fixed during initialization (fixed=true), using available start value (start=%s) as default value."));
Expand Down

0 comments on commit 75fffde

Please sign in to comment.