@@ -232,6 +232,9 @@ func (f *FunctionDecl) AddOverload(overload *OverloadDecl) error {
232232 }
233233 return fmt .Errorf ("overload redefinition in function. %s: %s has multiple definitions" , f .Name (), oID )
234234 }
235+ if overload .HasLateBinding () != o .HasLateBinding () {
236+ return fmt .Errorf ("overload with late binding cannot be added to function %s: cannot mix late and non-late bindings" , f .Name ())
237+ }
235238 }
236239 f .overloadOrdinals = append (f .overloadOrdinals , overload .ID ())
237240 f .overloads [overload .ID ()] = overload
@@ -250,15 +253,30 @@ func (f *FunctionDecl) OverloadDecls() []*OverloadDecl {
250253 return overloads
251254}
252255
256+ // Returns true if the function has late bindings. A function cannot mix late bindings with other bindings.
257+ func (f * FunctionDecl ) HasLateBinding () bool {
258+ if f == nil {
259+ return false
260+ }
261+ for _ , oID := range f .overloadOrdinals {
262+ if f .overloads [oID ].HasLateBinding () {
263+ return true
264+ }
265+ }
266+ return false
267+ }
268+
253269// Bindings produces a set of function bindings, if any are defined.
254270func (f * FunctionDecl ) Bindings () ([]* functions.Overload , error ) {
255271 if f == nil {
256272 return []* functions.Overload {}, nil
257273 }
258274 overloads := []* functions.Overload {}
259275 nonStrict := false
276+ hasLateBinding := false
260277 for _ , oID := range f .overloadOrdinals {
261278 o := f .overloads [oID ]
279+ hasLateBinding = hasLateBinding || o .HasLateBinding ()
262280 if o .hasBinding () {
263281 overload := & functions.Overload {
264282 Operator : o .ID (),
@@ -276,6 +294,9 @@ func (f *FunctionDecl) Bindings() ([]*functions.Overload, error) {
276294 if len (overloads ) != 0 {
277295 return nil , fmt .Errorf ("singleton function incompatible with specialized overloads: %s" , f .Name ())
278296 }
297+ if hasLateBinding {
298+ return nil , fmt .Errorf ("singleton function incompatible with late bindings: %s" , f .Name ())
299+ }
279300 overloads = []* functions.Overload {
280301 {
281302 Operator : f .Name (),
@@ -516,6 +537,9 @@ type OverloadDecl struct {
516537 argTypes []* types.Type
517538 resultType * types.Type
518539 isMemberFunction bool
540+ // hasLateBinding indicates that the function has a binding which is not known at compile time.
541+ // This is useful for functions which have side-effects or are not deterministically computable.
542+ hasLateBinding bool
519543 // nonStrict indicates that the function will accept error and unknown arguments as inputs.
520544 nonStrict bool
521545 // operandTrait indicates whether the member argument should have a specific type-trait.
@@ -571,6 +595,14 @@ func (o *OverloadDecl) IsNonStrict() bool {
571595 return o .nonStrict
572596}
573597
598+ // HasLateBinding returns whether the overload has a binding which is not known at compile time.
599+ func (o * OverloadDecl ) HasLateBinding () bool {
600+ if o == nil {
601+ return false
602+ }
603+ return o .hasLateBinding
604+ }
605+
574606// OperandTrait returns the trait mask of the first operand to the overload call, e.g.
575607// `traits.Indexer`
576608func (o * OverloadDecl ) OperandTrait () int {
@@ -739,6 +771,9 @@ func UnaryBinding(binding functions.UnaryOp) OverloadOpt {
739771 if len (o .ArgTypes ()) != 1 {
740772 return nil , fmt .Errorf ("unary function bound to non-unary overload: %s" , o .ID ())
741773 }
774+ if o .hasLateBinding {
775+ return nil , fmt .Errorf ("overload already has a late binding: %s" , o .ID ())
776+ }
742777 o .unaryOp = binding
743778 return o , nil
744779 }
@@ -754,6 +789,9 @@ func BinaryBinding(binding functions.BinaryOp) OverloadOpt {
754789 if len (o .ArgTypes ()) != 2 {
755790 return nil , fmt .Errorf ("binary function bound to non-binary overload: %s" , o .ID ())
756791 }
792+ if o .hasLateBinding {
793+ return nil , fmt .Errorf ("overload already has a late binding: %s" , o .ID ())
794+ }
757795 o .binaryOp = binding
758796 return o , nil
759797 }
@@ -766,11 +804,26 @@ func FunctionBinding(binding functions.FunctionOp) OverloadOpt {
766804 if o .hasBinding () {
767805 return nil , fmt .Errorf ("overload already has a binding: %s" , o .ID ())
768806 }
807+ if o .hasLateBinding {
808+ return nil , fmt .Errorf ("overload already has a late binding: %s" , o .ID ())
809+ }
769810 o .functionOp = binding
770811 return o , nil
771812 }
772813}
773814
815+ // LateFunctionBinding indicates that the function has a binding which is not known at compile time.
816+ // This is useful for functions which have side-effects or are not deterministically computable.
817+ func LateFunctionBinding () OverloadOpt {
818+ return func (o * OverloadDecl ) (* OverloadDecl , error ) {
819+ if o .hasBinding () {
820+ return nil , fmt .Errorf ("overload already has a binding: %s" , o .ID ())
821+ }
822+ o .hasLateBinding = true
823+ return o , nil
824+ }
825+ }
826+
774827// OverloadIsNonStrict enables the function to be called with error and unknown argument values.
775828//
776829// Note: do not use this option unless absoluately necessary as it should be an uncommon feature.
0 commit comments