From bac7d12e38583fd8676a59e1cdf691282367d544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20=C3=96stlund?= Date: Wed, 10 Oct 2018 15:13:46 +0200 Subject: [PATCH] [NF] Improve typing of record field dimensions. - Handle typing of record field dimensions where the record instance has a binding. Belonging to [master]: - OpenModelica/OMCompiler#2711 - OpenModelica/OpenModelica-testsuite#1048 --- Compiler/NFFrontEnd/NFBinding.mo | 40 +++++++++++++ Compiler/NFFrontEnd/NFCeval.mo | 27 +++++++++ Compiler/NFFrontEnd/NFClass.mo | 16 ++++++ Compiler/NFFrontEnd/NFClassTree.mo | 13 +++++ Compiler/NFFrontEnd/NFExpression.mo | 85 +++++++++++++++++++++++++++ Compiler/NFFrontEnd/NFTyping.mo | 89 +++++++++++++++++++++-------- 6 files changed, 247 insertions(+), 23 deletions(-) diff --git a/Compiler/NFFrontEnd/NFBinding.mo b/Compiler/NFFrontEnd/NFBinding.mo index 7379b2885b5..34d2dc99e8f 100644 --- a/Compiler/NFFrontEnd/NFBinding.mo +++ b/Compiler/NFFrontEnd/NFBinding.mo @@ -250,6 +250,46 @@ public end match; end isRecordExp; + function recordFieldBinding + input String fieldName; + input Binding recordBinding; + output Binding fieldBinding = recordBinding; + protected + Expression exp; + Type ty; + Variability var; + algorithm + fieldBinding := match fieldBinding + case UNTYPED_BINDING() + algorithm + fieldBinding.bindingExp := Expression.recordElement(fieldName, fieldBinding.bindingExp); + then + fieldBinding; + + case TYPED_BINDING() + algorithm + exp := Expression.recordElement(fieldName, fieldBinding.bindingExp); + ty := Expression.typeOf(exp); + var := Expression.variability(exp); + then + TYPED_BINDING(exp, ty, var, fieldBinding.parents, fieldBinding.isEach, fieldBinding.info); + + case FLAT_BINDING() + algorithm + exp := Expression.recordElement(fieldName, fieldBinding.bindingExp); + var := Expression.variability(exp); + then + FLAT_BINDING(exp, var); + + case CEVAL_BINDING() + algorithm + fieldBinding.bindingExp := Expression.recordElement(fieldName, fieldBinding.bindingExp); + then + fieldBinding; + + end match; + end recordFieldBinding; + function variability input Binding binding; output Variability var; diff --git a/Compiler/NFFrontEnd/NFCeval.mo b/Compiler/NFFrontEnd/NFCeval.mo index cb0feccc9d2..c33cafb315a 100644 --- a/Compiler/NFFrontEnd/NFCeval.mo +++ b/Compiler/NFFrontEnd/NFCeval.mo @@ -239,6 +239,9 @@ algorithm then Expression.tupleElement(exp1, exp.ty, exp.index); + case Expression.RECORD_ELEMENT() + then evalRecordElement(exp, target); + case Expression.MUTABLE() algorithm exp1 := evalExp(Mutable.access(exp.exp), target); @@ -2800,6 +2803,30 @@ algorithm result := Expression.applySubscripts(subs, result); end evalSubscriptedExp; +function evalRecordElement + input Expression exp; + input EvalTarget target; + output Expression result; +protected + Expression e; + Integer index; +algorithm + Expression.RECORD_ELEMENT(recordExp = e, index = index) := exp; + e := evalExp(e, target); + + result := match e + case Expression.RECORD() + then listGet(e.elements, index); + + else + algorithm + Error.assertion(false, getInstanceName() + " could not evaluate " + + Expression.toString(exp), sourceInfo()); + then + fail(); + end match; +end evalRecordElement; + protected function printUnboundError diff --git a/Compiler/NFFrontEnd/NFClass.mo b/Compiler/NFFrontEnd/NFClass.mo index f7f93ff3fe8..6f056a7acd4 100644 --- a/Compiler/NFFrontEnd/NFClass.mo +++ b/Compiler/NFFrontEnd/NFClass.mo @@ -206,6 +206,22 @@ uniontype Class (node, isImport) := ClassTree.lookupElement(name, classTree(cls)); end lookupElement; + function lookupComponentIndex + input String name; + input Class cls; + output Integer index; + algorithm + index := ClassTree.lookupComponentIndex(name, classTree(cls)); + end lookupComponentIndex; + + function nthComponent + input Integer index; + input Class cls; + output InstNode component; + algorithm + component := ClassTree.nthComponent(index, classTree(cls)); + end nthComponent; + function lookupAttributeBinding input String name; input Class cls; diff --git a/Compiler/NFFrontEnd/NFClassTree.mo b/Compiler/NFFrontEnd/NFClassTree.mo index 4b37b382ac2..f8df2f9a34a 100644 --- a/Compiler/NFFrontEnd/NFClassTree.mo +++ b/Compiler/NFFrontEnd/NFClassTree.mo @@ -861,6 +861,19 @@ public LookupTree.get(lookupTree(tree), name); end lookupComponentIndex; + function nthComponent + input Integer index; + input ClassTree tree; + output InstNode component; + algorithm + component := match tree + case PARTIAL_TREE() then arrayGet(tree.components, index); + case EXPANDED_TREE() then arrayGet(tree.components, index); + case INSTANTIATED_TREE() then Mutable.access(arrayGet(tree.components, index)); + case FLAT_TREE() then arrayGet(tree.components, index); + end match; + end nthComponent; + function mapClasses input ClassTree tree; input FuncT func; diff --git a/Compiler/NFFrontEnd/NFExpression.mo b/Compiler/NFFrontEnd/NFExpression.mo index b3f16b7c5df..35c80b89936 100644 --- a/Compiler/NFFrontEnd/NFExpression.mo +++ b/Compiler/NFFrontEnd/NFExpression.mo @@ -283,6 +283,13 @@ public Type ty; end TUPLE_ELEMENT; + record RECORD_ELEMENT + Expression recordExp; + Integer index; + String fieldName; + Type ty; + end RECORD_ELEMENT; + record BOX "MetaModelica boxed value" Expression exp; end BOX; @@ -613,6 +620,17 @@ public then comp; + case RECORD_ELEMENT() + algorithm + RECORD_ELEMENT(recordExp = e1, index = i) := exp2; + comp := Util.intCompare(exp1.index, i); + + if comp == 0 then + comp := compare(exp1.recordExp, e1); + end if; + then + comp; + else algorithm Error.assertion(false, getInstanceName() + " got unknown expression.", sourceInfo()); @@ -694,6 +712,7 @@ public case UNBOX() then exp.ty; case SUBSCRIPTED_EXP() then exp.ty; case TUPLE_ELEMENT() then exp.ty; + case RECORD_ELEMENT() then exp.ty; case BOX() then Type.METABOXED(typeOf(exp.exp)); case MUTABLE() then typeOf(Mutable.access(exp.exp)); case EMPTY() then exp.ty; @@ -723,6 +742,7 @@ public case UNBOX() algorithm exp.ty := ty; then (); case SUBSCRIPTED_EXP() algorithm exp.ty := ty; then (); case TUPLE_ELEMENT() algorithm exp.ty := ty; then (); + case RECORD_ELEMENT() algorithm exp.ty := ty; then (); else (); end match; end setType; @@ -1351,6 +1371,7 @@ public case CAST() then "CAST(" + Type.toString(exp.ty) + ", " + toString(exp.exp) + ")"; case SUBSCRIPTED_EXP() then toString(exp.exp) + Subscript.toStringList(exp.subscripts); case TUPLE_ELEMENT() then toString(exp.tupleExp) + "[" + intString(exp.index) + "]"; + case RECORD_ELEMENT() then toString(exp.recordExp) + "[field: " + exp.fieldName + "]"; case MUTABLE() then toString(Mutable.access(exp.exp)); case EMPTY() then "#EMPTY#"; @@ -1502,6 +1523,9 @@ public case TUPLE_ELEMENT() then DAE.TSUB(toDAE(exp.tupleExp), exp.index, Type.toDAE(exp.ty)); + case RECORD_ELEMENT() + then DAE.RSUB(toDAE(exp.recordExp), exp.index, exp.fieldName, Type.toDAE(exp.ty)); + else algorithm Error.assertion(false, getInstanceName() + " got unknown expression '" + toString(exp) + "'", sourceInfo()); @@ -1714,6 +1738,12 @@ public then if referenceEq(exp.tupleExp, e1) then exp else TUPLE_ELEMENT(e1, exp.index, exp.ty); + case RECORD_ELEMENT() + algorithm + e1 := map(exp.recordExp, func); + then + if referenceEq(exp.recordExp, e1) then exp else RECORD_ELEMENT(e1, exp.index, exp.fieldName, exp.ty); + case BOX() algorithm e1 := map(exp.exp, func); @@ -2040,6 +2070,12 @@ public then if referenceEq(exp.tupleExp, e1) then exp else TUPLE_ELEMENT(e1, exp.index, exp.ty); + case RECORD_ELEMENT() + algorithm + e1 := func(exp.recordExp); + then + if referenceEq(exp.recordExp, e1) then exp else RECORD_ELEMENT(e1, exp.index, exp.fieldName, exp.ty); + case BOX() algorithm e1 := func(exp.exp); @@ -2341,6 +2377,7 @@ public List.fold(exp.subscripts, function Subscript.foldExp(func = func), result); case TUPLE_ELEMENT() then fold(exp.tupleExp, func, arg); + case RECORD_ELEMENT() then fold(exp.recordExp, func, arg); case BOX() then fold(exp.exp, func, arg); case MUTABLE() then fold(Mutable.access(exp.exp), func, arg); else arg; @@ -2596,6 +2633,7 @@ public (); case TUPLE_ELEMENT() algorithm apply(exp.tupleExp, func); then (); + case RECORD_ELEMENT() algorithm apply(exp.recordExp, func); then (); case BOX() algorithm apply(exp.exp, func); then (); case MUTABLE() algorithm apply(Mutable.access(exp.exp), func); then (); else (); @@ -2912,6 +2950,12 @@ public then if referenceEq(exp.tupleExp, e1) then exp else TUPLE_ELEMENT(e1, exp.index, exp.ty); + case RECORD_ELEMENT() + algorithm + (e1, arg) := mapFold(exp.recordExp, func, arg); + then + if referenceEq(exp.recordExp, e1) then exp else RECORD_ELEMENT(e1, exp.index, exp.fieldName, exp.ty); + case MUTABLE() algorithm (e1, arg) := mapFold(Mutable.access(exp.exp), func, arg); @@ -3225,6 +3269,12 @@ public then if referenceEq(exp.tupleExp, e1) then exp else TUPLE_ELEMENT(e1, exp.index, exp.ty); + case RECORD_ELEMENT() + algorithm + (e1, arg) := func(exp.recordExp, arg); + then + if referenceEq(exp.recordExp, e1) then exp else RECORD_ELEMENT(e1, exp.index, exp.fieldName, exp.ty); + case MUTABLE() algorithm (e1, arg) := func(Mutable.access(exp.exp), arg); @@ -3535,6 +3585,9 @@ public case TUPLE_ELEMENT() then contains(exp.tupleExp, func); + case RECORD_ELEMENT() + then contains(exp.recordExp, func); + case BOX() then contains(exp.exp, func); else false; end match; @@ -4171,6 +4224,7 @@ public case SUBSCRIPTED_EXP() then Prefixes.variabilityMax(variability(exp.exp), Subscript.variabilityList(exp.subscripts)); case TUPLE_ELEMENT() then variability(exp.tupleExp); + case RECORD_ELEMENT() then variability(exp.recordExp); case BOX() then variability(exp.exp); else algorithm @@ -4303,6 +4357,37 @@ public end match; end tupleElement; + function recordElement + input String elementName; + input Expression recordExp; + output Expression outExp; + algorithm + outExp := match recordExp + local + InstNode node; + Class cls; + Type ty; + Integer index; + + case RECORD(ty = Type.COMPLEX(cls = node)) + algorithm + cls := InstNode.getClass(node); + index := Class.lookupComponentIndex(elementName, cls); + then + listGet(recordExp.elements, index); + + else + algorithm + Type.COMPLEX(cls = node) := typeOf(recordExp); + cls := InstNode.getClass(node); + index := Class.lookupComponentIndex(elementName, cls); + ty := InstNode.getType(Class.nthComponent(index, cls)); + then + RECORD_ELEMENT(recordExp, index, elementName, ty); + + end match; + end recordElement; + function splitRecord input Expression recordExp; output list recordFields; diff --git a/Compiler/NFFrontEnd/NFTyping.mo b/Compiler/NFFrontEnd/NFTyping.mo index 1a219d1cf99..c3ee2068dcc 100644 --- a/Compiler/NFFrontEnd/NFTyping.mo +++ b/Compiler/NFFrontEnd/NFTyping.mo @@ -583,12 +583,19 @@ algorithm // If the dimension is unknown in a class, try to infer it from the components binding. case Dimension.UNKNOWN() algorithm - // If the component doesn't have a binding, try to use the start attribute instead. - // TODO: Any attribute should actually be fine to use here. - b := if Binding.isUnbound(binding) then - Class.lookupAttributeBinding("start", InstNode.getClass(component)) - else - binding; + b := binding; + + if Binding.isUnbound(binding) then + // If the component has no binding, try to use its parent's binding + // (i.e. for record fields where the record instance has a binding). + b := getRecordElementBinding(component); + + if Binding.isUnbound(b) then + // If the component still doesn't have a binding, try to use the start attribute instead. + // TODO: Any attribute should actually be fine to use here. + b := Class.lookupAttributeBinding("start", InstNode.getClass(component)); + end if; + end if; dim := match b // Print an error if there's no binding. @@ -638,6 +645,45 @@ algorithm end match; end typeDimension; +function getRecordElementBinding + "Tries to fetch the binding for a given record field by using the binding of + the record instance." + input InstNode component; + output Binding binding; +protected + InstNode parent; + Expression exp; + Binding parent_binding; +algorithm + parent := InstNode.parent(component); + + if InstNode.isComponent(parent) then + // Get the binding of the component's parent. + parent_binding := Component.getBinding(InstNode.component(parent)); + + if Binding.isUnbound(parent_binding) then + // If the parent has no binding, try the parent's parent. + binding := getRecordElementBinding(parent); + else + // Otherwise type the binding, so we can safely look up the field name. + binding := typeBinding(parent_binding, ExpOrigin.CLASS); + + // If the binding wasn't typed before, update the parent component with it + // so we don't have to type it again. + if not referenceEq(parent_binding, binding) then + InstNode.componentApply(parent, Component.setBinding, binding); + end if; + end if; + + // If we found a binding, get the binding for the field from it. + if Binding.isBound(binding) then + binding := Binding.recordFieldBinding(InstNode.name(component), binding); + end if; + else + binding := NFBinding.EMPTY_BINDING; + end if; +end getRecordElementBinding; + function typeBindings input InstNode cls; input InstNode component; @@ -702,23 +748,6 @@ algorithm c := InstNode.component(node); () := match c - // A component with a binding that's already been typed. - case Component.TYPED_COMPONENT(binding = Binding.TYPED_BINDING()) then (); - case Component.TYPED_COMPONENT(binding = Binding.CEVAL_BINDING()) then (); - - case Component.TYPED_COMPONENT(binding = Binding.UNBOUND()) - algorithm - checkBindingEach(c.binding); - - if Binding.isBound(c.condition) then - c.condition := typeComponentCondition(c.condition, origin); - InstNode.updateComponent(c, node); - end if; - - typeBindings(c.classInst, component, origin); - then - (); - case Component.TYPED_COMPONENT(binding = Binding.UNTYPED_BINDING()) algorithm name := InstNode.name(component); @@ -749,6 +778,20 @@ algorithm then (); + // A component without a binding, or with a binding that's already been typed. + case Component.TYPED_COMPONENT() + algorithm + checkBindingEach(c.binding); + + if Binding.isBound(c.condition) then + c.condition := typeComponentCondition(c.condition, origin); + InstNode.updateComponent(c, node); + end if; + + typeBindings(c.classInst, component, origin); + then + (); + case Component.ENUM_LITERAL() then (); case Component.TYPE_ATTRIBUTE(modifier = Modifier.NOMOD()) then ();