@@ -281,6 +281,9 @@ func (f *FunctionDecl) AddOverload(overload *OverloadDecl) error {
281281 }
282282 return fmt .Errorf ("overload redefinition in function. %s: %s has multiple definitions" , f .Name (), oID )
283283 }
284+ if overload .HasLateBinding () != o .HasLateBinding () {
285+ return fmt .Errorf ("overload with late binding cannot be added to function %s: cannot mix late and non-late bindings" , f .Name ())
286+ }
284287 }
285288 f .overloadOrdinals = append (f .overloadOrdinals , overload .ID ())
286289 f .overloads [overload .ID ()] = overload
@@ -300,6 +303,19 @@ func (f *FunctionDecl) OverloadDecls() []*OverloadDecl {
300303 return overloads
301304}
302305
306+ // Returns true if the function has late bindings. A function cannot mix late bindings with other bindings.
307+ func (f * FunctionDecl ) HasLateBinding () bool {
308+ if f == nil {
309+ return false
310+ }
311+ for _ , oID := range f .overloadOrdinals {
312+ if f .overloads [oID ].HasLateBinding () {
313+ return true
314+ }
315+ }
316+ return false
317+ }
318+
303319// Bindings produces a set of function bindings, if any are defined.
304320func (f * FunctionDecl ) Bindings () ([]* functions.Overload , error ) {
305321 var emptySet []* functions.Overload
@@ -308,8 +324,10 @@ func (f *FunctionDecl) Bindings() ([]*functions.Overload, error) {
308324 }
309325 overloads := []* functions.Overload {}
310326 nonStrict := false
327+ hasLateBinding := false
311328 for _ , oID := range f .overloadOrdinals {
312329 o := f .overloads [oID ]
330+ hasLateBinding = hasLateBinding || o .HasLateBinding ()
313331 if o .hasBinding () {
314332 overload := & functions.Overload {
315333 Operator : o .ID (),
@@ -327,6 +345,9 @@ func (f *FunctionDecl) Bindings() ([]*functions.Overload, error) {
327345 if len (overloads ) != 0 {
328346 return nil , fmt .Errorf ("singleton function incompatible with specialized overloads: %s" , f .Name ())
329347 }
348+ if hasLateBinding {
349+ return nil , fmt .Errorf ("singleton function incompatible with late bindings: %s" , f .Name ())
350+ }
330351 overloads = []* functions.Overload {
331352 {
332353 Operator : f .Name (),
@@ -576,6 +597,9 @@ type OverloadDecl struct {
576597 argTypes []* types.Type
577598 resultType * types.Type
578599 isMemberFunction bool
600+ // hasLateBinding indicates that the function has a binding which is not known at compile time.
601+ // This is useful for functions which have side-effects or are not deterministically computable.
602+ hasLateBinding bool
579603 // nonStrict indicates that the function will accept error and unknown arguments as inputs.
580604 nonStrict bool
581605 // operandTrait indicates whether the member argument should have a specific type-trait.
@@ -640,6 +664,14 @@ func (o *OverloadDecl) IsNonStrict() bool {
640664 return o .nonStrict
641665}
642666
667+ // HasLateBinding returns whether the overload has a binding which is not known at compile time.
668+ func (o * OverloadDecl ) HasLateBinding () bool {
669+ if o == nil {
670+ return false
671+ }
672+ return o .hasLateBinding
673+ }
674+
643675// OperandTrait returns the trait mask of the first operand to the overload call, e.g.
644676// `traits.Indexer`
645677func (o * OverloadDecl ) OperandTrait () int {
@@ -816,6 +848,9 @@ func UnaryBinding(binding functions.UnaryOp) OverloadOpt {
816848 if len (o .ArgTypes ()) != 1 {
817849 return nil , fmt .Errorf ("unary function bound to non-unary overload: %s" , o .ID ())
818850 }
851+ if o .hasLateBinding {
852+ return nil , fmt .Errorf ("overload already has a late binding: %s" , o .ID ())
853+ }
819854 o .unaryOp = binding
820855 return o , nil
821856 }
@@ -831,6 +866,9 @@ func BinaryBinding(binding functions.BinaryOp) OverloadOpt {
831866 if len (o .ArgTypes ()) != 2 {
832867 return nil , fmt .Errorf ("binary function bound to non-binary overload: %s" , o .ID ())
833868 }
869+ if o .hasLateBinding {
870+ return nil , fmt .Errorf ("overload already has a late binding: %s" , o .ID ())
871+ }
834872 o .binaryOp = binding
835873 return o , nil
836874 }
@@ -843,11 +881,26 @@ func FunctionBinding(binding functions.FunctionOp) OverloadOpt {
843881 if o .hasBinding () {
844882 return nil , fmt .Errorf ("overload already has a binding: %s" , o .ID ())
845883 }
884+ if o .hasLateBinding {
885+ return nil , fmt .Errorf ("overload already has a late binding: %s" , o .ID ())
886+ }
846887 o .functionOp = binding
847888 return o , nil
848889 }
849890}
850891
892+ // LateFunctionBinding indicates that the function has a binding which is not known at compile time.
893+ // This is useful for functions which have side-effects or are not deterministically computable.
894+ func LateFunctionBinding () OverloadOpt {
895+ return func (o * OverloadDecl ) (* OverloadDecl , error ) {
896+ if o .hasBinding () {
897+ return nil , fmt .Errorf ("overload already has a binding: %s" , o .ID ())
898+ }
899+ o .hasLateBinding = true
900+ return o , nil
901+ }
902+ }
903+
851904// OverloadIsNonStrict enables the function to be called with error and unknown argument values.
852905//
853906// Note: do not use this option unless absoluately necessary as it should be an uncommon feature.
0 commit comments