diff --git a/Compiler/NFFrontEnd/NFBinding.mo b/Compiler/NFFrontEnd/NFBinding.mo index c02d09889b..79f3cb1408 100644 --- a/Compiler/NFFrontEnd/NFBinding.mo +++ b/Compiler/NFFrontEnd/NFBinding.mo @@ -74,6 +74,13 @@ public Expression bindingExp; end FLAT_BINDING; + record CEVAL_BINDING + "Used by the constant evaluation for generated bindings (e.g. record + bindings constructed from the record fields) that should be discarded + during flattening." + Expression bindingExp; + end CEVAL_BINDING; + public function fromAbsyn input Option bindingExp; @@ -105,6 +112,17 @@ public end match; end isBound; + function isExplicitlyBound + input Binding binding; + output Boolean isBound; + algorithm + isBound := match binding + case UNBOUND() then false; + case CEVAL_BINDING() then false; + else true; + end match; + end isExplicitlyBound; + function isUnbound input Binding binding; output Boolean isUnbound; diff --git a/Compiler/NFFrontEnd/NFCeval.mo b/Compiler/NFFrontEnd/NFCeval.mo index 7c984c4659..88165add1d 100644 --- a/Compiler/NFFrontEnd/NFCeval.mo +++ b/Compiler/NFFrontEnd/NFCeval.mo @@ -46,6 +46,8 @@ import Type = NFType; import NFTyping.ExpOrigin; import ExpressionSimplify; import NFPrefixes.Variability; +import NFClassTree.ClassTree; +import ComplexType = NFComplexType; protected import NFFunction.Function; @@ -268,6 +270,10 @@ algorithm comp := InstNode.component(node); binding := Component.getBinding(comp); + if not Binding.isBound(binding) then + binding := makeComponentBinding(comp, node, originExp, target); + end if; + exp := match binding case Binding.TYPED_BINDING() algorithm @@ -281,6 +287,8 @@ algorithm then exp; + case Binding.CEVAL_BINDING() then binding.bindingExp; + case Binding.UNBOUND() algorithm printUnboundError(target, originExp); @@ -295,6 +303,59 @@ algorithm end match; end evalComponentBinding; +function makeComponentBinding + input Component component; + input InstNode node; + input Expression originExp; + input EvalTarget target; + output Binding binding; +protected + ClassTree tree; + array comps; + list fields; + Type ty; + InstNode rec_node; + Expression exp; + ComponentRef cr; +algorithm + binding := matchcontinue (component, originExp, node) + // A record component without an explicit binding, create one from its children. + case (Component.TYPED_COMPONENT(ty = Type.COMPLEX(complexTy = ComplexType.RECORD(rec_node))), + Expression.CREF(cref = cr), _) + algorithm + tree := Class.classTree(InstNode.getClass(component.classInst)); + comps := ClassTree.getComponents(tree); + fields := {}; + + for i in arrayLength(comps):-1:1 loop + ty := InstNode.getType(comps[i]); + fields := Expression.CREF(ty, + ComponentRef.CREF(comps[i], {}, ty, NFComponentRef.Origin.CREF, cr)) :: fields; + end for; + + exp := Expression.RECORD(InstNode.scopePath(rec_node), component.ty, fields); + exp := evalExp(exp); + binding := Binding.CEVAL_BINDING(exp); + InstNode.updateComponent(Component.setBinding(binding, component), node); + then + binding; + + // A record field without an explicit binding, evaluate the parent's binding + // if it as one and fetch the binding from it instead. + case (_, _, InstNode.COMPONENT_NODE(parent = rec_node as InstNode.COMPONENT_NODE())) + guard Type.isRecord(InstNode.getType(rec_node)) + algorithm + exp := evalComponentBinding(rec_node, Expression.EMPTY(), target); + exp := Expression.lookupRecordField(InstNode.name(node), exp); + binding := Binding.CEVAL_BINDING(exp); + InstNode.updateComponent(Component.setBinding(binding, component), node); + then + binding; + + else Binding.UNBOUND(NONE()); + end matchcontinue; +end makeComponentBinding; + function evalTypename input Type ty; input Expression originExp; diff --git a/Compiler/NFFrontEnd/NFFlatten.mo b/Compiler/NFFrontEnd/NFFlatten.mo index 6be13091b9..e501c0d134 100644 --- a/Compiler/NFFrontEnd/NFFlatten.mo +++ b/Compiler/NFFrontEnd/NFFlatten.mo @@ -379,7 +379,7 @@ algorithm binding := Component.getBinding(comp); // Create an equation if there's a binding on a complex component. - if Binding.isBound(binding) then + if Binding.isExplicitlyBound(binding) then binding := flattenBinding(binding, prefix, node); binding_exp := Binding.getTypedExp(binding); @@ -444,12 +444,12 @@ function flattenBinding input ComponentRef prefix; input InstNode component; algorithm - () := match binding + binding := match binding local list subs; Integer binding_level; - case Binding.UNBOUND() then (); + case Binding.UNBOUND() then binding; case Binding.TYPED_BINDING() algorithm @@ -460,7 +460,9 @@ algorithm binding.bindingExp := Expression.applySubscripts(subs, binding.bindingExp); end if; then - (); + binding; + + case Binding.CEVAL_BINDING() then Binding.UNBOUND(NONE()); else algorithm @@ -838,7 +840,7 @@ algorithm funcs := collectTypeFuncs(ty, funcs); // Collect functions used in the component's binding, if it has one. - if Binding.isBound(binding) then + if Binding.isExplicitlyBound(binding) then funcs := collectExpFuncs(Binding.getTypedExp(binding), funcs); end if; then @@ -1104,7 +1106,7 @@ algorithm funcs := collectTypeFuncs(Component.getType(comp), funcs); binding := Component.getBinding(comp); - if Binding.isBound(binding) then + if Binding.isExplicitlyBound(binding) then funcs := collectExpFuncs(Binding.getTypedExp(binding), funcs); end if; end for; diff --git a/Compiler/NFFrontEnd/NFType.mo b/Compiler/NFFrontEnd/NFType.mo index 845cfe2367..f1ba46a3e9 100644 --- a/Compiler/NFFrontEnd/NFType.mo +++ b/Compiler/NFFrontEnd/NFType.mo @@ -329,7 +329,7 @@ public output Boolean isRecord; algorithm isRecord := match ty - case COMPLEX() then Restriction.isRecord(Class.restriction(InstNode.getClass(ty.cls))); + case COMPLEX(complexTy = ComplexType.RECORD()) then true; else false; end match; end isRecord; diff --git a/Compiler/NFFrontEnd/NFTypeCheck.mo b/Compiler/NFFrontEnd/NFTypeCheck.mo index cedd121af3..a5897da2f2 100644 --- a/Compiler/NFFrontEnd/NFTypeCheck.mo +++ b/Compiler/NFFrontEnd/NFTypeCheck.mo @@ -124,7 +124,7 @@ function isValidArgumentMatch output Boolean v = kind == MatchKind.EXACT or kind == MatchKind.CAST or kind == MatchKind.GENERIC - ; + or kind == MatchKind.PLUG_COMPATIBLE; end isValidArgumentMatch; function isValidPlugCompatibleMatch diff --git a/Compiler/NFFrontEnd/NFTyping.mo b/Compiler/NFFrontEnd/NFTyping.mo index b73adbd5f7..c97666dd7e 100644 --- a/Compiler/NFFrontEnd/NFTyping.mo +++ b/Compiler/NFFrontEnd/NFTyping.mo @@ -84,6 +84,7 @@ import NFInstNode.CachedData; import Direction = NFPrefixes.Direction; import BindingOrigin = NFBindingOrigin; import ElementSource; +import StringUtil; uniontype TypingError record NO_ERROR end NO_ERROR; @@ -657,6 +658,11 @@ function typeComponentBinding protected InstNode node = InstNode.resolveOuter(component); Component c; + Binding binding; + InstNode cls; + MatchKind matchKind; + String name; + Variability comp_var, comp_eff_var, bind_var; algorithm if InstNode.isEmpty(component) then return; @@ -665,71 +671,67 @@ algorithm c := InstNode.component(node); () := match c - local - Binding binding; - InstNode cls; - MatchKind matchKind; - Boolean dirty; - String name; - Variability comp_var, comp_eff_var, bind_var; - - // A component that's already been typed. + // 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() + case Component.TYPED_COMPONENT(binding = Binding.UNBOUND()) algorithm - name := InstNode.name(component); checkBindingEach(c.binding, not Type.isArray(c.ty), InstNode.parent(component)); - binding := typeBinding(c.binding, intBitOr(origin, ExpOrigin.BINDING)); - dirty := not referenceEq(binding, c.binding); - - // If the binding changed during typing it means it was an untyped - // binding which is now typed, and it needs to be type checked. - if dirty then - binding := TypeCheck.matchBinding(binding, c.ty, name, InstNode.derivedParent(node)); - comp_var := Component.variability(c); - comp_eff_var := Prefixes.effectiveVariability(comp_var); - bind_var := Prefixes.effectiveVariability(Binding.variability(binding)); - - if bind_var > comp_eff_var then - if comp_var == Variability.PARAMETER and intBitAnd(origin, ExpOrigin.FUNCTION) > 0 then - Error.addSourceMessage(Error.FUNCTION_HIGHER_VARIABILITY_BINDING, { - name, Prefixes.variabilityString(comp_eff_var), - Binding.toString(c.binding), Prefixes.variabilityString(bind_var)}, - Binding.getInfo(binding)); - else - Error.addSourceMessage(Error.HIGHER_VARIABILITY_BINDING, { - name, Prefixes.variabilityString(comp_eff_var), - "'" + Binding.toString(c.binding) + "'", Prefixes.variabilityString(bind_var)}, - Binding.getInfo(binding)); - fail(); - end if; - end if; - // Evaluate the binding if the component is a constant. - if comp_var <= Variability.STRUCTURAL_PARAMETER then - // TODO: Allow this to fail for now. Once constant evaluation has - // been improved we should print an error when a constant binding - // couldn't be evaluated instead. - try - binding := evalBinding(binding, origin); - else - end try; + 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); + binding := typeBinding(c.binding, intBitOr(origin, ExpOrigin.BINDING)); + checkBindingEach(binding, not Type.isArray(c.ty), InstNode.parent(component)); + + binding := TypeCheck.matchBinding(binding, c.ty, name, InstNode.derivedParent(node)); + comp_var := Component.variability(c); + comp_eff_var := Prefixes.effectiveVariability(comp_var); + bind_var := Prefixes.effectiveVariability(Binding.variability(binding)); + + if bind_var > comp_eff_var then + if comp_var == Variability.PARAMETER and intBitAnd(origin, ExpOrigin.FUNCTION) > 0 then + Error.addSourceMessage(Error.FUNCTION_HIGHER_VARIABILITY_BINDING, { + name, Prefixes.variabilityString(comp_eff_var), + Binding.toString(c.binding), Prefixes.variabilityString(bind_var)}, + Binding.getInfo(binding)); + else + Error.addSourceMessage(Error.HIGHER_VARIABILITY_BINDING, { + name, Prefixes.variabilityString(comp_eff_var), + "'" + Binding.toString(c.binding) + "'", Prefixes.variabilityString(bind_var)}, + Binding.getInfo(binding)); + fail(); end if; + end if; - c.binding := binding; + // Evaluate the binding if the component is a constant. + if comp_var <= Variability.STRUCTURAL_PARAMETER then + // TODO: Allow this to fail for now. Once constant evaluation has + // been improved we should print an error when a constant binding + // couldn't be evaluated instead. + try + binding := evalBinding(binding, origin); + else + end try; end if; + c.binding := binding; + if Binding.isBound(c.condition) then c.condition := typeComponentCondition(c.condition, origin); - dirty := true; - end if; - - // Update the node if the component changed. - if dirty then - InstNode.updateComponent(c, node); end if; + InstNode.updateComponent(c, node); typeBindings(c.classInst, component, origin); then ();