Skip to content

Commit

Permalink
gate new behaviour (errors for all invalid type cases) behind a featu…
Browse files Browse the repository at this point in the history
…re flag
  • Loading branch information
turbolent committed Nov 21, 2024
1 parent d1d5626 commit b4f27e1
Show file tree
Hide file tree
Showing 16 changed files with 750 additions and 340 deletions.
9 changes: 7 additions & 2 deletions runtime/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"time"

"go.opentelemetry.io/otel/attribute"
"golang.org/x/mod/semver"

"github.com/onflow/cadence"
"github.com/onflow/cadence/runtime/activations"
Expand Down Expand Up @@ -1461,6 +1462,9 @@ func (e *interpreterEnvironment) newValidateAccountCapabilitiesPublishHandler()
}
}

// TODO:
const MinimumRequiredVersionForInvalidTypeErrorFixes = "v1.0.3"

func (e *interpreterEnvironment) configureVersionedFeatures() {
var (
minimumRequiredVersion string
Expand All @@ -1473,6 +1477,7 @@ func (e *interpreterEnvironment) configureVersionedFeatures() {
panic(err)
}

// No feature flags yet
_ = minimumRequiredVersion
e.CheckerConfig.InvalidTypeErrorFixesEnabled =
minimumRequiredVersion != "" &&
semver.Compare(MinimumRequiredVersionForInvalidTypeErrorFixes, minimumRequiredVersion) >= 0
}
85 changes: 55 additions & 30 deletions runtime/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11553,41 +11553,66 @@ func TestRuntimeInvocationReturnTypeInferenceFailure(t *testing.T) {

t.Parallel()

address := common.MustBytesToAddress([]byte{0x1})
test := func(invalidTypeErrorFixesEnabled bool) {

newRuntimeInterface := func() Interface {
name := fmt.Sprintf(
"error fixes enabled: %v",
invalidTypeErrorFixesEnabled,
)

return &TestRuntimeInterface{
Storage: NewTestLedger(nil, nil),
OnGetSigningAccounts: func() ([]common.Address, error) {
return []common.Address{address}, nil
},
}
}
t.Run(name, func(t *testing.T) {
t.Parallel()

runtime := NewTestInterpreterRuntime()
address := common.MustBytesToAddress([]byte{0x1})

nextTransactionLocation := NewTransactionLocationGenerator()
runtimeInterface := &TestRuntimeInterface{
Storage: NewTestLedger(nil, nil),
OnGetSigningAccounts: func() ([]common.Address, error) {
return []common.Address{address}, nil
},
OnMinimumRequiredVersion: func() (string, error) {
if invalidTypeErrorFixesEnabled {
return MinimumRequiredVersionForInvalidTypeErrorFixes, nil
}
return "", nil
},
}

tx := []byte(`
transaction{
prepare(signer: auth(Storage) &Account){
let functions = [signer.storage.save].reverse()
}
}
`)
runtime := NewTestInterpreterRuntime()

err := runtime.ExecuteTransaction(
Script{
Source: tx,
},
Context{
Interface: newRuntimeInterface(),
Location: nextTransactionLocation(),
},
)
RequireError(t, err)
nextTransactionLocation := NewTransactionLocationGenerator()

var typeErr *sema.InvocationTypeInferenceError
require.ErrorAs(t, err, &typeErr)
tx := []byte(`
transaction{
prepare(signer: auth(Storage) &Account){
let functions = [signer.storage.save].reverse()
}
}
`)

err := runtime.ExecuteTransaction(
Script{
Source: tx,
},
Context{
Interface: runtimeInterface,
Location: nextTransactionLocation(),
},
)

RequireError(t, err)

if invalidTypeErrorFixesEnabled {
var typeErr *sema.InvocationTypeInferenceError
require.ErrorAs(t, err, &typeErr)
} else {
var transferErr interpreter.ValueTransferTypeError
require.ErrorAs(t, err, &transferErr)
}
})
}

for _, invalidTypeErrorFixesEnabled := range []bool{true, false} {
test(invalidTypeErrorFixesEnabled)
}
}
42 changes: 24 additions & 18 deletions runtime/sema/check_invocation_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,12 +503,14 @@ func (checker *Checker) checkInvocation(

returnType = functionType.ReturnTypeAnnotation.Type.Resolve(typeArguments)
if returnType == nil {
checker.report(&InvocationTypeInferenceError{
Range: ast.NewRangeFromPositioned(
checker.memoryGauge,
invocationExpression,
),
})
if checker.Config.InvalidTypeErrorFixesEnabled {
checker.report(&InvocationTypeInferenceError{
Range: ast.NewRangeFromPositioned(
checker.memoryGauge,
invocationExpression,
),
})
}

returnType = InvalidType
}
Expand Down Expand Up @@ -605,12 +607,14 @@ func (checker *Checker) checkInvocationRequiredArgument(
parameterType = parameterType.Resolve(typeParameters)
// If the type parameter could not be resolved, use the invalid type.
if parameterType == nil {
checker.report(&InvocationTypeInferenceError{
Range: ast.NewRangeFromPositioned(
checker.memoryGauge,
argument.Expression,
),
})
if checker.Config.InvalidTypeErrorFixesEnabled {
checker.report(&InvocationTypeInferenceError{
Range: ast.NewRangeFromPositioned(
checker.memoryGauge,
argument.Expression,
),
})
}
parameterType = InvalidType
}
}
Expand Down Expand Up @@ -686,12 +690,14 @@ func (checker *Checker) checkInvocationRequiredArgument(
parameterType = parameterType.Resolve(typeParameters)
// If the type parameter could not be resolved, use the invalid type.
if parameterType == nil {
checker.report(&InvocationTypeInferenceError{
Range: ast.NewRangeFromPositioned(
checker.memoryGauge,
argument.Expression,
),
})
if checker.Config.InvalidTypeErrorFixesEnabled {
checker.report(&InvocationTypeInferenceError{
Range: ast.NewRangeFromPositioned(
checker.memoryGauge,
argument.Expression,
),
})
}
parameterType = InvalidType
}
}
Expand Down
16 changes: 11 additions & 5 deletions runtime/sema/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -886,12 +886,16 @@ func (checker *Checker) ConvertType(t ast.Type) Type {
case *ast.InstantiationType:
return checker.convertInstantiationType(t)

default:
checker.report(&UnconvertableTypeError{
Range: ast.NewRangeFromPositioned(checker.memoryGauge, t),
})
case nil:
if checker.Config.InvalidTypeErrorFixesEnabled {
checker.report(&UnconvertableTypeError{
Range: ast.NewRangeFromPositioned(checker.memoryGauge, t),
})
}
return InvalidType
}

panic(&astTypeConversionError{invalidASTType: t})
}

func CheckIntersectionType(
Expand Down Expand Up @@ -2611,7 +2615,9 @@ func (checker *Checker) visitExpressionWithForceType(

actualType = ast.AcceptExpression[Type](expr, checker)

checker.checkErrorsForInvalidExpressionTypes(actualType, expectedType)
if checker.Config.InvalidTypeErrorFixesEnabled {
checker.checkErrorsForInvalidExpressionTypes(actualType, expectedType)
}

if checker.Config.ExtendedElaborationEnabled {
checker.Elaboration.SetExpressionTypes(
Expand Down
2 changes: 2 additions & 0 deletions runtime/sema/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,6 @@ type Config struct {
AllowStaticDeclarations bool
// AttachmentsEnabled determines if attachments are enabled
AttachmentsEnabled bool
// InvalidTypeErrorFixesEnabled determines if errors for invalid type errors are reported
InvalidTypeErrorFixesEnabled bool
}
10 changes: 10 additions & 0 deletions runtime/sema/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ func ErrorMessageExpectedActualTypes(
return
}

// astTypeConversionError

type astTypeConversionError struct {
invalidASTType ast.Type
}

func (e *astTypeConversionError) Error() string {
return fmt.Sprintf("cannot convert unsupported AST type: %#+v", e.invalidASTType)
}

// unsupportedOperation

type unsupportedOperation struct {
Expand Down
Loading

0 comments on commit b4f27e1

Please sign in to comment.