Skip to content

Commit dfb7517

Browse files
perostOpenModelica-Hudson
authored andcommitted
[NF] Do substitution of default function arguments.
- Substitute parameter names in default arguments with the actual bindings of the parameters. Belonging to [master]: - OpenModelica/OMCompiler#2649 - OpenModelica/OpenModelica-testsuite#1031
1 parent 3f9eccc commit dfb7517

File tree

2 files changed

+188
-60
lines changed

2 files changed

+188
-60
lines changed

Compiler/NFFrontEnd/NFBuiltinFuncs.mo

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import ComponentRef = NFComponentRef;
5757
import NFComponentRef.Origin;
5858
import NFModifier.Modifier;
5959
import Sections = NFSections;
60+
import NFFunction.SlotEvalStatus;
6061

6162
protected
6263
import MetaModelica.Dangerous.*;
@@ -127,7 +128,7 @@ constant InstNode INTEGER_DUMMY_NODE = NFInstNode.CLASS_NODE("Integer",
127128

128129
constant Function INTEGER_FUNCTION = Function.FUNCTION(Path.IDENT("Integer"),
129130
INTEGER_DUMMY_NODE, {ENUM_PARAM}, {}, {}, {
130-
Slot.SLOT("e", SlotType.POSITIONAL, NONE(), NONE())
131+
Slot.SLOT("e", SlotType.POSITIONAL, NONE(), NONE(), 1, SlotEvalStatus.NOT_EVALUATED)
131132
}, Type.INTEGER(), DAE.FUNCTION_ATTRIBUTES_BUILTIN, {}, Pointer.createImmutable(true), Pointer.createImmutable(0));
132133

133134
constant InstNode INTEGER_NODE = InstNode.CLASS_NODE("IntegerFunc",
@@ -149,45 +150,45 @@ constant InstNode STRING_DUMMY_NODE = NFInstNode.CLASS_NODE("String",
149150
// String(r, significantDigits=d, minimumLength=0, leftJustified=true)
150151
constant Function STRING_REAL = Function.FUNCTION(Path.IDENT("String"),
151152
STRING_DUMMY_NODE, {REAL_PARAM, INT_PARAM, INT_PARAM, BOOL_PARAM}, {STRING_PARAM}, {}, {
152-
Slot.SLOT("r", SlotType.POSITIONAL, NONE(), NONE()),
153-
Slot.SLOT("significantDigits", SlotType.NAMED, SOME(Expression.INTEGER(6)), NONE()),
154-
Slot.SLOT("minimumLength", SlotType.NAMED, SOME(Expression.INTEGER(0)), NONE()),
155-
Slot.SLOT("leftJustified", SlotType.NAMED, SOME(Expression.BOOLEAN(true)), NONE())
153+
Slot.SLOT("r", SlotType.POSITIONAL, NONE(), NONE(), 1, SlotEvalStatus.NOT_EVALUATED),
154+
Slot.SLOT("significantDigits", SlotType.NAMED, SOME(Expression.INTEGER(6)), NONE(), 2, SlotEvalStatus.NOT_EVALUATED),
155+
Slot.SLOT("minimumLength", SlotType.NAMED, SOME(Expression.INTEGER(0)), NONE(), 3, SlotEvalStatus.NOT_EVALUATED),
156+
Slot.SLOT("leftJustified", SlotType.NAMED, SOME(Expression.BOOLEAN(true)), NONE(), 4, SlotEvalStatus.NOT_EVALUATED)
156157
}, Type.STRING(), DAE.FUNCTION_ATTRIBUTES_BUILTIN, {},
157158
Pointer.createImmutable(true), Pointer.createImmutable(0));
158159

159160
// String(r, format="-0.6g")
160161
constant Function STRING_REAL_FORMAT = Function.FUNCTION(Path.IDENT("String"),
161162
STRING_DUMMY_NODE, {REAL_PARAM, STRING_PARAM}, {STRING_PARAM}, {}, {
162-
Slot.SLOT("r", SlotType.POSITIONAL, NONE(), NONE()),
163-
Slot.SLOT("format", SlotType.NAMED, NONE(), NONE())
163+
Slot.SLOT("r", SlotType.POSITIONAL, NONE(), NONE(), 1, SlotEvalStatus.NOT_EVALUATED),
164+
Slot.SLOT("format", SlotType.NAMED, NONE(), NONE(), 2, SlotEvalStatus.NOT_EVALUATED)
164165
}, Type.STRING(), DAE.FUNCTION_ATTRIBUTES_BUILTIN, {},
165166
Pointer.createImmutable(true), Pointer.createImmutable(0));
166167

167168
// String(i, minimumLength=0, leftJustified=true)
168169
constant Function STRING_INT = Function.FUNCTION(Path.IDENT("String"),
169170
STRING_DUMMY_NODE, {INT_PARAM, INT_PARAM, BOOL_PARAM}, {STRING_PARAM}, {}, {
170-
Slot.SLOT("i", SlotType.POSITIONAL, NONE(), NONE()),
171-
Slot.SLOT("minimumLength", SlotType.NAMED, SOME(Expression.INTEGER(0)), NONE()),
172-
Slot.SLOT("leftJustified", SlotType.NAMED, SOME(Expression.BOOLEAN(true)), NONE())
171+
Slot.SLOT("i", SlotType.POSITIONAL, NONE(), NONE(), 1, SlotEvalStatus.NOT_EVALUATED),
172+
Slot.SLOT("minimumLength", SlotType.NAMED, SOME(Expression.INTEGER(0)), NONE(), 2, SlotEvalStatus.NOT_EVALUATED),
173+
Slot.SLOT("leftJustified", SlotType.NAMED, SOME(Expression.BOOLEAN(true)), NONE(), 3, SlotEvalStatus.NOT_EVALUATED)
173174
}, Type.STRING(), DAE.FUNCTION_ATTRIBUTES_BUILTIN, {},
174175
Pointer.createImmutable(true), Pointer.createImmutable(0));
175176

176177
// String(b, minimumLength=0, leftJustified=true)
177178
constant Function STRING_BOOL = Function.FUNCTION(Path.IDENT("String"),
178179
STRING_DUMMY_NODE, {BOOL_PARAM, INT_PARAM, BOOL_PARAM}, {STRING_PARAM}, {}, {
179-
Slot.SLOT("b", SlotType.POSITIONAL, NONE(), NONE()),
180-
Slot.SLOT("minimumLength", SlotType.NAMED, SOME(Expression.INTEGER(0)), NONE()),
181-
Slot.SLOT("leftJustified", SlotType.NAMED, SOME(Expression.BOOLEAN(true)), NONE())
180+
Slot.SLOT("b", SlotType.POSITIONAL, NONE(), NONE(), 1, SlotEvalStatus.NOT_EVALUATED),
181+
Slot.SLOT("minimumLength", SlotType.NAMED, SOME(Expression.INTEGER(0)), NONE(), 2, SlotEvalStatus.NOT_EVALUATED),
182+
Slot.SLOT("leftJustified", SlotType.NAMED, SOME(Expression.BOOLEAN(true)), NONE(), 3, SlotEvalStatus.NOT_EVALUATED)
182183
}, Type.STRING(), DAE.FUNCTION_ATTRIBUTES_BUILTIN, {},
183184
Pointer.createImmutable(true), Pointer.createImmutable(0));
184185

185186
// String(e, minimumLength=0, leftJustified=true)
186187
constant Function STRING_ENUM = Function.FUNCTION(Path.IDENT("String"),
187188
STRING_DUMMY_NODE, {ENUM_PARAM, INT_PARAM, BOOL_PARAM}, {STRING_PARAM}, {}, {
188-
Slot.SLOT("e", SlotType.POSITIONAL, NONE(), NONE()),
189-
Slot.SLOT("minimumLength", SlotType.NAMED, SOME(Expression.INTEGER(0)), NONE()),
190-
Slot.SLOT("leftJustified", SlotType.NAMED, SOME(Expression.BOOLEAN(true)), NONE())
189+
Slot.SLOT("e", SlotType.POSITIONAL, NONE(), NONE(), 1, SlotEvalStatus.NOT_EVALUATED),
190+
Slot.SLOT("minimumLength", SlotType.NAMED, SOME(Expression.INTEGER(0)), NONE(), 2, SlotEvalStatus.NOT_EVALUATED),
191+
Slot.SLOT("leftJustified", SlotType.NAMED, SOME(Expression.BOOLEAN(true)), NONE(), 3, SlotEvalStatus.NOT_EVALUATED)
191192
}, Type.STRING(), DAE.FUNCTION_ATTRIBUTES_BUILTIN, {},
192193
Pointer.createImmutable(true), Pointer.createImmutable(0));
193194

Compiler/NFFrontEnd/NFFunction.mo

Lines changed: 171 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ import Statement = NFStatement;
7171
import Sections = NFSections;
7272
import Algorithm = NFAlgorithm;
7373
import OperatorOverloading = NFOperatorOverloading;
74+
import MetaModelica.Dangerous.listReverseInPlace;
75+
import Array;
7476

7577

7678
public
@@ -85,12 +87,16 @@ type SlotType = enumeration(
8587
GENERIC "Accepts both positional and named arguments."
8688
) "Determines which type of argument a slot accepts.";
8789

90+
type SlotEvalStatus = enumeration(NOT_EVALUATED, EVALUATING, EVALUATED);
91+
8892
uniontype Slot
8993
record SLOT
9094
String name;
9195
SlotType ty;
9296
Option<Expression> default;
9397
Option<TypedArg> arg;
98+
Integer index;
99+
SlotEvalStatus evalStatus;
94100
end SLOT;
95101

96102
function positional
@@ -114,6 +120,12 @@ uniontype Slot
114120
else false;
115121
end match;
116122
end named;
123+
124+
function hasName
125+
input String name;
126+
input Slot slot;
127+
output Boolean hasName = name == slot.name;
128+
end hasName;
117129
end Slot;
118130

119131
public
@@ -582,55 +594,42 @@ uniontype Function
582594
output Boolean matching;
583595
protected
584596
Slot slot;
585-
list<Slot> slots;
597+
list<Slot> slots, remaining_slots;
586598
list<TypedArg> filled_named_args;
599+
array<Slot> slots_arr;
600+
Integer pos_arg_count, slot_count, index = 1;
587601
algorithm
588602
slots := fn.slots;
603+
pos_arg_count := listLength(posArgs);
604+
slot_count := listLength(slots);
589605

590-
if listLength(posArgs) > listLength(slots) then
606+
if pos_arg_count > slot_count then
607+
// If we have too many positional arguments it can't possibly match.
591608
matching := false;
592609
return;
610+
elseif pos_arg_count == slot_count and listEmpty(namedArgs) then
611+
// If we have exactly as many positional arguments as slots and no named
612+
// arguments we can just return the list of arguments as it is.
613+
matching := true;
614+
return;
593615
end if;
594-
// Remove as many slots as there are positional arguments. We don't actually
595-
// need to fill the slots, the positional arguments will always be first
596-
// anyway. This makes it a bit slower to figure out what error to give if a
597-
// named argument is wrong, but faster for the most common case of
598-
// everything being correct.
616+
617+
slots_arr := listArray(slots);
618+
599619
for arg in args loop
600-
slot :: slots := slots;
620+
slot := slots_arr[index];
601621

602622
if not Slot.positional(slot) then
603623
// Slot doesn't allow positional arguments (used for some builtin functions).
604624
matching := false;
605625
return;
606626
end if;
607-
end for;
608-
609-
// Fill the remaining slots with the named arguments.
610-
(filled_named_args, matching) := fillNamedArgs(namedArgs, slots, fn, info);
611627

612-
// Append the now ordered named arguments to the positional arguments.
613-
if matching then
614-
args := listAppend(posArgs, filled_named_args);
615-
end if;
616-
end fillArgs;
628+
slot.arg := SOME(arg);
629+
arrayUpdate(slots_arr, index, slot);
630+
index := index + 1;
631+
end for;
617632

618-
function fillNamedArgs
619-
"Sorts a list of named arguments based on the given slots, and returns the
620-
arguments for the slots if the arguments are correct. If the arguments
621-
are not correct the list of expressions returned is undefined, along with
622-
the matching output being false."
623-
input list<TypedNamedArg> namedArgs;
624-
input list<Slot> slots;
625-
input Function fn;
626-
input SourceInfo info;
627-
output list<TypedArg> args = {};
628-
output Boolean matching = true;
629-
protected
630-
array<Slot> slots_arr = listArray(slots);
631-
String name;
632-
Expression arg;
633-
algorithm
634633
for narg in namedArgs loop
635634
(slots_arr, matching) := fillNamedArg(narg, slots_arr, fn, info);
636635

@@ -640,7 +639,7 @@ uniontype Function
640639
end for;
641640

642641
(args, matching) := collectArgs(slots_arr, info);
643-
end fillNamedArgs;
642+
end fillArgs;
644643

645644
function fillNamedArg
646645
"Looks up a slot with the given name and tries to fill it with the given
@@ -658,7 +657,9 @@ uniontype Function
658657
Variability var;
659658
algorithm
660659
// Try to find a slot and fill it with the argument expression.
661-
for i in 1:arrayLength(slots) loop
660+
// Positional arguments fill the slots from the start of the array, so
661+
// searching backwards will generally be a bit more efficient.
662+
for i in arrayLength(slots):-1:1 loop
662663
s := slots[i];
663664

664665
(argName, argExp, ty, var) := inArg;
@@ -718,22 +719,133 @@ uniontype Function
718719
for s in slots loop
719720
SLOT(name = name, default = default, arg = arg) := s;
720721

721-
args := match (default, arg)
722-
case (_, SOME(a)) then a :: args; // Use the argument from the call if one was given.
723-
// TODO: save this info in the defaults in slots (the type we can get from the exp manually but the variability is lost.).
724-
case (SOME(e), _) then (e,Expression.typeOf(e),Variability.CONSTANT) ::args; // Otherwise, check that a default value exists.
725-
else // Give an error if no argument was given and there's no default value.
722+
args := matchcontinue arg
723+
// Use the argument from the call if one was given.
724+
case SOME(a) then a :: args;
725+
726+
// Otherwise, try to fill the slot with its default argument.
727+
case _ then fillDefaultSlot(s, slots, info) :: args;
728+
729+
else
726730
algorithm
727-
Error.addSourceMessage(Error.UNFILLED_SLOT, {name}, info);
728731
matching := false;
729732
then
730733
args;
731-
end match;
734+
end matchcontinue;
732735
end for;
733736

734737
args := listReverse(args);
735738
end collectArgs;
736739

740+
function fillDefaultSlot
741+
input Slot slot;
742+
input array<Slot> slots;
743+
input SourceInfo info;
744+
output TypedArg outArg;
745+
algorithm
746+
outArg := match slot
747+
// Slot already filled by function argument.
748+
case SLOT(arg = SOME(outArg)) then outArg;
749+
750+
// Slot not filled by function argument, but has default value.
751+
case SLOT(default = SOME(_))
752+
then fillDefaultSlot2(slot, slots, info);
753+
754+
// Give an error if no argument was given and there's no default argument.
755+
else
756+
algorithm
757+
Error.addSourceMessage(Error.UNFILLED_SLOT, {slot.name}, info);
758+
then
759+
fail();
760+
761+
end match;
762+
end fillDefaultSlot;
763+
764+
function fillDefaultSlot2
765+
input Slot slot;
766+
input array<Slot> slots;
767+
input SourceInfo info;
768+
output TypedArg outArg;
769+
algorithm
770+
outArg := match slot.evalStatus
771+
local
772+
Expression exp;
773+
774+
// An already evaluated slot, return its binding.
775+
case SlotEvalStatus.EVALUATED
776+
then Util.getOption(slot.arg);
777+
778+
// A slot in the process of being evaluated => cyclic bindings.
779+
case SlotEvalStatus.EVALUATING
780+
algorithm
781+
Error.addSourceMessage(Error.CYCLIC_DEFAULT_VALUE, {slot.name}, info);
782+
then
783+
fail();
784+
785+
// A slot with a not evaluated binding, evaluate the binding and return it.
786+
case SlotEvalStatus.NOT_EVALUATED
787+
algorithm
788+
slot.evalStatus := SlotEvalStatus.EVALUATING;
789+
arrayUpdate(slots, slot.index, slot);
790+
791+
exp := evaluateSlotExp(Util.getOption(slot.default), slots, info);
792+
outArg := (exp, Expression.typeOf(exp), Expression.variability(exp));
793+
794+
slot.arg := SOME(outArg);
795+
slot.evalStatus := SlotEvalStatus.EVALUATED;
796+
arrayUpdate(slots, slot.index, slot);
797+
then
798+
outArg;
799+
800+
end match;
801+
end fillDefaultSlot2;
802+
803+
function evaluateSlotExp
804+
input Expression exp;
805+
input array<Slot> slots;
806+
input SourceInfo info;
807+
output Expression outExp;
808+
algorithm
809+
outExp := Expression.map(exp,
810+
function evaluateSlotExp_traverser(slots = slots, info = info));
811+
end evaluateSlotExp;
812+
813+
function evaluateSlotExp_traverser
814+
input Expression exp;
815+
input array<Slot> slots;
816+
input SourceInfo info;
817+
output Expression outExp;
818+
algorithm
819+
outExp := match exp
820+
local
821+
ComponentRef cref;
822+
Option<Slot> slot;
823+
824+
case Expression.CREF(cref = cref as ComponentRef.CREF(restCref = ComponentRef.EMPTY()))
825+
algorithm
826+
slot := lookupSlotInArray(ComponentRef.firstName(cref), slots);
827+
then
828+
if isSome(slot) then Util.tuple31(fillDefaultSlot(Util.getOption(slot), slots, info)) else exp;
829+
830+
else exp;
831+
end match;
832+
end evaluateSlotExp_traverser;
833+
834+
function lookupSlotInArray
835+
input String slotName;
836+
input array<Slot> slots;
837+
output Option<Slot> outSlot;
838+
protected
839+
Slot slot;
840+
algorithm
841+
try
842+
slot := Array.getMemberOnTrue(slotName, slots, Slot.hasName);
843+
outSlot := SOME(slot);
844+
else
845+
outSlot := NONE();
846+
end try;
847+
end lookupSlotInArray;
848+
737849
function matchArgsVectorize
738850
input Function func;
739851
input output list<TypedArg> args;
@@ -1042,7 +1154,7 @@ uniontype Function
10421154
end for;
10431155

10441156
// Make the slots and return type for the function.
1045-
fn.slots := list(makeSlot(i) for i in fn.inputs);
1157+
fn.slots := makeSlots(fn.inputs);
10461158
checkParamTypes(fn);
10471159
fn.returnType := makeReturnType(fn);
10481160
end if;
@@ -1360,8 +1472,23 @@ protected
13601472
end if;
13611473
end paramDirection;
13621474

1475+
function makeSlots
1476+
input list<InstNode> inputs;
1477+
output list<Slot> slots = {};
1478+
protected
1479+
Integer index = 1;
1480+
algorithm
1481+
for i in inputs loop
1482+
slots := makeSlot(i, index) :: slots;
1483+
index := index + 1;
1484+
end for;
1485+
1486+
slots := listReverseInPlace(slots);
1487+
end makeSlots;
1488+
13631489
function makeSlot
13641490
input InstNode component;
1491+
input Integer index;
13651492
output Slot slot;
13661493
protected
13671494
Component comp;
@@ -1380,7 +1507,7 @@ protected
13801507
end if;
13811508
end if;
13821509

1383-
slot := SLOT(InstNode.name(component), SlotType.GENERIC, default, NONE());
1510+
slot := SLOT(InstNode.name(component), SlotType.GENERIC, default, NONE(), index, SlotEvalStatus.NOT_EVALUATED);
13841511
else
13851512
Error.assertion(false, getInstanceName() + " got invalid component", sourceInfo());
13861513
end try;

0 commit comments

Comments
 (0)