Skip to content
This repository was archived by the owner on May 18, 2019. It is now read-only.

Commit 0c79e1c

Browse files
perostOpenModelica-Hudson
authored andcommitted
[NF] Try to detect instantiation loops.
- Set a limit on the depth of the instance tree, to be able to give an error message instead of overflowing the stack when a model contains mutually dependent classes. Belonging to [master]: - #2978 - OpenModelica/OpenModelica-testsuite#1138
1 parent 9a49909 commit 0c79e1c

File tree

2 files changed

+87
-21
lines changed

2 files changed

+87
-21
lines changed

Compiler/NFFrontEnd/NFInst.mo

Lines changed: 85 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ function instantiate
186186
algorithm
187187
node := partialInstClass(node);
188188
node := expandClass(node);
189-
node := instClass(node, Modifier.NOMOD(), NFComponent.DEFAULT_ATTR, true, parent);
189+
node := instClass(node, Modifier.NOMOD(), NFComponent.DEFAULT_ATTR, true, 0, parent);
190190
end instantiate;
191191

192192
function expand
@@ -705,6 +705,7 @@ function instClass
705705
input Modifier modifier;
706706
input output Component.Attributes attributes = NFComponent.DEFAULT_ATTR;
707707
input Boolean useBinding;
708+
input Integer instLevel;
708709
input InstNode parent = InstNode.EMPTY_NODE();
709710
protected
710711
Class cls;
@@ -721,7 +722,7 @@ algorithm
721722
fail();
722723
end if;
723724

724-
(attributes, node) := instClassDef(cls, modifier, attributes, useBinding, node, parent);
725+
(attributes, node) := instClassDef(cls, modifier, attributes, useBinding, node, parent, instLevel);
725726
end instClass;
726727

727728
function instClassDef
@@ -731,6 +732,7 @@ function instClassDef
731732
input Boolean useBinding;
732733
input output InstNode node;
733734
input InstNode parent;
735+
input Integer instLevel;
734736
protected
735737
InstNode par, base_node;
736738
Class inst_cls;
@@ -768,7 +770,7 @@ algorithm
768770
applyModifier(mod, cls_tree, InstNode.name(node));
769771

770772
// Apply element redeclares.
771-
ClassTree.mapRedeclareChains(cls_tree, redeclareElements);
773+
ClassTree.mapRedeclareChains(cls_tree, function redeclareElements(instLevel = instLevel));
772774
// Redeclare classes with redeclare modifiers. Redeclared components could
773775
// also be handled here, but since each component is only instantiated once
774776
// it's more efficient to apply the redeclare when instantiating them instead.
@@ -777,12 +779,12 @@ algorithm
777779
// Instantiate the extends nodes.
778780
ClassTree.mapExtends(cls_tree,
779781
function instExtends(attributes = attributes, useBinding = useBinding,
780-
visibility = ExtendsVisibility.PUBLIC));
782+
visibility = ExtendsVisibility.PUBLIC, instLevel = instLevel + 1));
781783

782784
// Instantiate local components.
783785
ClassTree.applyLocalComponents(cls_tree,
784786
function instComponent(attributes = attributes, innerMod = Modifier.NOMOD(),
785-
originalAttr = NONE(), useBinding = useBinding));
787+
originalAttr = NONE(), useBinding = useBinding, instLevel = instLevel + 1));
786788

787789
// Remove duplicate elements.
788790
cls_tree := ClassTree.replaceDuplicates(cls_tree);
@@ -804,7 +806,7 @@ algorithm
804806
attributes := mergeDerivedAttributes(attrs, attributes, parent);
805807

806808
// Instantiate the base class and update the nodes.
807-
(base_node, attributes) := instClass(base_node, mod, attributes, useBinding, par);
809+
(base_node, attributes) := instClass(base_node, mod, attributes, useBinding, instLevel, par);
808810
cls.baseClass := base_node;
809811
cls.attributes := attributes;
810812
cls.dims := arrayCopy(cls.dims);
@@ -848,7 +850,7 @@ algorithm
848850
node := InstNode.replaceClass(Class.NOT_INSTANTIATED(), node);
849851
node := InstNode.setNodeType(InstNodeType.NORMAL_CLASS(), node);
850852
node := expand(node);
851-
node := instClass(node, outerMod, attributes, useBinding, parent);
853+
node := instClass(node, outerMod, attributes, useBinding, instLevel, parent);
852854
updateComponentType(parent, node);
853855
then
854856
();
@@ -995,6 +997,7 @@ function instExtends
995997
input Component.Attributes attributes;
996998
input Boolean useBinding;
997999
input ExtendsVisibility visibility;
1000+
input Integer instLevel;
9981001
protected
9991002
Class cls, inst_cls;
10001003
ClassTree cls_tree;
@@ -1023,11 +1026,12 @@ algorithm
10231026
end if;
10241027

10251028
ClassTree.mapExtends(cls_tree,
1026-
function instExtends(attributes = attributes, useBinding = useBinding, visibility = vis));
1029+
function instExtends(attributes = attributes, useBinding = useBinding,
1030+
visibility = vis, instLevel = instLevel));
10271031

10281032
ClassTree.applyLocalComponents(cls_tree,
10291033
function instComponent(attributes = attributes, innerMod = Modifier.NOMOD(),
1030-
originalAttr = NONE(), useBinding = useBinding));
1034+
originalAttr = NONE(), useBinding = useBinding, instLevel = instLevel));
10311035
then
10321036
();
10331037

@@ -1037,7 +1041,7 @@ algorithm
10371041
vis := ExtendsVisibility.DERIVED_PROTECTED;
10381042
end if;
10391043

1040-
cls.baseClass := instExtends(cls.baseClass, attributes, useBinding, vis);
1044+
cls.baseClass := instExtends(cls.baseClass, attributes, useBinding, vis, instLevel);
10411045
node := InstNode.updateClass(cls, node);
10421046
then
10431047
();
@@ -1160,17 +1164,23 @@ end redeclareClasses;
11601164

11611165
function redeclareElements
11621166
input list<Mutable<InstNode>> chain;
1167+
input Integer instLevel;
11631168
protected
11641169
InstNode node;
11651170
Mutable<InstNode> node_ptr;
11661171
algorithm
11671172
node := Mutable.access(listHead(chain));
1173+
node_ptr := listHead(chain);
11681174

11691175
if InstNode.isClass(node) then
1170-
node_ptr := redeclareClassElement(cls_ptr for cls_ptr in chain);
1176+
for cls_ptr in listRest(chain) loop
1177+
node_ptr := redeclareClassElement(cls_ptr, node_ptr);
1178+
end for;
11711179
node := Mutable.access(node_ptr);
11721180
else
1173-
node_ptr := redeclareComponentElement(comp_ptr for comp_ptr in chain);
1181+
for comp_ptr in listRest(chain) loop
1182+
node_ptr := redeclareComponentElement(comp_ptr, node_ptr, instLevel);
1183+
end for;
11741184
node := Mutable.access(node_ptr);
11751185
end if;
11761186

@@ -1195,14 +1205,15 @@ end redeclareClassElement;
11951205
function redeclareComponentElement
11961206
input Mutable<InstNode> redeclareComp;
11971207
input Mutable<InstNode> replaceableComp;
1208+
input Integer instLevel;
11981209
output Mutable<InstNode> outComp;
11991210
protected
12001211
InstNode rdcl_node, repl_node;
12011212
algorithm
12021213
rdcl_node := Mutable.access(redeclareComp);
12031214
repl_node := Mutable.access(replaceableComp);
1204-
instComponent(repl_node, NFComponent.DEFAULT_ATTR, Modifier.NOMOD(), true);
1205-
redeclareComponent(rdcl_node, repl_node, Modifier.NOMOD(), Modifier.NOMOD(), NFComponent.DEFAULT_ATTR, rdcl_node);
1215+
instComponent(repl_node, NFComponent.DEFAULT_ATTR, Modifier.NOMOD(), true, instLevel);
1216+
redeclareComponent(rdcl_node, repl_node, Modifier.NOMOD(), Modifier.NOMOD(), NFComponent.DEFAULT_ATTR, rdcl_node, instLevel);
12061217
outComp := Mutable.create(rdcl_node);
12071218
end redeclareComponentElement;
12081219

@@ -1301,6 +1312,7 @@ function instComponent
13011312
input Component.Attributes attributes "Attributes to be propagated to the component.";
13021313
input Modifier innerMod;
13031314
input Boolean useBinding "Ignore the component's binding if false.";
1315+
input Integer instLevel;
13041316
input Option<Component.Attributes> originalAttr = NONE();
13051317
protected
13061318
Component comp;
@@ -1317,6 +1329,8 @@ algorithm
13171329

13181330
// Skip already instantiated components.
13191331
if not Component.isDefinition(comp) then
1332+
// An already instantiated component might be due to an instantiation loop, check it.
1333+
checkRecursiveDefinition(Component.classInstance(comp), comp_node, limitReached = false);
13201334
return;
13211335
end if;
13221336

@@ -1325,7 +1339,7 @@ algorithm
13251339
if Modifier.isRedeclare(outer_mod) then
13261340
checkOuterComponentMod(outer_mod, def, comp_node);
13271341
instComponentDef(def, Modifier.NOMOD(), Modifier.NOMOD(), NFComponent.DEFAULT_ATTR,
1328-
useBinding, comp_node, parent, originalAttr, isRedeclared = true);
1342+
useBinding, comp_node, parent, instLevel, originalAttr, isRedeclared = true);
13291343

13301344
Modifier.REDECLARE(element = rdcl_node, mod = outer_mod) := outer_mod;
13311345

@@ -1337,9 +1351,9 @@ algorithm
13371351

13381352
outer_mod := Modifier.merge(InstNode.getModifier(rdcl_node), outer_mod);
13391353
InstNode.setModifier(outer_mod, rdcl_node);
1340-
redeclareComponent(rdcl_node, node, Modifier.NOMOD(), cc_mod, attributes, node);
1354+
redeclareComponent(rdcl_node, node, Modifier.NOMOD(), cc_mod, attributes, node, instLevel);
13411355
else
1342-
instComponentDef(def, outer_mod, cc_mod, attributes, useBinding, comp_node, parent, originalAttr);
1356+
instComponentDef(def, outer_mod, cc_mod, attributes, useBinding, comp_node, parent, instLevel, originalAttr);
13431357
end if;
13441358
end instComponent;
13451359

@@ -1351,6 +1365,7 @@ function instComponentDef
13511365
input Boolean useBinding;
13521366
input InstNode node;
13531367
input InstNode parent;
1368+
input Integer instLevel;
13541369
input Option<Component.Attributes> originalAttr = NONE();
13551370
input Boolean isRedeclared = false;
13561371
algorithm
@@ -1403,7 +1418,7 @@ algorithm
14031418

14041419
// Instantiate the type of the component.
14051420
(ty_node, ty_attr) := instTypeSpec(component.typeSpec, mod, attr,
1406-
useBinding and not Binding.isBound(binding), parent, node, info);
1421+
useBinding and not Binding.isBound(binding), parent, node, info, instLevel);
14071422
ty := InstNode.getClass(ty_node);
14081423

14091424
// Update the component's variability based on its type (e.g. Integer is discrete).
@@ -1491,6 +1506,7 @@ function redeclareComponent
14911506
input Modifier constrainingMod;
14921507
input Component.Attributes outerAttr;
14931508
input InstNode redeclaredNode;
1509+
input Integer instLevel;
14941510
protected
14951511
Component orig_comp, rdcl_comp, new_comp;
14961512
Binding binding, condition;
@@ -1513,7 +1529,7 @@ algorithm
15131529
rdcl_node := InstNode.setNodeType(rdcl_type, redeclareNode);
15141530
rdcl_node := InstNode.copyInstancePtr(originalNode, rdcl_node);
15151531
rdcl_node := InstNode.updateComponent(InstNode.component(redeclareNode), rdcl_node);
1516-
instComponent(rdcl_node, outerAttr, constrainingMod, true, SOME(Component.getAttributes(orig_comp)));
1532+
instComponent(rdcl_node, outerAttr, constrainingMod, true, instLevel, SOME(Component.getAttributes(orig_comp)));
15171533
rdcl_comp := InstNode.component(rdcl_node);
15181534

15191535
new_comp := match (orig_comp, rdcl_comp)
@@ -1855,15 +1871,21 @@ function instTypeSpec
18551871
input InstNode scope;
18561872
input InstNode parent;
18571873
input SourceInfo info;
1874+
input Integer instLevel;
18581875
output InstNode node;
18591876
output Component.Attributes outAttributes;
18601877
algorithm
18611878
node := match typeSpec
18621879
case Absyn.TPATH()
18631880
algorithm
18641881
node := Lookup.lookupClassName(typeSpec.path, scope, info);
1882+
1883+
if instLevel >= 100 then
1884+
checkRecursiveDefinition(node, parent, limitReached = true);
1885+
end if;
1886+
18651887
node := expand(node);
1866-
(node, outAttributes) := instClass(node, modifier, attributes, useBinding, parent);
1888+
(node, outAttributes) := instClass(node, modifier, attributes, useBinding, instLevel, parent);
18671889
then
18681890
node;
18691891

@@ -1876,6 +1898,48 @@ algorithm
18761898
end match;
18771899
end instTypeSpec;
18781900

1901+
function checkRecursiveDefinition
1902+
"Prints an error if a component causes a loop in the instance tree, for
1903+
example because it has the same type as one of its parents. If the depth
1904+
limit of the instance tree is reached, indicated by limitReached = true, then
1905+
some error is always given. Otherwise an error is only given if an actual
1906+
issue can be detected."
1907+
input InstNode componentType;
1908+
input InstNode component;
1909+
input Boolean limitReached;
1910+
protected
1911+
InstNode parent = InstNode.parent(component);
1912+
InstNode parent_type;
1913+
algorithm
1914+
// Functions can contain instances of a parent, e.g. in equalityConstraint
1915+
// functions, so skip this check for functions.
1916+
if not Class.isFunction(InstNode.getClass(parent)) then
1917+
// Check whether any parent of the component has the same type as the component.
1918+
while not InstNode.isEmpty(parent) loop
1919+
parent_type := InstNode.classScope(parent);
1920+
1921+
// Check equality by comparing the definitions, because comparing the
1922+
// nodes or instances in the nodes is unreliable due to instantiation
1923+
// creating new nodes.
1924+
if referenceEq(InstNode.definition(componentType), InstNode.definition(parent_type)) then
1925+
Error.addSourceMessage(Error.RECURSIVE_DEFINITION,
1926+
{InstNode.name(component), InstNode.name(InstNode.classScope(InstNode.parent(component)))},
1927+
InstNode.info(component));
1928+
fail();
1929+
end if;
1930+
1931+
parent := InstNode.parent(parent);
1932+
end while;
1933+
end if;
1934+
1935+
if limitReached then
1936+
// If we couldn't determine the exact cause of the recursion, print a generic error.
1937+
Error.addSourceMessage(Error.INST_RECURSION_LIMIT_REACHED,
1938+
{Absyn.pathString(InstNode.scopePath(component))}, InstNode.info(component));
1939+
fail();
1940+
end if;
1941+
end checkRecursiveDefinition;
1942+
18791943
function instDimension
18801944
input output Dimension dimension;
18811945
input InstNode scope;
@@ -3039,7 +3103,7 @@ algorithm
30393103
// not part of the flat class.
30403104
if InstNode.isComponent(n) then
30413105
// The components shouldn't have been instantiated yet, so do it here.
3042-
instComponent(n, NFComponent.DEFAULT_ATTR, Modifier.NOMOD(), true);
3106+
instComponent(n, NFComponent.DEFAULT_ATTR, Modifier.NOMOD(), true, 0);
30433107

30443108
// If the component's class has a missingInnerMessage annotation, use it
30453109
// to give a diagnostic message.

Compiler/Util/Error.mo

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,8 @@ public constant Message VARIABLE_BINDING_DIMS_MISMATCH = MESSAGE(347, TRANSLATIO
824824
Util.gettext("Type mismatch in binding ‘%s = %s‘, expected array dimensions %s, got %s."));
825825
public constant Message MODIFIER_NON_ARRAY_TYPE_ERROR = MESSAGE(348, TRANSLATION(), ERROR(),
826826
Util.gettext("Non-array modification ‘%s‘ for array component ‘%s‘, possibly due to missing ‘each‘."));
827+
public constant Message INST_RECURSION_LIMIT_REACHED = MESSAGE(349, TRANSLATION(), ERROR(),
828+
Util.gettext("Recursion limit reached while instantiating ‘%s‘."));
827829
public constant Message INITIALIZATION_NOT_FULLY_SPECIFIED = MESSAGE(496, TRANSLATION(), WARNING(),
828830
Util.gettext("The initial conditions are not fully specified. %s."));
829831
public constant Message INITIALIZATION_OVER_SPECIFIED = MESSAGE(497, TRANSLATION(), WARNING(),

0 commit comments

Comments
 (0)