Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for max parameter count (internal #128) #2656

Merged
merged 4 commits into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions runtime/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2361,7 +2361,7 @@ func TestGetAuthAccount(t *testing.T) {

errs := checker.RequireCheckerErrors(t, err, 1)

assert.IsType(t, &sema.ArgumentCountError{}, errs[0])
assert.IsType(t, &sema.InsufficientArgumentsError{}, errs[0])
})

t.Run("too many args", func(t *testing.T) {
Expand All @@ -2388,7 +2388,7 @@ func TestGetAuthAccount(t *testing.T) {
)
errs := checker.RequireCheckerErrors(t, err, 1)

assert.IsType(t, &sema.ArgumentCountError{}, errs[0])
assert.IsType(t, &sema.ExcessiveArgumentsError{}, errs[0])
})

t.Run("transaction", func(t *testing.T) {
Expand Down
14 changes: 8 additions & 6 deletions runtime/interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,12 +423,15 @@ func (interpreter *Interpreter) InvokeExternally(

if argumentCount != parameterCount {

// if the function has defined optional parameters,
// then the provided arguments must be equal to or greater than
// the number of required parameters.
if functionType.RequiredArgumentCount == nil ||
argumentCount < *functionType.RequiredArgumentCount {
if argumentCount < functionType.Arity.MinCount(parameterCount) {
return nil, ArgumentCountError{
ParameterCount: parameterCount,
ArgumentCount: argumentCount,
}
}

maxCount := functionType.Arity.MaxCount(parameterCount)
if maxCount != nil && argumentCount > *maxCount {
return nil, ArgumentCountError{
ParameterCount: parameterCount,
ArgumentCount: argumentCount,
Expand Down Expand Up @@ -1065,7 +1068,6 @@ func (interpreter *Interpreter) declareNonEnumCompositeValue(
ReturnTypeAnnotation: sema.TypeAnnotation{
Type: compositeType,
},
RequiredArgumentCount: nil,
}

var initializerFunction FunctionValue
Expand Down
2 changes: 1 addition & 1 deletion runtime/sema/authaccount.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ package sema
var AuthAccountTypeLinkAccountFunctionTypePathParameterTypeAnnotation = AuthAccountTypeLinkAccountFunctionType.Parameters[0].TypeAnnotation

func init() {
AuthAccountContractsTypeAddFunctionType.RequiredArgumentCount = RequiredArgumentCount(2)
AuthAccountContractsTypeAddFunctionType.Arity = &Arity{Min: 2}
AuthAccountTypeGetCapabilityFunctionTypeParameterT.Optional = true
PublicAccountTypeGetCapabilityFunctionTypeParameterT.Optional = true
}
31 changes: 18 additions & 13 deletions runtime/sema/check_invocation_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ func (checker *Checker) checkInvocation(
returnType Type,
) {
parameterCount := len(functionType.Parameters)
requiredArgumentCount := functionType.RequiredArgumentCount
arity := functionType.Arity
typeParameterCount := len(functionType.TypeParameters)

// Check the type arguments and bind them to type parameters
Expand Down Expand Up @@ -421,7 +421,7 @@ func (checker *Checker) checkInvocation(
checker.checkInvocationArgumentCount(
argumentCount,
parameterCount,
requiredArgumentCount,
arity,
invocationExpression,
)

Expand Down Expand Up @@ -592,23 +592,28 @@ func (checker *Checker) checkInvocationRequiredArgument(
func (checker *Checker) checkInvocationArgumentCount(
argumentCount int,
parameterCount int,
requiredArgumentCount *int,
arity *Arity,
pos ast.HasPosition,
) {

if argumentCount == parameterCount {
minCount := arity.MinCount(parameterCount)
if argumentCount < minCount {
checker.report(
&InsufficientArgumentsError{
MinCount: minCount,
ActualCount: argumentCount,
Range: ast.NewRangeFromPositioned(checker.memoryGauge, pos),
},
)
return
}

// TODO: improve
if requiredArgumentCount == nil ||
argumentCount < *requiredArgumentCount {

maxCount := arity.MaxCount(parameterCount)
if maxCount != nil && argumentCount > *maxCount {
checker.report(
&ArgumentCountError{
ParameterCount: parameterCount,
ArgumentCount: argumentCount,
Range: ast.NewRangeFromPositioned(checker.memoryGauge, pos),
&ExcessiveArgumentsError{
MaxCount: *maxCount,
ActualCount: argumentCount,
Range: ast.NewRangeFromPositioned(checker.memoryGauge, pos),
},
)
}
Expand Down
58 changes: 43 additions & 15 deletions runtime/sema/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,31 +411,59 @@ func (e *NotCallableError) Error() string {
)
}

// ArgumentCountError
// InsufficientArgumentsError

type ArgumentCountError struct {
ParameterCount int
ArgumentCount int
type InsufficientArgumentsError struct {
MinCount int
ActualCount int
ast.Range
}

var _ SemanticError = &ArgumentCountError{}
var _ errors.UserError = &ArgumentCountError{}
var _ errors.SecondaryError = &ArgumentCountError{}
var _ SemanticError = &InsufficientArgumentsError{}
var _ errors.UserError = &InsufficientArgumentsError{}
var _ errors.SecondaryError = &InsufficientArgumentsError{}

func (*ArgumentCountError) isSemanticError() {}
func (*InsufficientArgumentsError) isSemanticError() {}

func (*ArgumentCountError) IsUserError() {}
func (*InsufficientArgumentsError) IsUserError() {}

func (e *ArgumentCountError) Error() string {
return "incorrect number of arguments"
func (e *InsufficientArgumentsError) Error() string {
return "too few arguments"
}

func (e *ArgumentCountError) SecondaryError() string {
func (e *InsufficientArgumentsError) SecondaryError() string {
return fmt.Sprintf(
"expected %d, got %d",
e.ParameterCount,
e.ArgumentCount,
"expected at least %d, got %d",
e.MinCount,
e.ActualCount,
)
}

// ExcessiveArgumentsError

type ExcessiveArgumentsError struct {
MaxCount int
ActualCount int
ast.Range
}

var _ SemanticError = &ExcessiveArgumentsError{}
var _ errors.UserError = &ExcessiveArgumentsError{}
var _ errors.SecondaryError = &ExcessiveArgumentsError{}

func (*ExcessiveArgumentsError) isSemanticError() {}

func (*ExcessiveArgumentsError) IsUserError() {}

func (e *ExcessiveArgumentsError) Error() string {
return "too many arguments"
}

func (e *ExcessiveArgumentsError) SecondaryError() string {
return fmt.Sprintf(
"expected up to %d, got %d",
e.MaxCount,
e.ActualCount,
)
}

Expand Down
48 changes: 36 additions & 12 deletions runtime/sema/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -2655,10 +2655,38 @@ func formatFunctionType(
return builder.String()
}

// Arity

type Arity struct {
Min int
Max int
}

func (arity *Arity) MinCount(parameterCount int) int {
minCount := parameterCount
if arity != nil {
minCount = arity.Min
}

return minCount
}

func (arity *Arity) MaxCount(parameterCount int) *int {
maxCount := parameterCount
if arity != nil {
if arity.Max < parameterCount {
return nil
}
maxCount = arity.Max
}

return &maxCount
}

// FunctionType
type FunctionType struct {
ReturnTypeAnnotation TypeAnnotation
RequiredArgumentCount *int
Arity *Arity
ArgumentExpressionsCheck ArgumentExpressionsCheck
Members *StringMemberOrderedMap
TypeParameters []*TypeParameter
Expand All @@ -2670,10 +2698,6 @@ type FunctionType struct {

var _ Type = &FunctionType{}

func RequiredArgumentCount(count int) *int {
return &count
}

func (*FunctionType) IsType() {}

func (t *FunctionType) Tag() TypeTag {
Expand Down Expand Up @@ -2994,10 +3018,10 @@ func (t *FunctionType) RewriteWithRestrictedTypes() (Type, bool) {
}

return &FunctionType{
TypeParameters: rewrittenTypeParameters,
Parameters: rewrittenParameters,
ReturnTypeAnnotation: NewTypeAnnotation(rewrittenReturnType),
RequiredArgumentCount: t.RequiredArgumentCount,
TypeParameters: rewrittenTypeParameters,
Parameters: rewrittenParameters,
ReturnTypeAnnotation: NewTypeAnnotation(rewrittenReturnType),
Arity: t.Arity,
}, true
} else {
return t, false
Expand Down Expand Up @@ -3106,9 +3130,9 @@ func (t *FunctionType) Resolve(typeArguments *TypeParameterTypeOrderedMap) Type
}

return &FunctionType{
Parameters: newParameters,
ReturnTypeAnnotation: NewTypeAnnotation(newReturnType),
RequiredArgumentCount: t.RequiredArgumentCount,
Parameters: newParameters,
ReturnTypeAnnotation: NewTypeAnnotation(newReturnType),
Arity: t.Arity,
}

}
Expand Down
3 changes: 2 additions & 1 deletion runtime/stdlib/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ var assertFunctionType = &sema.FunctionType{
ReturnTypeAnnotation: sema.NewTypeAnnotation(
sema.VoidType,
),
RequiredArgumentCount: sema.RequiredArgumentCount(1),
// `message` parameter is optional
Arity: &sema.Arity{Min: 1, Max: 2},
}

var AssertFunction = NewStandardLibraryFunction(
Expand Down
Loading