From 179666681bcfe6377ca383609d7974afba998406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 26 Jun 2023 15:59:49 -0700 Subject: [PATCH 01/10] fix checking of reference expressions involving optionals --- runtime/runtime_test.go | 96 ++++++++++++++++++++++ runtime/sema/check_reference_expression.go | 56 +++++++++---- runtime/tests/checker/reference_test.go | 28 ++++--- 3 files changed, 152 insertions(+), 28 deletions(-) diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index 35d9fe1f71..ae88f56491 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -8638,3 +8638,99 @@ func TestInvalidatedResourceUse2(t *testing.T) { var destroyedResourceErr interpreter.DestroyedResourceError require.ErrorAs(t, err, &destroyedResourceErr) } + +func TestRuntimeOptionalReferenceAttack(t *testing.T) { + + t.Parallel() + + script := ` + pub resource Vault { + pub var balance: UFix64 + + init(balance: UFix64) { + self.balance = balance + } + + pub fun withdraw(amount: UFix64): @Vault { + self.balance = self.balance - amount + return <-create Vault(balance: amount) + } + + pub fun deposit(from: @Vault) { + self.balance = self.balance + from.balance + destroy from + } + } + + pub fun empty(): @Vault { + return <- create Vault(balance: 0.0) + } + + pub fun giveme(): @Vault { + return <- create Vault(balance: 10.0) + } + + pub fun main() { + var vault <- giveme() //get 10 token + var someDict:@{Int:Vault} <- {1:<-vault} + var r = (&someDict[1] as auth &AnyResource) as! &Vault + var double <- empty() + double.deposit(from: <- someDict.remove(key:1)!) + double.deposit(from: <- r.withdraw(amount:10.0)) + log(double.balance) // 20 + destroy double + destroy someDict + } + ` + + runtime := newTestInterpreterRuntime() + + accountCodes := map[common.Location][]byte{} + + var events []cadence.Event + + signerAccount := common.MustBytesToAddress([]byte{0x1}) + + storage := newTestLedger(nil, nil) + + runtimeInterface := &testRuntimeInterface{ + getCode: func(location Location) (bytes []byte, err error) { + return accountCodes[location], nil + }, + storage: storage, + getSigningAccounts: func() ([]Address, error) { + return []Address{signerAccount}, nil + }, + resolveLocation: singleIdentifierLocationResolver(t), + getAccountContractCode: func(location common.AddressLocation) (code []byte, err error) { + return accountCodes[location], nil + }, + updateAccountContractCode: func(location common.AddressLocation, code []byte) error { + accountCodes[location] = code + return nil + }, + emitEvent: func(event cadence.Event) error { + events = append(events, event) + return nil + }, + log: func(s string) { + + }, + } + runtimeInterface.decodeArgument = func(b []byte, t cadence.Type) (value cadence.Value, err error) { + return json.Decode(nil, b) + } + + _, err := runtime.ExecuteScript( + Script{ + Source: []byte(script), + Arguments: [][]byte{}, + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + }, + ) + + require.NoError(t, err) +} diff --git a/runtime/sema/check_reference_expression.go b/runtime/sema/check_reference_expression.go index 36c6e1e4bc..a7403ba1f4 100644 --- a/runtime/sema/check_reference_expression.go +++ b/runtime/sema/check_reference_expression.go @@ -28,40 +28,47 @@ func (checker *Checker) VisitReferenceExpression(referenceExpression *ast.Refere // Check the result type and ensure it is a reference type - resultType := checker.ConvertType(referenceExpression.Type) - checker.checkInvalidInterfaceAsType(resultType, referenceExpression.Type) + rightType := checker.ConvertType(referenceExpression.Type) + checker.checkInvalidInterfaceAsType(rightType, referenceExpression.Type) + var isOpt bool var referenceType *ReferenceType - var targetType, returnType Type + var expectedLeftType Type + var returnType Type + + if !rightType.IsInvalidType() { - if !resultType.IsInvalidType() { - var ok bool // Reference expressions may reference a value which has an optional type. // For example, the result of indexing into a dictionary is an optional: // // let ints: {Int: String} = {0: "zero"} // let ref: &T? = &ints[0] as &T? // read as (&T)? // + // In this case the reference expression's borrow type must be an optional type. + // // In this case the reference expression's type is an optional type. // Unwrap it one level to get the actual reference type - optType, optOk := resultType.(*OptionalType) - if optOk { - resultType = optType.Type + + var optType *OptionalType + optType, isOpt = rightType.(*OptionalType) + if isOpt { + rightType = optType.Type } - referenceType, ok = resultType.(*ReferenceType) - if !ok { + var isRef bool + referenceType, isRef = rightType.(*ReferenceType) + if !isRef { checker.report( &NonReferenceTypeReferenceError{ - ActualType: resultType, + ActualType: rightType, Range: ast.NewRangeFromPositioned(checker.memoryGauge, referenceExpression.Type), }, ) } else { - targetType = referenceType.Type + expectedLeftType = referenceType.Type returnType = referenceType - if optOk { - targetType = &OptionalType{Type: targetType} + if isOpt { + expectedLeftType = &OptionalType{Type: expectedLeftType} returnType = &OptionalType{Type: returnType} } } @@ -71,7 +78,24 @@ func (checker *Checker) VisitReferenceExpression(referenceExpression *ast.Refere referencedExpression := referenceExpression.Expression - referencedType := checker.VisitExpression(referencedExpression, targetType) + referencedType, actualType := checker.visitExpression(referencedExpression, expectedLeftType) + + // If the reference type was an optional type, + // we proposed an optional type to the referenced expression. + // + // Check that it actually has an optional type + + // If the reference type was a non-optional type, + // check that the referenced expression does not have an optional type + + if _, ok := actualType.(*OptionalType); (ok && !isOpt) || (!ok && isOpt) { + checker.report(&TypeMismatchError{ + ExpectedType: expectedLeftType, + ActualType: actualType, + Expression: referencedExpression, + Range: checker.expressionRange(referenceExpression), + }) + } if referenceType == nil { return InvalidType @@ -79,7 +103,7 @@ func (checker *Checker) VisitReferenceExpression(referenceExpression *ast.Refere checker.checkUnusedExpressionResourceLoss(referencedType, referencedExpression) - checker.Elaboration.SetReferenceExpressionBorrowType(referenceExpression, returnType) + checker.Elaboration.SetReferenceExpressionBorrowType(referenceExpression, rightType) return returnType } diff --git a/runtime/tests/checker/reference_test.go b/runtime/tests/checker/reference_test.go index 34a5555215..4b4fb18632 100644 --- a/runtime/tests/checker/reference_test.go +++ b/runtime/tests/checker/reference_test.go @@ -1069,26 +1069,30 @@ func TestCheckReferenceExpressionOfOptional(t *testing.T) { assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) }) - t.Run("upcast to optional", func(t *testing.T) { + t.Run("optional reference to non-optional value", func(t *testing.T) { t.Parallel() - checker, err := ParseAndCheck(t, ` + _, err := ParseAndCheck(t, ` let i: Int = 1 let ref = &i as &Int? `) - require.NoError(t, err) - refValueType := RequireGlobalValue(t, checker.Elaboration, "ref") + errs := RequireCheckerErrors(t, err, 1) + assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) + }) - assert.Equal(t, - &sema.OptionalType{ - Type: &sema.ReferenceType{ - Type: sema.IntType, - }, - }, - refValueType, - ) + t.Run("non-optional reference to optional value", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + let opt: Int? = 1 + let ref = &opt as &AnyStruct + `) + + errs := RequireCheckerErrors(t, err, 1) + assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) }) } From 92c0385a832d24b5781f08355231a35c8ae0f859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 26 Jun 2023 16:07:27 -0700 Subject: [PATCH 02/10] do not repeatedly report type mismatch errors --- runtime/sema/check_reference_expression.go | 35 ++++++++++++---------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/runtime/sema/check_reference_expression.go b/runtime/sema/check_reference_expression.go index a7403ba1f4..36b41f63a8 100644 --- a/runtime/sema/check_reference_expression.go +++ b/runtime/sema/check_reference_expression.go @@ -78,23 +78,28 @@ func (checker *Checker) VisitReferenceExpression(referenceExpression *ast.Refere referencedExpression := referenceExpression.Expression + beforeErrors := len(checker.errors) + referencedType, actualType := checker.visitExpression(referencedExpression, expectedLeftType) - // If the reference type was an optional type, - // we proposed an optional type to the referenced expression. - // - // Check that it actually has an optional type - - // If the reference type was a non-optional type, - // check that the referenced expression does not have an optional type - - if _, ok := actualType.(*OptionalType); (ok && !isOpt) || (!ok && isOpt) { - checker.report(&TypeMismatchError{ - ExpectedType: expectedLeftType, - ActualType: actualType, - Expression: referencedExpression, - Range: checker.expressionRange(referenceExpression), - }) + hasErrors := len(checker.errors) > beforeErrors + if !hasErrors { + // If the reference type was an optional type, + // we proposed an optional type to the referenced expression. + // + // Check that it actually has an optional type + + // If the reference type was a non-optional type, + // check that the referenced expression does not have an optional type + + if _, ok := actualType.(*OptionalType); (ok && !isOpt) || (!ok && isOpt) { + checker.report(&TypeMismatchError{ + ExpectedType: expectedLeftType, + ActualType: actualType, + Expression: referencedExpression, + Range: checker.expressionRange(referenceExpression), + }) + } } if referenceType == nil { From 48fc2fff26b31a90cb3ea2b1f4c0659d5f305a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 26 Jun 2023 16:13:10 -0700 Subject: [PATCH 03/10] test should fail --- runtime/runtime_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/runtime/runtime_test.go b/runtime/runtime_test.go index ae88f56491..5259c29cef 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -8732,5 +8732,12 @@ func TestRuntimeOptionalReferenceAttack(t *testing.T) { }, ) - require.NoError(t, err) + RequireError(t, err) + + var checkerErr *sema.CheckerError + require.ErrorAs(t, err, &checkerErr) + + errs := checker.RequireCheckerErrors(t, checkerErr, 1) + + assert.IsType(t, &sema.TypeMismatchError{}, errs[0]) } From 720fd0291d7f4fc0d8c2e501954bf85a66aef1bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 26 Jun 2023 16:23:07 -0700 Subject: [PATCH 04/10] fix reference expression borrow type --- runtime/sema/check_reference_expression.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/sema/check_reference_expression.go b/runtime/sema/check_reference_expression.go index 36b41f63a8..d96f56a905 100644 --- a/runtime/sema/check_reference_expression.go +++ b/runtime/sema/check_reference_expression.go @@ -108,7 +108,7 @@ func (checker *Checker) VisitReferenceExpression(referenceExpression *ast.Refere checker.checkUnusedExpressionResourceLoss(referencedType, referencedExpression) - checker.Elaboration.SetReferenceExpressionBorrowType(referenceExpression, rightType) + checker.Elaboration.SetReferenceExpressionBorrowType(referenceExpression, returnType) return returnType } From 3393a10a10a21b017945950e5daa686ecdb8a284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 26 Jun 2023 16:23:17 -0700 Subject: [PATCH 05/10] adjust tests --- runtime/tests/interpreter/interpreter_test.go | 48 +++++++++++-------- .../tests/interpreter/memory_metering_test.go | 2 +- runtime/tests/interpreter/reference_test.go | 17 ------- 3 files changed, 29 insertions(+), 38 deletions(-) diff --git a/runtime/tests/interpreter/interpreter_test.go b/runtime/tests/interpreter/interpreter_test.go index 5e56e230bd..54d5d8bc80 100644 --- a/runtime/tests/interpreter/interpreter_test.go +++ b/runtime/tests/interpreter/interpreter_test.go @@ -8682,7 +8682,7 @@ func TestInterpretNonStorageReference(t *testing.T) { <-create NFT(id: 2) ] - let nftRef = (&resources[1] as &NFT?)! + let nftRef = &resources[1] as &NFT let nftRef2 = nftRef nftRef2.id = 3 @@ -10399,38 +10399,46 @@ func TestInterpretOptionalReference(t *testing.T) { t.Parallel() - inter := parseCheckAndInterpret(t, - ` + t.Run("present", func(t *testing.T) { + + inter := parseCheckAndInterpret(t, ` fun present(): &Int { let x: Int? = 1 let y = &x as &Int? return y! } + `) + + value, err := inter.Invoke("present") + require.NoError(t, err) + require.Equal( + t, + &interpreter.EphemeralReferenceValue{ + Value: interpreter.NewUnmeteredIntValueFromInt64(1), + BorrowedType: sema.IntType, + }, + value, + ) + + }) + + t.Run("absent", func(t *testing.T) { + t.Parallel() + inter := parseCheckAndInterpret(t, ` fun absent(): &Int { let x: Int? = nil let y = &x as &Int? return y! } - `, - ) - - value, err := inter.Invoke("present") - require.NoError(t, err) - require.Equal( - t, - &interpreter.EphemeralReferenceValue{ - Value: interpreter.NewUnmeteredIntValueFromInt64(1), - BorrowedType: sema.IntType, - }, - value, - ) + `) - _, err = inter.Invoke("absent") - RequireError(t, err) + _, err := inter.Invoke("absent") + RequireError(t, err) - var forceNilError interpreter.ForceNilError - require.ErrorAs(t, err, &forceNilError) + var forceNilError interpreter.ForceNilError + require.ErrorAs(t, err, &forceNilError) + }) } func TestInterpretCastingBoxing(t *testing.T) { diff --git a/runtime/tests/interpreter/memory_metering_test.go b/runtime/tests/interpreter/memory_metering_test.go index ab51805ed2..ddd0e577d3 100644 --- a/runtime/tests/interpreter/memory_metering_test.go +++ b/runtime/tests/interpreter/memory_metering_test.go @@ -8423,7 +8423,7 @@ func TestInterpretASTMetering(t *testing.T) { k() // identifier, invocation var l = c ? 1 : 2 // conditional, identifier, integer x2 var m = d as AnyStruct // casting, identifier - var n = &d as &AnyStruct // reference, casting, identifier + var n = &d as &AnyStruct? // reference, casting, identifier var o = d! // force, identifier var p = /public/somepath // path } diff --git a/runtime/tests/interpreter/reference_test.go b/runtime/tests/interpreter/reference_test.go index 48b256df50..ffbad5939d 100644 --- a/runtime/tests/interpreter/reference_test.go +++ b/runtime/tests/interpreter/reference_test.go @@ -876,23 +876,6 @@ func TestInterpretReferenceExpressionOfOptional(t *testing.T) { value := inter.Globals.Get("ref").GetValue() require.IsType(t, interpreter.Nil, value) }) - - t.Run("upcast to optional", func(t *testing.T) { - - t.Parallel() - - inter := parseCheckAndInterpret(t, ` - let i: Int = 1 - let ref = &i as &Int? - `) - - value := inter.Globals.Get("ref").GetValue() - require.IsType(t, &interpreter.SomeValue{}, value) - - innerValue := value.(*interpreter.SomeValue). - InnerValue(inter, interpreter.EmptyLocationRange) - require.IsType(t, &interpreter.EphemeralReferenceValue{}, innerValue) - }) } func TestInterpretReferenceTrackingOnInvocation(t *testing.T) { From e5fd91f8d5926791cbceb31faf687dd7cdf14c87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 26 Jun 2023 16:56:41 -0700 Subject: [PATCH 06/10] simplify boolean expression Co-authored-by: Daniel Sainati --- runtime/sema/check_reference_expression.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/sema/check_reference_expression.go b/runtime/sema/check_reference_expression.go index d96f56a905..8ae9906317 100644 --- a/runtime/sema/check_reference_expression.go +++ b/runtime/sema/check_reference_expression.go @@ -92,7 +92,7 @@ func (checker *Checker) VisitReferenceExpression(referenceExpression *ast.Refere // If the reference type was a non-optional type, // check that the referenced expression does not have an optional type - if _, ok := actualType.(*OptionalType); (ok && !isOpt) || (!ok && isOpt) { + if _, ok := actualType.(*OptionalType); ok != isOpt { checker.report(&TypeMismatchError{ ExpectedType: expectedLeftType, ActualType: actualType, From c13e979c4a36467f26461b1a0b4ef45d540aeafd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Mon, 26 Jun 2023 17:33:06 -0700 Subject: [PATCH 07/10] do not unwrap referenced optional anymore --- runtime/interpreter/value.go | 13 +----- runtime/tests/interpreter/reference_test.go | 51 +++++++++++++++++++++ 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 30fc38dc01..4969ef67be 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -18789,18 +18789,7 @@ func (v *EphemeralReferenceValue) ReferencedValue( locationRange LocationRange, _ bool, ) *Value { - // Just like for storage references, references to optionals are unwrapped, - // i.e. a reference to `nil` aborts when dereferenced. - - switch referenced := v.Value.(type) { - case *SomeValue: - innerValue := referenced.InnerValue(interpreter, locationRange) - return &innerValue - case NilValue: - return nil - default: - return &v.Value - } + return &v.Value } func (v *EphemeralReferenceValue) MustReferencedValue( diff --git a/runtime/tests/interpreter/reference_test.go b/runtime/tests/interpreter/reference_test.go index ffbad5939d..f3739d8819 100644 --- a/runtime/tests/interpreter/reference_test.go +++ b/runtime/tests/interpreter/reference_test.go @@ -918,3 +918,54 @@ func TestInterpretReferenceTrackingOnInvocation(t *testing.T) { require.NoError(t, err) }) } + +func TestInterpretInvalidReferenceToOptionalConfusion(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + struct S { + fun foo() {} + } + + fun main() { + let y: AnyStruct? = nil + let z: AnyStruct = y + let ref = &z as auth &AnyStruct + let s = ref as! &S + s.foo() + } + `) + + _, err := inter.Invoke("main") + RequireError(t, err) + + require.ErrorAs(t, err, &interpreter.ForceCastTypeMismatchError{}) +} + +func TestInterpretReferenceToOptional(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + fun main(): AnyStruct { + let y: Int? = nil + let z: AnyStruct = y + return &z as auth &AnyStruct + } + `) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + AssertValuesEqual( + t, + inter, + &interpreter.EphemeralReferenceValue{ + Value: interpreter.Nil, + BorrowedType: sema.AnyStructType, + Authorized: true, + }, + value, + ) +} From 83b6a5d0f0f93651f0fa921a74830247d97654e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 27 Jun 2023 10:16:37 -0700 Subject: [PATCH 08/10] Add support for forks to compatibility check (#2612) --- .github/workflows/compatibility-check-template.yml | 8 ++++++-- .github/workflows/compatibility-check.yml | 8 +++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/compatibility-check-template.yml b/.github/workflows/compatibility-check-template.yml index 62e9b4937a..065e8f1337 100644 --- a/.github/workflows/compatibility-check-template.yml +++ b/.github/workflows/compatibility-check-template.yml @@ -3,6 +3,10 @@ name: BackwardCompatibilityCheckTemplate on: workflow_call: inputs: + repo: + required: true + type: string + default: onflow/cadence current-branch: required: true type: string @@ -77,7 +81,7 @@ jobs: - name: Check contracts using ${{ inputs.current-branch }} working-directory: ./tools/compatibility-check run: | - GOPROXY=direct go get github.com/onflow/cadence@${{ inputs.current-branch }} + GOPROXY=direct go mod edit -replace github.com/onflow/cadence=github.com/${{ inputs.repo }}@${{ inputs.current-branch }} go mod tidy go run ./cmd/check_contracts/main.go ../../tmp/contracts.csv ../../tmp/output-new.txt @@ -86,7 +90,7 @@ jobs: - name: Check contracts using ${{ inputs.base-branch }} working-directory: ./tools/compatibility-check run: | - GOPROXY=direct go get github.com/onflow/cadence@${{ inputs.base-branch }} + GOPROXY=direct go mod edit -replace github.com/onflow/cadence=github.com/${{ inputs.repo }}@${{ inputs.base-branch }} go mod tidy go run ./cmd/check_contracts/main.go ../../tmp/contracts.csv ../../tmp/output-old.txt diff --git a/.github/workflows/compatibility-check.yml b/.github/workflows/compatibility-check.yml index cbebd2a53c..15bde9893e 100644 --- a/.github/workflows/compatibility-check.yml +++ b/.github/workflows/compatibility-check.yml @@ -3,6 +3,8 @@ name: BackwardCompatibilityCheck on: workflow_dispatch: inputs: + repo: + description: Repository (defaults to 'onflow/cadence') branch: description: 'Current branch/tag' required: true @@ -32,6 +34,7 @@ jobs: runs-on: ubuntu-latest outputs: # Map step output to the job output, so that next job can use these values. + repo: ${{ steps.setup.outputs.repo }} branch: ${{ steps.setup.outputs.branch }} base: ${{ steps.setup.outputs.base }} steps: @@ -44,12 +47,13 @@ jobs: # instead of the branch name, since 'go get' command does not support all kinds of branch names. # # Here there also is a limitation that we can't use the 'merge-branch' because it is not visible to 'go get'. - # So the workflow will not work across forks. run: | if [[ "${{ github.event_name }}" == "pull_request" ]]; then + echo "repo=`(echo "${{ github.event.pull_request.head.repo.full_name }}")`" >> $GITHUB_OUTPUT echo "branch=`(echo "${{ github.event.pull_request.head.sha }}")`" >> $GITHUB_OUTPUT echo "base=`(echo "${{ github.base_ref }}")`" >> $GITHUB_OUTPUT else + echo "repo=`(echo "${{ inputs.repo || 'onflow/cadence' }}")`" >> $GITHUB_OUTPUT echo "branch=`(echo "${{ inputs.branch }}")`" >> $GITHUB_OUTPUT echo "base=`(echo "${{ inputs.base }}")`" >> $GITHUB_OUTPUT fi @@ -57,6 +61,7 @@ jobs: needs: setup uses: ./.github/workflows/compatibility-check-template.yml with: + repo: ${{ inputs.repo }} base-branch: ${{ needs.setup.outputs.base }} current-branch: ${{ needs.setup.outputs.branch }} chain: flow-mainnet @@ -66,6 +71,7 @@ jobs: needs: setup uses: ./.github/workflows/compatibility-check-template.yml with: + repo: ${{ needs.setup.outputs.repo }} base-branch: ${{ needs.setup.outputs.base }} current-branch: ${{ needs.setup.outputs.branch }} chain: flow-testnet From 2d877ad04b7f6faa662f05f3c4a7b3110d544552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20M=C3=BCller?= Date: Tue, 27 Jun 2023 11:18:45 -0700 Subject: [PATCH 09/10] set credentials --- .github/workflows/compatibility-check-template.yml | 6 ++++++ .github/workflows/compatibility-check.yml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/compatibility-check-template.yml b/.github/workflows/compatibility-check-template.yml index 065e8f1337..d406cc9db7 100644 --- a/.github/workflows/compatibility-check-template.yml +++ b/.github/workflows/compatibility-check-template.yml @@ -76,6 +76,12 @@ jobs: path: tmp/contracts.csv key: ${{ steps.cache-key-generator.outputs.cache-key }}-contracts + - name: Configure permissions + if: github.repository != 'onflow/cadence' + run: | + echo "GOPRIVATE=github.com/${{ inputs.repo }}" >> "$GITHUB_ENV" + git config --global url."https://${{ github.actor }}:${{ github.token }}@github.com".insteadOf "https://github.com" + # Check contracts using current branch - name: Check contracts using ${{ inputs.current-branch }} diff --git a/.github/workflows/compatibility-check.yml b/.github/workflows/compatibility-check.yml index 15bde9893e..98456f56c3 100644 --- a/.github/workflows/compatibility-check.yml +++ b/.github/workflows/compatibility-check.yml @@ -61,7 +61,7 @@ jobs: needs: setup uses: ./.github/workflows/compatibility-check-template.yml with: - repo: ${{ inputs.repo }} + repo: ${{ needs.setup.outputs.repo }} base-branch: ${{ needs.setup.outputs.base }} current-branch: ${{ needs.setup.outputs.branch }} chain: flow-mainnet From 51d75f32072c9b67aa9c9aff6fb7c57a3f68dcc8 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 3 Jul 2023 15:55:10 -0700 Subject: [PATCH 10/10] Add runtime tracking for reference to optional resource --- runtime/interpreter/interpreter_expression.go | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/runtime/interpreter/interpreter_expression.go b/runtime/interpreter/interpreter_expression.go index fc49fd6ed3..debcdecb43 100644 --- a/runtime/interpreter/interpreter_expression.go +++ b/runtime/interpreter/interpreter_expression.go @@ -1155,6 +1155,12 @@ func (interpreter *Interpreter) VisitReferenceExpression(referenceExpression *as interpreter.maybeTrackReferencedResourceKindedValue(result) + // There are four potential cases: + // 1) Target type is optional, actual value is also optional (nil/SomeValue) + // 2) Target type is optional, actual value is non-optional + // 3) Target type is non-optional, actual value is optional (SomeValue) + // 4) Target type is non-optional, actual value is non-optional + switch typ := borrowType.(type) { case *sema.OptionalType: innerBorrowType, ok := typ.Type.(*sema.ReferenceType) @@ -1165,6 +1171,7 @@ func (interpreter *Interpreter) VisitReferenceExpression(referenceExpression *as switch result := result.(type) { case *SomeValue: + // Case (1): // References to optionals are transformed into optional references, // so move the *SomeValue out to the reference itself @@ -1190,6 +1197,7 @@ func (interpreter *Interpreter) VisitReferenceExpression(referenceExpression *as return Nil default: + // Case (2): // If the referenced value is non-optional, // but the target type is optional, // then box the reference properly @@ -1212,8 +1220,21 @@ func (interpreter *Interpreter) VisitReferenceExpression(referenceExpression *as } case *sema.ReferenceType: + // Case (3): target type is non-optional, actual value is optional. + // Unwrap the optional and add it to reference tracking. + if someValue, ok := result.(*SomeValue); ok { + locationRange := LocationRange{ + Location: interpreter.Location, + HasPosition: referenceExpression.Expression, + } + innerValue := someValue.InnerValue(interpreter, locationRange) + interpreter.maybeTrackReferencedResourceKindedValue(innerValue) + } + + // Case (4): target type is non-optional, actual value is also non-optional return NewEphemeralReferenceValue(interpreter, typ.Authorized, result, typ.Type) } + panic(errors.NewUnreachableError()) }