diff --git a/Compiler/NFFrontEnd/NFBuiltin.mo b/Compiler/NFFrontEnd/NFBuiltin.mo index f370b253e70..7b812075ba4 100644 --- a/Compiler/NFFrontEnd/NFBuiltin.mo +++ b/Compiler/NFFrontEnd/NFBuiltin.mo @@ -110,12 +110,16 @@ encapsulated package Elements }), SCode.noComment, Absyn.dummyInfo); + constant SCode.Element EXTERNALOBJECT = SCode.CLASS("ExternalObject", + SCode.defaultPrefixes, SCode.NOT_ENCAPSULATED(), SCode.PARTIAL(), SCode.R_CLASS(), + SCode.PARTS({}, {}, {}, {}, {}, {}, {}, NONE()), SCode.noComment, Absyn.dummyInfo); + end Elements; // InstNodes for the builtin types. These have empty class trees to prevent // access to the attributes via dot notation (which is not needed for // modifiers and illegal in other cases). -constant InstNode ANYTYPE_NODE = InstNode.CLASS_NODE("polymorphic", +constant InstNode POLYMORPHIC_NODE = InstNode.CLASS_NODE("polymorphic", Elements.ANY, Visibility.PUBLIC, Pointer.createImmutable(Class.PARTIAL_BUILTIN(Type.POLYMORPHIC(""), NFClassTree.EMPTY, Modifier.NOMOD(), Restriction.TYPE())), @@ -307,6 +311,13 @@ constant InstNode STATESELECT_ALWAYS = constant ComponentRef STATESELECT_ALWAYS_CREF = ComponentRef.CREF(STATESELECT_ALWAYS, {}, STATESELECT_TYPE, Origin.CREF, STATESELECT_CREF); +constant InstNode EXTERNALOBJECT_NODE = InstNode.CLASS_NODE("ExternalObject", + Elements.EXTERNALOBJECT, Visibility.PUBLIC, + Pointer.createImmutable(Class.PARTIAL_BUILTIN(Type.UNKNOWN(), NFClassTree.EMPTY, + Modifier.NOMOD(), Restriction.CLASS())), + arrayCreate(NFInstNode.NUMBER_OF_CACHES, NFInstNode.CachedData.NO_CACHE()), + InstNode.EMPTY_NODE(), InstNodeType.BUILTIN_CLASS()); + constant Type ASSERTIONLEVEL_TYPE = Type.ENUMERATION( Absyn.IDENT("AssertionLevel"), {"error", "warning"}); diff --git a/Compiler/NFFrontEnd/NFClass.mo b/Compiler/NFFrontEnd/NFClass.mo index 759b9a43a09..8a795cbc071 100644 --- a/Compiler/NFFrontEnd/NFClass.mo +++ b/Compiler/NFFrontEnd/NFClass.mo @@ -393,6 +393,16 @@ uniontype Class output Boolean isConnector = Restriction.isConnector(restriction(cls)); end isConnectorClass; + function isExternalObject + input Class cls; + output Boolean isExternalObject = Restriction.isExternalObject(restriction(cls)); + end isExternalObject; + + function isFunction + input Class cls; + output Boolean isFunction = Restriction.isFunction(restriction(cls)); + end isFunction; + end Class; annotation(__OpenModelica_Interface="frontend"); diff --git a/Compiler/NFFrontEnd/NFClassTree.mo b/Compiler/NFFrontEnd/NFClassTree.mo index e2b39cfbf30..4c42bf0ccdc 100644 --- a/Compiler/NFFrontEnd/NFClassTree.mo +++ b/Compiler/NFFrontEnd/NFClassTree.mo @@ -692,6 +692,23 @@ public element := resolveEntryPtr(entry, tree); end lookupElementPtr; + function foldClasses + input ClassTree tree; + input FuncT func; + input output ArgT arg; + + partial function FuncT + input InstNode clsNode; + input output ArgT arg; + end FuncT; + protected + array clss = getClasses(tree); + algorithm + for cls in clss loop + arg := func(cls, arg); + end for; + end foldClasses; + function applyExtends input ClassTree tree; input FuncT func; @@ -797,6 +814,18 @@ public end match; end applyLocalComponents; + function classCount + input ClassTree tree; + output Integer count; + algorithm + count := match tree + case PARTIAL_TREE() then arrayLength(tree.classes); + case EXPANDED_TREE() then arrayLength(tree.classes); + case INSTANTIATED_TREE() then arrayLength(tree.classes); + case FLAT_TREE() then arrayLength(tree.classes); + end match; + end classCount; + function componentCount input ClassTree tree; output Integer count; @@ -804,7 +833,7 @@ public count := match tree case PARTIAL_TREE() then arrayLength(tree.components) - arrayLength(tree.exts); case EXPANDED_TREE() then arrayLength(tree.components) - arrayLength(tree.exts); - case INSTANTIATED_TREE() then arrayLength(tree.components) - arrayLength(tree.exts); + case INSTANTIATED_TREE() then arrayLength(tree.components); case FLAT_TREE() then arrayLength(tree.components); end match; end componentCount; @@ -953,6 +982,17 @@ public end match; end enumerateComponents2; + function getClasses + input ClassTree tree; + output array clss; + algorithm + clss := match tree + case PARTIAL_TREE() then tree.classes; + case EXPANDED_TREE() then tree.classes; + case FLAT_TREE() then tree.classes; + end match; + end getClasses; + function getExtends input ClassTree tree; output array exts; @@ -964,6 +1004,17 @@ public end match; end getExtends; + function getComponents + input ClassTree tree; + output array comps; + algorithm + comps := match tree + case PARTIAL_TREE() then tree.components; + case EXPANDED_TREE() then tree.components; + case FLAT_TREE() then tree.components; + end match; + end getComponents; + protected function instExtendsComps diff --git a/Compiler/NFFrontEnd/NFComplexType.mo b/Compiler/NFFrontEnd/NFComplexType.mo index 3ac40c42e69..1379e2b63ef 100644 --- a/Compiler/NFFrontEnd/NFComplexType.mo +++ b/Compiler/NFFrontEnd/NFComplexType.mo @@ -45,5 +45,10 @@ public Boolean isExpandable; end CONNECTOR; + record EXTERNAL_OBJECT + InstNode constructor; + InstNode destructor; + end EXTERNAL_OBJECT; + annotation(__OpenModelica_Interface="frontend"); end NFComplexType; diff --git a/Compiler/NFFrontEnd/NFConvertDAE.mo b/Compiler/NFFrontEnd/NFConvertDAE.mo index 3daf508ba90..f0202cfd51d 100644 --- a/Compiler/NFFrontEnd/NFConvertDAE.mo +++ b/Compiler/NFFrontEnd/NFConvertDAE.mo @@ -908,7 +908,6 @@ function convertFunction output DAE.Function dfunc; protected Class cls; - array comps; list elems; DAE.FunctionDefinition def; Sections sections; @@ -916,31 +915,27 @@ algorithm cls := InstNode.getClass(Function.instance(func)); dfunc := match cls - case Class.INSTANCED_CLASS(elements = ClassTree.FLAT_TREE(components = comps), sections = sections) + case Class.INSTANCED_CLASS(sections = sections) algorithm - elems := {}; - - for c in func.inputs loop - elems := convertFunctionParam(c) :: elems; - end for; - - for c in func.outputs loop - elems := convertFunctionParam(c) :: elems; - end for; - - for c in func.locals loop - elems := convertFunctionParam(c) :: elems; - end for; - - // elems := list(convertFunctionParam(c) for c in func.locals) :: elems; - elems := match sections - case Sections.SECTIONS() then convertAlgorithms(sections.algorithms, elems); - else elems; + elems := convertFunctionParams(func.inputs, {}); + elems := convertFunctionParams(func.outputs, elems); + elems := convertFunctionParams(func.locals, elems); + + def := match sections + // A function with an algorithm section. + case Sections.SECTIONS() + algorithm + elems := convertAlgorithms(sections.algorithms, elems); + then + DAE.FunctionDefinition.FUNCTION_DEF(listReverse(elems)); + + // An external function. + case Sections.EXTERNAL() + then convertExternalDecl(sections, listReverse(elems)); + + // A function without either algorithm or external section. + else DAE.FunctionDefinition.FUNCTION_DEF(listReverse(elems)); end match; - - elems := listReverse(elems); - - def := DAE.FunctionDefinition.FUNCTION_DEF(elems); then Function.toDAE(func, {def}); @@ -953,6 +948,15 @@ algorithm end match; end convertFunction; +function convertFunctionParams + input list params; + input output list elements; +algorithm + for p in params loop + elements := convertFunctionParam(p) :: elements; + end for; +end convertFunctionParams; + function convertFunctionParam input InstNode node; output DAE.Element element; @@ -988,6 +992,67 @@ algorithm end match; end convertFunctionParam; +function convertExternalDecl + input Sections extDecl; + input list parameters; + output DAE.FunctionDefinition funcDef; +protected + DAE.ExternalDecl decl; + list args; + DAE.ExtArg ret_arg; +algorithm + funcDef := match extDecl + case Sections.EXTERNAL() + algorithm + args := list(convertExternalDeclArg(e) for e in extDecl.args); + ret_arg := convertExternalDeclOutput(extDecl.outputRef); + decl := DAE.ExternalDecl.EXTERNALDECL(extDecl.name, args, ret_arg, extDecl.language, extDecl.ann); + then + DAE.FunctionDefinition.FUNCTION_EXT(parameters, decl); + end match; +end convertExternalDecl; + +function convertExternalDeclArg + input Expression exp; + output DAE.ExtArg arg; +algorithm + arg := match exp + local + Absyn.Direction dir; + ComponentRef cref; + Expression e; + + case Expression.CREF(cref = cref as ComponentRef.CREF()) + algorithm + dir := Prefixes.directionToAbsyn(Component.direction(InstNode.component(cref.node))); + then + DAE.ExtArg.EXTARG(ComponentRef.toDAE(cref), dir, Type.toDAE(exp.ty)); + + case Expression.SIZE(exp = Expression.CREF(cref = cref as ComponentRef.CREF()), dimIndex = SOME(e)) + then DAE.ExtArg.EXTARGSIZE(ComponentRef.toDAE(cref), Type.toDAE(cref.ty), Expression.toDAE(e)); + + else DAE.ExtArg.EXTARGEXP(Expression.toDAE(exp), Type.toDAE(Expression.typeOf(exp))); + + end match; +end convertExternalDeclArg; + +function convertExternalDeclOutput + input ComponentRef cref; + output DAE.ExtArg arg; +algorithm + arg := match cref + local + Absyn.Direction dir; + + case ComponentRef.CREF() + algorithm + dir := Prefixes.directionToAbsyn(Component.direction(InstNode.component(cref.node))); + then + DAE.ExtArg.EXTARG(ComponentRef.toDAE(cref), dir, Type.toDAE(cref.ty)); + + else DAE.ExtArg.NOEXTARG(); + end match; +end convertExternalDeclOutput; annotation(__OpenModelica_Interface="frontend"); end NFConvertDAE; diff --git a/Compiler/NFFrontEnd/NFExpression.mo b/Compiler/NFFrontEnd/NFExpression.mo index 82f53da9360..e724e32845f 100644 --- a/Compiler/NFFrontEnd/NFExpression.mo +++ b/Compiler/NFFrontEnd/NFExpression.mo @@ -1763,5 +1763,19 @@ public end match; end isZero; + function isScalarConst + input Expression exp; + output Boolean isScalar; + algorithm + isScalar := match exp + case INTEGER() then true; + case REAL() then true; + case STRING() then true; + case BOOLEAN() then true; + case ENUM_LITERAL() then true; + else false; + end match; + end isScalarConst; + annotation(__OpenModelica_Interface="frontend"); end NFExpression; diff --git a/Compiler/NFFrontEnd/NFFlatten.mo b/Compiler/NFFrontEnd/NFFlatten.mo index 29a7ea83bf6..74c8a7db772 100644 --- a/Compiler/NFFrontEnd/NFFlatten.mo +++ b/Compiler/NFFrontEnd/NFFlatten.mo @@ -72,6 +72,8 @@ import ConnectEquations = NFConnectEquations; import Connections = NFConnections; import Face = NFConnector.Face; import System; +import ComplexType = NFComplexType; +import NFInstNode.CachedData; public type FunctionTree = FunctionTreeImpl.Tree; @@ -716,14 +718,44 @@ function collectComponentFuncs input output FunctionTree funcs; protected Binding binding; + ComponentRef cref; + InstNode node; + Type ty; algorithm - (_, binding) := component; + (cref, binding) := component; + ComponentRef.CREF(node = node, ty = ty) := cref; + + // TODO: Collect functions from the component's type attributes. + + () := match ty + case Type.COMPLEX(complexTy = ComplexType.EXTERNAL_OBJECT()) + algorithm + funcs := collectExternalObjectStructors(ty.complexTy, funcs); + then + (); + + else (); + end match; if Binding.isBound(binding) then funcs := collectExpFuncs(Binding.getTypedExp(binding), funcs); end if; end collectComponentFuncs; +function collectExternalObjectStructors + input ComplexType ty; + input output FunctionTree funcs; +protected + InstNode constructor, destructor; + Function fn; +algorithm + ComplexType.EXTERNAL_OBJECT(constructor, destructor) := ty; + CachedData.FUNCTION(funcs = {fn}) := InstNode.getFuncCache(constructor); + funcs := flattenFunction(fn, funcs); + CachedData.FUNCTION(funcs = {fn}) := InstNode.getFuncCache(destructor); + funcs := flattenFunction(fn, funcs); +end collectExternalObjectStructors; + function collectEquationFuncs input Equation eq; input output FunctionTree funcs; diff --git a/Compiler/NFFrontEnd/NFFunction.mo b/Compiler/NFFrontEnd/NFFunction.mo index ac70550ef01..91b38357c21 100644 --- a/Compiler/NFFrontEnd/NFFunction.mo +++ b/Compiler/NFFrontEnd/NFFunction.mo @@ -60,8 +60,8 @@ import Prefixes = NFPrefixes; import NFLookupState.LookupState; import Record = NFRecord; import NFTyping.ClassScope; - import MatchKind = NFTypeCheck.MatchKind; +import Restriction = NFRestriction; public type NamedArg = tuple; @@ -210,7 +210,6 @@ uniontype Function end lookupFunction; - public function instFunc input Absyn.ComponentRef functionName; input InstNode scope; @@ -233,6 +232,24 @@ uniontype Function end match; end instFunc; + function instFuncNode + "Instantiates the given InstNode as a function." + input InstNode node; + protected + CachedData cache; + algorithm + cache := InstNode.getFuncCache(node); + + () := match cache + case CachedData.FUNCTION() then (); + else + algorithm + instFunc2(InstNode.scopePath(node), node, InstNode.info(node)); + then + (); + end match; + end instFuncNode; + function instFunc2 input Absyn.Path fnPath; input output InstNode fnNode; @@ -1120,23 +1137,20 @@ protected case Type.POLYMORPHIC() then true; case Type.ARRAY() then isValidParamType(ty.elementType); case Type.COMPLEX() then isValidParamState(ty.cls); + else false; end match; end isValidParamType; function isValidParamState input InstNode cls; output Boolean isValid; - protected - SCode.Restriction res; algorithm - // TODO: Use derived restriction instead, since classes can inherit restrictions. - res := SCode.getClassRestriction(InstNode.definition(cls)); - - isValid := match res - case SCode.Restriction.R_RECORD() then true; - case SCode.Restriction.R_TYPE() then true; - case SCode.Restriction.R_OPERATOR() then true; - case SCode.Restriction.R_FUNCTION() then true; + isValid := match Class.restriction(InstNode.getClass(cls)) + case Restriction.RECORD() then true; + case Restriction.TYPE() then true; + case Restriction.OPERATOR() then true; + case Restriction.FUNCTION() then true; + case Restriction.EXTERNAL_OBJECT() then true; else false; end match; end isValidParamState; diff --git a/Compiler/NFFrontEnd/NFInst.mo b/Compiler/NFFrontEnd/NFInst.mo index c3b710fabf4..fecac615b22 100644 --- a/Compiler/NFFrontEnd/NFInst.mo +++ b/Compiler/NFFrontEnd/NFInst.mo @@ -90,6 +90,7 @@ import Scalarize = NFScalarize; import Restriction = NFRestriction; import ComplexType = NFComplexType; import Package = NFPackage; +import NFFunction.Function; type EquationScope = enumeration(NORMAL, INITIAL, WHEN); @@ -314,7 +315,7 @@ algorithm prefs := instClassPrefixes(def); if isSome(builtin_ext) then - node := expandBuiltinExtends(builtin_ext, tree, node); + node := expandBuiltinExtends(builtin_ext, tree, prefs, node); else tree := ClassTree.expand(tree); res := Restriction.fromSCode(SCode.getClassRestriction(def)); @@ -362,7 +363,7 @@ algorithm if isSome(builtin_ext) or Class.isBuiltin(InstNode.getClass(ext_node)) then // A class extending from a base class may not have other base // classes, and since this is a class extends it has at least one. - node := Util.getOptionOrDefault(builtin_ext, ext_node); + node := Util.getOptionOrDefault(builtin_ext, ext_node); Error.addSourceMessage(Error.BUILTIN_EXTENDS_INVALID_ELEMENTS, {InstNode.name(node)}, InstNode.info(node)); fail(); @@ -545,31 +546,140 @@ function expandBuiltinExtends like Real or some type derived from Real." input Option builtinExtends; input ClassTree scope; + input Class.Prefixes prefixes; input output InstNode node; protected InstNode builtin_ext; Class c; ClassTree tree; + ComplexType eo_ty; algorithm // Fetch the class of the builtin type. SOME(builtin_ext) := builtinExtends; - c := InstNode.getClass(builtin_ext); - tree := Class.classTree(InstNode.getClass(node)); + node := match InstNode.name(builtin_ext) + case "ExternalObject" + algorithm + (tree, eo_ty) := makeExternalObjectType(scope, node); + tree := ClassTree.expand(tree); + c := Class.PARTIAL_BUILTIN(Type.COMPLEX(node, eo_ty), tree, Modifier.NOMOD(), + Restriction.EXTERNAL_OBJECT()); + node := InstNode.updateClass(c, node); + then + node; - // A class extending from a builtin type may not have other components or baseclasses. - if ClassTree.componentCount(tree) > 0 or ClassTree.extendsCount(tree) > 1 then - // ***TODO***: Find the invalid element and use its info to make the error - // message more accurate. - Error.addSourceMessage(Error.BUILTIN_EXTENDS_INVALID_ELEMENTS, - {InstNode.name(builtin_ext)}, InstNode.info(node)); - fail(); - end if; + else + algorithm + c := InstNode.getClass(builtin_ext); + + // A class extending from a builtin type may not have other components or baseclasses. + if ClassTree.componentCount(scope) > 0 or ClassTree.extendsCount(scope) > 1 then + // ***TODO***: Find the invalid element and use its info to make the error + // message more accurate. + Error.addSourceMessage(Error.BUILTIN_EXTENDS_INVALID_ELEMENTS, + {InstNode.name(builtin_ext)}, InstNode.info(node)); + fail(); + end if; - // Replace the class we're expanding with the builtin type. - node := InstNode.updateClass(c, node); + // Replace the class we're expanding with the builtin type. + node := InstNode.updateClass(c, node); + then + node; + + end match; end expandBuiltinExtends; +function makeExternalObjectType + "Constructs a ComplexType for an external object, and also checks that the + external object declaration is valid." + input output ClassTree tree; + input InstNode node; + output ComplexType ty; +protected + Absyn.Path base_path; + InstNode constructor = InstNode.EMPTY_NODE(), destructor = InstNode.EMPTY_NODE(); +algorithm + (tree, ty) := match tree + case ClassTree.PARTIAL_TREE() + algorithm + // An external object may not contain components. + for comp in tree.components loop + if InstNode.isComponent(comp) then + Error.addSourceMessage(Error.EXTERNAL_OBJECT_INVALID_ELEMENT, + {InstNode.name(node), InstNode.name(comp)}, InstNode.info(comp)); + fail(); + end if; + end for; + + // An external object may not contain extends other than the ExternalObject one. + if arrayLength(tree.exts) > 1 then + for ext in tree.exts loop + if InstNode.name(ext) <> "ExternalObject" then + InstNode.CLASS_NODE(nodeType = InstNodeType.BASE_CLASS(definition = + SCode.EXTENDS(baseClassPath = base_path))) := ext; + Error.addSourceMessage(Error.EXTERNAL_OBJECT_INVALID_ELEMENT, + {InstNode.name(node), "extends " + Absyn.pathString(base_path)}, InstNode.info(ext)); + fail(); + end if; + end for; + end if; + + // An external object must have exactly two functions called constructor and + // destructor. + for cls in tree.classes loop + () := match InstNode.name(cls) + case "constructor" guard SCode.isFunction(InstNode.definition(cls)) + algorithm + constructor := cls; + then + (); + + case "destructor" guard SCode.isFunction(InstNode.definition(cls)) + algorithm + destructor := cls; + then + (); + + else + algorithm + // Found some other element => error. + Error.addSourceMessage(Error.EXTERNAL_OBJECT_INVALID_ELEMENT, + {InstNode.name(node), InstNode.name(cls)}, InstNode.info(cls)); + then + fail(); + + end match; + end for; + + if InstNode.isEmpty(constructor) then + // The constructor is missing. + Error.addSourceMessage(Error.EXTERNAL_OBJECT_MISSING_STRUCTOR, + {InstNode.name(node), "constructor"}, InstNode.info(node)); + fail(); + end if; + + if InstNode.isEmpty(destructor) then + // The destructor is missing. + Error.addSourceMessage(Error.EXTERNAL_OBJECT_MISSING_STRUCTOR, + {InstNode.name(node), "destructor"}, InstNode.info(node)); + fail(); + end if; + + // We don't need the ExternalObject extends anymore, get rid of it so we + // don't have to handle it later. + tree.exts := arrayCreate(0, InstNode.EMPTY_NODE()); + // The component array will only contain a reference node to the + // ExternalObject extends, so get rid of it too. + tree.components := arrayCreate(0, InstNode.EMPTY_NODE()); + + // Construct the ComplexType for the external object. + ty := ComplexType.EXTERNAL_OBJECT(constructor, destructor); + then + (tree, ty); + + end match; +end makeExternalObjectType; + function instClass input output InstNode node; input Modifier modifier; @@ -646,6 +756,15 @@ algorithm then (); + case (Class.PARTIAL_BUILTIN(restriction = Restriction.EXTERNAL_OBJECT()), _) + algorithm + inst_cls := Class.INSTANCED_BUILTIN(cls.ty, cls.elements, {}, cls.restriction); + node := InstNode.replaceClass(inst_cls, node); + updateComponentClass(parent, node); + instExternalObjectStructors(cls.ty, parent); + then + (); + case (Class.PARTIAL_BUILTIN(), _) algorithm mod := Modifier.fromElement(InstNode.definition(node), InstNode.level(parent), InstNode.parent(node)); @@ -667,6 +786,7 @@ algorithm node := InstNode.replaceClass(Class.NOT_INSTANTIATED(), node); node := expand(node); node := instClass(node, modifier, attributes, parent); + updateComponentClass(parent, node); then (); @@ -689,6 +809,26 @@ algorithm end if; end updateComponentClass; +function instExternalObjectStructors + "Instantiates the constructor and destructor for an ExternalObject class." + input Type ty; + input InstNode parent; +protected + InstNode constructor, destructor, par; +algorithm + // The constructor and destructor have function parameters that are instances + // of the external object class, and we instantiate the structors when we + // instantiate such instances. To break that loop we check that we're not + // inside the external object class before instantiating the structors. + par := InstNode.parent(InstNode.parent(parent)); + + if not (InstNode.isClass(par) and Class.isExternalObject(InstNode.getClass(par))) then + Type.COMPLEX(complexTy = ComplexType.EXTERNAL_OBJECT(constructor, destructor)) := ty; + Function.instFuncNode(constructor); + Function.instFuncNode(destructor); + end if; +end instExternalObjectStructors; + function instPackage "This function instantiates a package given a package node. If the package has already been instantiated, then the cached instance from the node is @@ -1609,12 +1749,23 @@ function instSections2 input InstNode scope; input output Sections sections; algorithm - sections := match parts + sections := match (parts, sections) local list eq, ieq; list> alg, ialg; + SCode.ExternalDecl ext_decl; - case SCode.PARTS() + case (_, Sections.EXTERNAL()) + algorithm + Error.addSourceMessage(Error.MULTIPLE_SECTIONS_IN_FUNCTION, + {InstNode.name(scope)}, InstNode.info(scope)); + then + fail(); + + case (SCode.PARTS(externalDecl = SOME(ext_decl)), _) + then instExternalDecl(ext_decl, scope); + + case (SCode.PARTS(), _) algorithm eq := instEquations(parts.normalEquationLst, scope, EquationScope.NORMAL); ieq := instEquations(parts.initialEquationLst, scope, EquationScope.INITIAL); @@ -1626,6 +1777,58 @@ algorithm end match; end instSections2; +function instExternalDecl + input SCode.ExternalDecl extDecl; + input InstNode scope; + output Sections sections; +algorithm + sections := match extDecl + local + String name; + String lang; + list args; + ComponentRef ret_cref; + SourceInfo info; + + case SCode.EXTERNALDECL() + algorithm + info := InstNode.info(scope); + name := Util.getOptionOrDefault(extDecl.funcName, InstNode.name(scope)); + lang := Util.getOptionOrDefault(extDecl.lang, "C"); + checkExternalDeclLanguage(lang, info); + args := list(instExp(arg, scope, info) for arg in extDecl.args); + + if isSome(extDecl.output_) then + ret_cref := Lookup.lookupLocalComponent(Util.getOption(extDecl.output_), scope, info); + else + ret_cref := ComponentRef.EMPTY(); + end if; + then + Sections.EXTERNAL(name, args, ret_cref, lang, extDecl.annotation_, isSome(extDecl.funcName)); + + end match; +end instExternalDecl; + +function checkExternalDeclLanguage + "Checks that the language declared for an external function is valid." + input String language; + input SourceInfo info; +algorithm + () := match language + // The specification also allows for C89, C99, and C11, but our code + // generation only seems to support C. + case "C" then (); + case "FORTRAN 77" then (); + case "builtin" then (); + else + algorithm + Error.addSourceMessage(Error.INVALID_EXTERNAL_LANGUAGE, + {language}, info); + then + fail(); + end match; +end checkExternalDeclLanguage; + function instEquations input list scodeEql; input InstNode scope; diff --git a/Compiler/NFFrontEnd/NFInstNode.mo b/Compiler/NFFrontEnd/NFInstNode.mo index c2544e80329..cd3a9f0bedd 100644 --- a/Compiler/NFFrontEnd/NFInstNode.mo +++ b/Compiler/NFFrontEnd/NFInstNode.mo @@ -418,6 +418,7 @@ uniontype InstNode case CLASS_NODE() then node.parentScope; case COMPONENT_NODE() then node.parent; case IMPLICIT_SCOPE() then node.parentScope; + else EMPTY_NODE(); end match; end parent; diff --git a/Compiler/NFFrontEnd/NFLookup.mo b/Compiler/NFFrontEnd/NFLookup.mo index f4fa95cc996..2626acc497a 100644 --- a/Compiler/NFFrontEnd/NFLookup.mo +++ b/Compiler/NFFrontEnd/NFLookup.mo @@ -120,6 +120,23 @@ algorithm end if; end fixTypenameState; +function lookupLocalComponent + "Looks up a component in the local scope, without searching in any enclosing + scopes. The found scope is returned since it can be different from the given + scope in the case where the cref refers to an outer component." + input Absyn.ComponentRef cref; + input InstNode scope "The scope to look in."; + input SourceInfo info; + output ComponentRef foundCref; + output InstNode foundScope "The scope the cref was found in."; +protected + LookupState state; + InstNode node; +algorithm + (foundCref, foundScope, state) := lookupLocalCref(cref, scope, info); + LookupState.assertComponent(state, ComponentRef.node(foundCref), cref, info); +end lookupLocalComponent; + function lookupCallableName input Absyn.ComponentRef cref; input InstNode scope "The scope to look in."; @@ -165,11 +182,9 @@ algorithm end lookupImport; function lookupCref - "This function will look up a component reference in the given scope, and - return a list of nodes that correspond to the parts of the cref in reverse - order. I.e. when looking up the cref a.b.c, the list of nodes {c, b, a} will - be returned. The scope where the first part of the cref was found will also - be returned." + "This function will look up an Absyn.ComponentRef in the given scope, and + construct a ComponentRef from the found nodes. The scope where the first part + of the cref was found will also be returned." input Absyn.ComponentRef cref; input InstNode scope "The scope to look in."; input SourceInfo info; @@ -233,6 +248,48 @@ algorithm end if; end lookupCref; +function lookupLocalCref + "Looks up a cref in the local scope without going into any enclosing scopes." + input Absyn.ComponentRef cref; + input InstNode scope "The scope to look in."; + input SourceInfo info; + output ComponentRef foundCref; + output InstNode foundScope "The scope where the first part of the cref was found."; + output LookupState state; +protected + MatchType match_ty; + InstNode node; +algorithm + (foundCref, foundScope, state) := matchcontinue cref + local + InstNode found_scope; + + case Absyn.ComponentRef.CREF_IDENT() + algorithm + (node, foundScope) := lookupLocalSimpleCref(cref.name, scope); + state := LookupState.nodeState(node); + then + (ComponentRef.fromAbsyn(node, cref.subscripts), foundScope, state); + + case Absyn.ComponentRef.CREF_QUAL() + algorithm + (node, foundScope) := lookupLocalSimpleCref(cref.name, scope); + state := LookupState.nodeState(node); + foundCref := ComponentRef.fromAbsyn(node, cref.subscripts); + (foundCref, foundScope, state) := + lookupCrefInNode(cref.componentRef, node, foundCref, foundScope, state); + then + (foundCref, foundScope, state); + + else + algorithm + Error.addSourceMessage(Error.LOOKUP_VARIABLE_ERROR, + {Dump.printComponentRefStr(cref), InstNode.name(scope)}, info); + then + fail(); + end matchcontinue; +end lookupLocalCref; + function lookupInner "Looks up the corresponding inner node given an outer node." input InstNode outerNode; @@ -565,7 +622,8 @@ algorithm case "Integer" then NFBuiltin.INTEGER_NODE; case "Boolean" then NFBuiltin.BOOLEAN_NODE; case "String" then NFBuiltin.STRING_NODE; - case "polymorphic" then NFBuiltin.ANYTYPE_NODE; + case "polymorphic" then NFBuiltin.POLYMORPHIC_NODE; + case "ExternalObject" then NFBuiltin.EXTERNALOBJECT_NODE; end match; end lookupSimpleBuiltinName; @@ -575,8 +633,6 @@ function lookupSimpleCref input InstNode scope; output InstNode node; output InstNode foundScope = scope; -protected - Class cls; algorithm // Look for the name in the given scope, and if not found there continue // through the enclosing scopes of that scope until we either run out of @@ -604,9 +660,6 @@ algorithm return; else // Look in the next enclosing scope. - // TODO: The enclosing scope here will be a package, so the lookup should - // be restricted to constants only. - //foundScope := Inst.instPackage(InstNode.parentScope(foundScope)); foundScope := InstNode.parentScope(foundScope); end try; end for; @@ -616,6 +669,32 @@ algorithm fail(); end lookupSimpleCref; +function lookupLocalSimpleCref + "This function look up a simple name as a cref in a given component, without + searching in any enclosing scope." + input String name; + input InstNode scope; + output InstNode node; + output InstNode foundScope = scope; +algorithm + node := match foundScope + case InstNode.IMPLICIT_SCOPE() + then lookupIterator(name, foundScope.locals); + case InstNode.CLASS_NODE() + then Class.lookupElement(name, InstNode.getClass(foundScope)); + case InstNode.COMPONENT_NODE() + then Class.lookupElement(name, InstNode.getClass(foundScope)); + case InstNode.INNER_OUTER_NODE() + then Class.lookupElement(name, InstNode.getClass(foundScope.innerNode)); + end match; + + // If the node is an outer node, return the inner instead. + if InstNode.isInnerOuterNode(node) then + node := InstNode.resolveInner(node); + foundScope := InstNode.parent(node); + end if; +end lookupLocalSimpleCref; + function lookupIterator input String name; input list iterators; diff --git a/Compiler/NFFrontEnd/NFPrefixes.mo b/Compiler/NFFrontEnd/NFPrefixes.mo index c7e6c6de7cd..03c369ab94c 100644 --- a/Compiler/NFFrontEnd/NFPrefixes.mo +++ b/Compiler/NFFrontEnd/NFPrefixes.mo @@ -224,7 +224,7 @@ algorithm dir := match scodeDir case Absyn.Direction.INPUT() then Direction.INPUT; case Absyn.Direction.OUTPUT() then Direction.OUTPUT; - case Absyn.Direction.BIDIR() then Direction.NONE; + else Direction.NONE; end match; end directionFromSCode; @@ -235,10 +235,21 @@ algorithm ddir := match dir case Direction.INPUT then DAE.VarDirection.INPUT(); case Direction.OUTPUT then DAE.VarDirection.OUTPUT(); - case Direction.NONE then DAE.VarDirection.BIDIR(); + else DAE.VarDirection.BIDIR(); end match; end directionToDAE; +function directionToAbsyn + input Direction dir; + output Absyn.Direction adir; +algorithm + adir := match dir + case Direction.INPUT then Absyn.INPUT(); + case Direction.OUTPUT then Absyn.OUTPUT(); + else Absyn.BIDIR(); + end match; +end directionToAbsyn; + function directionString input Direction dir; output String str; diff --git a/Compiler/NFFrontEnd/NFRestriction.mo b/Compiler/NFFrontEnd/NFRestriction.mo index 26eb01c31de..d854bcfcc3c 100644 --- a/Compiler/NFFrontEnd/NFRestriction.mo +++ b/Compiler/NFFrontEnd/NFRestriction.mo @@ -36,14 +36,19 @@ protected import Restriction = NFRestriction; public - record MODEL end MODEL; + record CLASS end CLASS; record CONNECTOR Boolean isExpandable; end CONNECTOR; - record TYPE end TYPE; record ENUMERATION end ENUMERATION; + record EXTERNAL_OBJECT end EXTERNAL_OBJECT; + record FUNCTION end FUNCTION; + record MODEL end MODEL; + record OPERATOR end OPERATOR; + record RECORD end RECORD; + record TYPE end TYPE; record UNKNOWN end UNKNOWN; function fromSCode @@ -51,9 +56,14 @@ public output Restriction res; algorithm res := match sres - case SCode.Restriction.R_CONNECTOR() - then CONNECTOR(sres.isExpandable); - + case SCode.Restriction.R_CLASS() then CLASS(); + case SCode.Restriction.R_CONNECTOR() then CONNECTOR(sres.isExpandable); + case SCode.Restriction.R_ENUMERATION() then ENUMERATION(); + case SCode.Restriction.R_FUNCTION() then FUNCTION(); + case SCode.Restriction.R_MODEL() then MODEL(); + case SCode.Restriction.R_OPERATOR() then OPERATOR(); + case SCode.Restriction.R_RECORD() then RECORD(); + case SCode.Restriction.R_TYPE() then TYPE(); else MODEL(); end match; end fromSCode; @@ -68,5 +78,25 @@ public end match; end isConnector; + function isExternalObject + input Restriction res; + output Boolean isExternalObject; + algorithm + isExternalObject := match res + case EXTERNAL_OBJECT() then true; + else false; + end match; + end isExternalObject; + + function isFunction + input Restriction res; + output Boolean isFunction; + algorithm + isFunction := match res + case FUNCTION() then true; + else false; + end match; + end isFunction; + annotation(__OpenModelica_Interface="frontend"); end NFRestriction; diff --git a/Compiler/NFFrontEnd/NFSections.mo b/Compiler/NFFrontEnd/NFSections.mo index 2262cacbc82..c8e12edccc7 100644 --- a/Compiler/NFFrontEnd/NFSections.mo +++ b/Compiler/NFFrontEnd/NFSections.mo @@ -32,6 +32,9 @@ encapsulated uniontype NFSections import Equation = NFEquation; import Statement = NFStatement; + import ComponentRef = NFComponentRef; + import Expression = NFExpression; + import SCode.Annotation; protected import Sections = NFSections; @@ -44,6 +47,15 @@ public list> initialAlgorithms; end SECTIONS; + record EXTERNAL + String name; + list args; + ComponentRef outputRef; + String language; + Option ann; + Boolean explicit; + end EXTERNAL; + record EMPTY end EMPTY; function new diff --git a/Compiler/NFFrontEnd/NFType.mo b/Compiler/NFFrontEnd/NFType.mo index 9ff0f24c5ac..07bedff5f99 100644 --- a/Compiler/NFFrontEnd/NFType.mo +++ b/Compiler/NFFrontEnd/NFType.mo @@ -314,6 +314,7 @@ public case ENUMERATION() then true; case ENUMERATION_ANY() then true; case FUNCTION() then isScalarBuiltin(ty.resultType); + else false; end match; end isScalarBuiltin; @@ -457,7 +458,7 @@ public case Type.UNKNOWN() then DAE.T_UNKNOWN_DEFAULT; case Type.COMPLEX() // TODO: Use proper ClassInf.State here. - then DAE.Type.T_COMPLEX(ClassInf.MODEL(Absyn.IDENT(InstNode.name(ty.cls))), {}, NONE()); + then DAE.Type.T_COMPLEX(ClassInf.MODEL(InstNode.scopePath(ty.cls)), {}, NONE()); case Type.POLYMORPHIC() then DAE.T_METAPOLYMORPHIC(ty.name); case Type.ANY() then DAE.T_ANYTYPE(NONE()); else diff --git a/Compiler/NFFrontEnd/NFTyping.mo b/Compiler/NFFrontEnd/NFTyping.mo index 5a653f24a30..df42abacfe5 100644 --- a/Compiler/NFFrontEnd/NFTyping.mo +++ b/Compiler/NFFrontEnd/NFTyping.mo @@ -81,6 +81,9 @@ import ComplexType = NFComplexType; import Restriction = NFRestriction; import NFModifier.ModTable; import Package = NFPackage; +import NFFunction.Function; +import NFInstNode.CachedData; +import Direction = NFPrefixes.Direction; uniontype TypingError record NO_ERROR end NO_ERROR; @@ -149,6 +152,12 @@ algorithm then (); + case Class.INSTANCED_BUILTIN(restriction = Restriction.EXTERNAL_OBJECT()) + algorithm + typeExternalObjectStructors(c.ty); + then + (); + case Class.INSTANCED_BUILTIN() then (); else @@ -161,6 +170,27 @@ algorithm end match; end typeComponents; +function typeExternalObjectStructors + input Type ty; +protected + InstNode constructor, destructor; + Function fn; + Boolean typed, special; +algorithm + Type.COMPLEX(complexTy = ComplexType.EXTERNAL_OBJECT(constructor, destructor)) := ty; + CachedData.FUNCTION({fn}, typed, special) := InstNode.getFuncCache(constructor); + if not typed then + fn := Function.typeFunction(fn); + InstNode.setFuncCache(constructor, CachedData.FUNCTION({fn}, true, special)); + end if; + + CachedData.FUNCTION({fn}, typed, special) := InstNode.getFuncCache(destructor); + if not typed then + fn := Function.typeFunction(fn); + InstNode.setFuncCache(destructor, CachedData.FUNCTION({fn}, true, special)); + end if; +end typeExternalObjectStructors; + function makeClassType input InstNode clsNode; output Type ty; @@ -1565,6 +1595,7 @@ protected Class cls, typed_cls; array components; Sections sections; + SourceInfo info; algorithm cls := InstNode.getClass(classNode); @@ -1572,10 +1603,26 @@ algorithm case Class.INSTANCED_CLASS(elements = ClassTree.FLAT_TREE(components = components), sections = sections) algorithm - // TODO: Setting scope here shouldn't be necessary, but bootstrapping fails - // without it. - sections := Sections.map(sections, - function typeEquation(scope = EquationScope.NORMAL), typeAlgorithm); + sections := match sections + case Sections.SECTIONS() + // TODO: Setting scope here shouldn't be necessary, but bootstrapping fails + // without it. + then Sections.map(sections, function typeEquation(scope = EquationScope.NORMAL), typeAlgorithm); + + case Sections.EXTERNAL(explicit = true) + algorithm + info := InstNode.info(classNode); + sections.args := list(typeExternalArg(arg, info, classNode) for arg in sections.args); + sections.outputRef := typeCref(sections.outputRef, info); + then + sections; + + case Sections.EXTERNAL() + then makeDefaultExternalCall(sections, classNode); + + else sections; + end match; + typed_cls := Class.setSections(sections, cls); for c in components loop @@ -1597,6 +1644,117 @@ algorithm end match; end typeSections; +function typeExternalArg + input output Expression arg; + input SourceInfo info; + input InstNode node; +protected + Type ty; + Variability var; +algorithm + (arg, ty, var) := typeExp(arg, info); + + // Check that the external function argument is valid. The valid types of + // expressions are crefs, scalar constants and size expressions with constant + // index. Size expressions with constant index are evaluated when they are + // typed, so we need only care about the first two types of expressions here. + arg := match arg + case Expression.CREF() then arg; + else + algorithm + if Type.isScalarBuiltin(ty) and var == Variability.CONSTANT then + arg := Ceval.evalExp(arg, Ceval.EvalTarget.IGNORE_ERRORS()); + arg := SimplifyExp.simplifyExp(arg); + else + Error.addSourceMessage(Error.EXTERNAL_ARG_WRONG_EXP, + {Expression.toString(arg)}, info); + fail(); + end if; + then + arg; + + end match; +end typeExternalArg; + +function makeDefaultExternalCall + "Constructs a default external call for an external function. If only one + output exists a call 'output = func(input1, input2, ...)' is generated, + otherwise a call 'func(param1, param2, ...)' is generated from the function's + formal parameters and local variables." + input output Sections extDecl; + input InstNode fnNode; +algorithm + extDecl := match extDecl + local + list args; + ComponentRef output_ref; + Function fn; + Boolean single_output; + array comps; + Component comp; + Type ty; + InstNode node; + Expression exp; + + case Sections.EXTERNAL() + algorithm + // An explicit function call isn't needed for builtin calls. + if extDecl.language == "builtin" then + return; + end if; + + // Fetch the cached function. + CachedData.FUNCTION(funcs = {fn}) := InstNode.getFuncCache(fnNode); + // Check whether we have a single output or not. + single_output := listLength(fn.outputs) == 1; + + // When there's a single array output we can't generate a call on the + // 'output = func(inputs)' form, so print a warning and treat is as + // though it's not a single output. + if single_output and Type.isArray(Function.returnType(fn)) then + single_output := false; + Error.addSourceMessage(Error.EXT_FN_SINGLE_RETURN_ARRAY, + {extDecl.language}, InstNode.info(fnNode)); + end if; + + // If we have a single output, set the external declaration's output to + // be a reference to the function's output. Otherwise leave it as empty. + if single_output then + {node} := fn.outputs; + ty := InstNode.getType(node); + extDecl.outputRef := ComponentRef.fromNode(node, ty); + end if; + + // Generate function arguments from the function's components. + comps := ClassTree.getComponents(Class.classTree(InstNode.getClass(fn.node))); + if arrayLength(comps) > 0 then + args := {}; + for c in comps loop + comp := InstNode.component(c); + + // Skip outputs if there's only a single output. + if not single_output or Component.direction(comp) <> Direction.OUTPUT then + // Generate a cref for the component and add it to the list of arguments. + ty := Component.getType(comp); + exp := Expression.CREF(ty, ComponentRef.fromNode(c, ty)); + args := exp :: args; + + // If the component is an array, generate a size expression for + // each dimension too. + for i in 1:Type.dimensionCount(ty) loop + args := Expression.SIZE(exp, SOME(Expression.INTEGER(i))) :: args; + end for; + end if; + end for; + + extDecl.args := listReverse(args); + end if; + then + extDecl; + + end match; +end makeDefaultExternalCall; + function typeComponentSections input InstNode component; protected diff --git a/Compiler/Util/Error.mo b/Compiler/Util/Error.mo index 667e6e28e74..006519538c2 100644 --- a/Compiler/Util/Error.mo +++ b/Compiler/Util/Error.mo @@ -751,6 +751,14 @@ public constant Message INVALID_CONNECTOR_FORM = MESSAGE(311, TRANSLATION(), ERR Util.gettext("%s is not a valid form for a connector, connectors must be either c1.c2...cn or m.c (where c is a connector and m is a non-connector).")); public constant Message CONNECTOR_PREFIX_OUTSIDE_CONNECTOR = MESSAGE(312, TRANSLATION(), WARNING(), Util.gettext("Prefix '%s' used outside connector declaration.")); +public constant Message EXTERNAL_OBJECT_INVALID_ELEMENT = MESSAGE(313, TRANSLATION(), ERROR(), + Util.gettext("External object %s contains invalid element '%s'.")); +public constant Message EXTERNAL_OBJECT_MISSING_STRUCTOR = MESSAGE(314, TRANSLATION(), ERROR(), + Util.gettext("External object %s is missing a %s.")); +public constant Message MULTIPLE_SECTIONS_IN_FUNCTION = MESSAGE(315, TRANSLATION(), ERROR(), + Util.gettext("Function %s has more than one algorithm section or external declaration.")); +public constant Message INVALID_EXTERNAL_LANGUAGE = MESSAGE(316, TRANSLATION(), ERROR(), + Util.gettext("'%s' is not a valid language for an external function.")); public constant Message INITIALIZATION_NOT_FULLY_SPECIFIED = MESSAGE(496, TRANSLATION(), WARNING(), Util.gettext("The initial conditions are not fully specified. %s.")); public constant Message INITIALIZATION_OVER_SPECIFIED = MESSAGE(497, TRANSLATION(), WARNING(),