diff --git a/src/core/errors.go b/src/core/errors.go index 8e0b655..d6aeed2 100644 --- a/src/core/errors.go +++ b/src/core/errors.go @@ -85,10 +85,15 @@ func (err *InvalidLoopControl) Error() string { // NoMatchingFunction is created when a function is not defined for the given type. type NoMatchingFunction struct { Function string + Types string } func (err *NoMatchingFunction) Error() string { - return fmt.Sprintf("No matching function for call to '%s'", err.Function) + if err.Types == "" { + return fmt.Sprintf("No matching function for call to '%s'", err.Function) + } + + return fmt.Sprintf("No matching function for call to '%s' with types [%s]", err.Function, err.Types) } // NotDataStruct is created when accessing field of a non-struct type. diff --git a/src/core/evaluateCall.go b/src/core/evaluateCall.go index f4d0d2c..4fcc734 100644 --- a/src/core/evaluateCall.go +++ b/src/core/evaluateCall.go @@ -1,6 +1,8 @@ package core import ( + "strings" + "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/expression" "git.urbach.dev/cli/q/src/ssa" @@ -45,7 +47,26 @@ func (f *Function) evaluateCall(expr *expression.Expression) (ssa.Value, error) return nil, err } - fn := ssaFunc.FunctionRef.(*Function) + variants := ssaFunc.FunctionRef.(*Function) + fn, err := f.selectFunction(variants, values, identifier) + + if err != nil { + return nil, err + } + + if fn == nil { + typeNames := make([]string, len(values)) + + for i := range values { + typeNames[i] = values[i].Type().Name() + } + + rawName, _, _ := strings.Cut(variants.FullName, "[") + return nil, errors.New(&NoMatchingFunction{Function: rawName, Types: strings.Join(typeNames, ",")}, f.File, identifier.Source()) + } + + ssaFunc.FunctionRef = fn + ssaFunc.Typ = fn.Type for i, value := range values { given := value.Type() @@ -77,6 +98,7 @@ func (f *Function) evaluateCall(expr *expression.Expression) (ssa.Value, error) Source: expr.Source(), }}) + f.Assembler.Libraries.Append(fn.Package(), fn.Name()) return v, nil } diff --git a/src/core/evaluateMethod.go b/src/core/evaluateMethod.go index d745b05..0f39e8d 100644 --- a/src/core/evaluateMethod.go +++ b/src/core/evaluateMethod.go @@ -17,12 +17,9 @@ func (f *Function) evaluateMethod(leftValue ssa.Value, left *expression.Expressi } call := expr.Parent - - if len(call.Children) < 2 || call.Children[1] != left { - call.Children = append(call.Children, nil) - copy(call.Children[2:], call.Children[1:]) - call.Children[1] = left - } + call.Children = append(call.Children, nil) + copy(call.Children[2:], call.Children[1:]) + call.Children[1] = left pkg := f.File.Package structure, isStructPointer := leftUnwrapped.(*types.Struct) diff --git a/src/core/evaluatePackageMember.go b/src/core/evaluatePackageMember.go index e8d946f..a55c42d 100644 --- a/src/core/evaluatePackageMember.go +++ b/src/core/evaluatePackageMember.go @@ -6,7 +6,6 @@ import ( "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/expression" "git.urbach.dev/cli/q/src/ssa" - "git.urbach.dev/cli/q/src/token" ) // evaluatePackageMember converts a pkg.something expression to an SSA value. @@ -14,38 +13,12 @@ func (f *Function) evaluatePackageMember(pkg *Package, rightText string, expr *e variants, exists := pkg.Functions[rightText] if exists { - if expr.Parent.Token.Kind == token.Call && expr.Parent.Children[0] == expr { - inputExpressions := expr.Parent.Children[1:] - fn, err := f.selectFunction(variants, inputExpressions, expr) - - if err != nil { - return nil, err - } - - if fn == nil { - return nil, errors.New(&NoMatchingFunction{Function: pkg.Name + "." + rightText}, f.File, expr.Source()) - } - - if fn.IsExtern() { - f.Assembler.Libraries.Append(fn.Package(), fn.Name()) - } - - v := &ssa.Function{ - FunctionRef: fn, - Typ: fn.Type, - Source: expr.Source(), - } - - return v, nil - } - - v := f.Append(&ssa.Function{ + v := &ssa.Function{ FunctionRef: variants, Typ: variants.Type, Source: expr.Source(), - }) + } - f.Calls.Add(variants) return v, nil } diff --git a/src/core/selectFunction.go b/src/core/selectFunction.go index f540bf4..404f4bc 100644 --- a/src/core/selectFunction.go +++ b/src/core/selectFunction.go @@ -3,37 +3,35 @@ package core import ( "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/expression" + "git.urbach.dev/cli/q/src/ssa" + "git.urbach.dev/cli/q/src/types" ) // selectFunction selects the correct variant of a function based on the input types. -func (f *Function) selectFunction(fn *Function, inputExpressions []*expression.Expression, identifier *expression.Expression) (*Function, error) { +func (f *Function) selectFunction(fn *Function, values []ssa.Value, identifier *expression.Expression) (*Function, error) { if fn.Next == nil { - if len(inputExpressions) != len(fn.Input) { - return nil, errors.NewAt(&ParameterCountMismatch{Function: fn.FullName, Count: len(inputExpressions), ExpectedCount: len(fn.Input)}, f.File, identifier.Source().End()) + if len(values) != len(fn.Input) { + return nil, errors.NewAt(&ParameterCountMismatch{Function: fn.FullName, Count: len(values), ExpectedCount: len(fn.Input)}, f.File, identifier.Source().End()) } return fn, nil } +match: for fn != nil { - if len(inputExpressions) != len(fn.Input) { + if len(values) != len(fn.Input) { fn = fn.Next - continue + continue match } - reset := len(f.Block().Instructions) - matches, err := f.matchesType(inputExpressions, fn.Input) - f.Block().Instructions = f.Block().Instructions[:reset] - - if err != nil { - return nil, err - } - - if matches { - return fn, nil + for i, value := range values { + if !types.Is(value.Type(), fn.Input[i].Typ) { + fn = fn.Next + continue match + } } - fn = fn.Next + return fn, nil } return nil, nil