Skip to content

Commit 266c157

Browse files
authored
Merge pull request #3775 from onflow/bastian/v1.3-port-internal-fixes
[v1.3] Port internal fixes
2 parents effc21a + 95214b0 commit 266c157

12 files changed

+505
-17
lines changed

ast/visitor.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type Element interface {
3030

3131
type StatementDeclarationVisitor[T any] interface {
3232
VisitVariableDeclaration(*VariableDeclaration) T
33-
VisitFunctionDeclaration(*FunctionDeclaration) T
33+
VisitFunctionDeclaration(declaration *FunctionDeclaration, isStatement bool) T
3434
VisitSpecialFunctionDeclaration(*SpecialFunctionDeclaration) T
3535
VisitCompositeDeclaration(*CompositeDeclaration) T
3636
VisitAttachmentDeclaration(*AttachmentDeclaration) T
@@ -68,7 +68,7 @@ func AcceptDeclaration[T any](declaration Declaration, visitor DeclarationVisito
6868
return visitor.VisitVariableDeclaration(declaration.(*VariableDeclaration))
6969

7070
case ElementTypeFunctionDeclaration:
71-
return visitor.VisitFunctionDeclaration(declaration.(*FunctionDeclaration))
71+
return visitor.VisitFunctionDeclaration(declaration.(*FunctionDeclaration), false)
7272

7373
case ElementTypeSpecialFunctionDeclaration:
7474
return visitor.VisitSpecialFunctionDeclaration(declaration.(*SpecialFunctionDeclaration))
@@ -151,7 +151,7 @@ func AcceptStatement[T any](statement Statement, visitor StatementVisitor[T]) (_
151151
return visitor.VisitVariableDeclaration(statement.(*VariableDeclaration))
152152

153153
case ElementTypeFunctionDeclaration:
154-
return visitor.VisitFunctionDeclaration(statement.(*FunctionDeclaration))
154+
return visitor.VisitFunctionDeclaration(statement.(*FunctionDeclaration), true)
155155

156156
case ElementTypeSpecialFunctionDeclaration:
157157
return visitor.VisitSpecialFunctionDeclaration(statement.(*SpecialFunctionDeclaration))

interpreter/interface_test.go

+96
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,102 @@ func TestInterpretInterfaceFunctionConditionsInheritance(t *testing.T) {
11071107
assert.Equal(t, "\"hello\"", logs[0])
11081108
})
11091109

1110+
t.Run("default and conditions in parent, more conditions in child", func(t *testing.T) {
1111+
1112+
t.Parallel()
1113+
1114+
var logs []string
1115+
1116+
logFunction := stdlib.NewStandardLibraryStaticFunction(
1117+
"log",
1118+
&sema.FunctionType{
1119+
Parameters: []sema.Parameter{
1120+
{
1121+
Label: sema.ArgumentLabelNotRequired,
1122+
Identifier: "value",
1123+
TypeAnnotation: sema.NewTypeAnnotation(sema.AnyStructType),
1124+
},
1125+
},
1126+
ReturnTypeAnnotation: sema.NewTypeAnnotation(
1127+
sema.VoidType,
1128+
),
1129+
Purity: sema.FunctionPurityView,
1130+
},
1131+
``,
1132+
func(invocation interpreter.Invocation) interpreter.Value {
1133+
message := invocation.Arguments[0].MeteredString(
1134+
invocation.Interpreter,
1135+
interpreter.SeenReferences{},
1136+
invocation.LocationRange,
1137+
)
1138+
logs = append(logs, message)
1139+
return interpreter.Void
1140+
},
1141+
)
1142+
1143+
baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation)
1144+
baseValueActivation.DeclareValue(logFunction)
1145+
1146+
baseActivation := activations.NewActivation(nil, interpreter.BaseActivation)
1147+
interpreter.Declare(baseActivation, logFunction)
1148+
1149+
code := `
1150+
struct interface Foo {
1151+
fun test() {
1152+
pre {
1153+
printMessage("invoked Foo.test() pre-condition")
1154+
}
1155+
post {
1156+
printMessage("invoked Foo.test() post-condition")
1157+
}
1158+
printMessage("invoked Foo.test()")
1159+
}
1160+
}
1161+
1162+
struct Test: Foo {
1163+
}
1164+
1165+
view fun printMessage(_ msg: String): Bool {
1166+
log(msg)
1167+
return true
1168+
}
1169+
1170+
fun main() {
1171+
Test().test()
1172+
}
1173+
`
1174+
1175+
inter, err := parseCheckAndInterpretWithOptions(
1176+
t,
1177+
code,
1178+
ParseCheckAndInterpretOptions{
1179+
Config: &interpreter.Config{
1180+
BaseActivationHandler: func(_ common.Location) *interpreter.VariableActivation {
1181+
return baseActivation
1182+
},
1183+
},
1184+
CheckerConfig: &sema.Config{
1185+
BaseValueActivationHandler: func(_ common.Location) *sema.VariableActivation {
1186+
return baseValueActivation
1187+
},
1188+
},
1189+
HandleCheckerError: nil,
1190+
},
1191+
)
1192+
require.NoError(t, err)
1193+
1194+
_, err = inter.Invoke("main")
1195+
require.NoError(t, err)
1196+
1197+
require.Equal(
1198+
t,
1199+
[]string{
1200+
`"invoked Foo.test() pre-condition"`,
1201+
`"invoked Foo.test()"`,
1202+
`"invoked Foo.test() post-condition"`,
1203+
}, logs,
1204+
)
1205+
})
11101206
}
11111207

11121208
func TestInterpretNestedInterfaceCast(t *testing.T) {

interpreter/interpreter.go

+46-5
Original file line numberDiff line numberDiff line change
@@ -703,10 +703,10 @@ func (interpreter *Interpreter) VisitProgram(program *ast.Program) {
703703
}
704704

705705
func (interpreter *Interpreter) VisitSpecialFunctionDeclaration(declaration *ast.SpecialFunctionDeclaration) StatementResult {
706-
return interpreter.VisitFunctionDeclaration(declaration.FunctionDeclaration)
706+
return interpreter.VisitFunctionDeclaration(declaration.FunctionDeclaration, false)
707707
}
708708

709-
func (interpreter *Interpreter) VisitFunctionDeclaration(declaration *ast.FunctionDeclaration) StatementResult {
709+
func (interpreter *Interpreter) VisitFunctionDeclaration(declaration *ast.FunctionDeclaration, isStatement bool) StatementResult {
710710

711711
identifier := declaration.Identifier.Identifier
712712

@@ -718,6 +718,33 @@ func (interpreter *Interpreter) VisitFunctionDeclaration(declaration *ast.Functi
718718
// lexical scope: variables in functions are bound to what is visible at declaration time
719719
lexicalScope := interpreter.activations.CurrentOrNew()
720720

721+
if isStatement {
722+
723+
// This function declaration is an inner function.
724+
//
725+
// Variables which are declared after this function declaration
726+
// should not be visible or even overwrite the variables captured by the closure
727+
/// (e.g. through shadowing).
728+
//
729+
// For example:
730+
//
731+
// fun foo(a: Int): Int {
732+
// fun bar(): Int {
733+
// return a
734+
// // ^ should refer to the `a` parameter of `foo`,
735+
// // not to the `a` variable declared after `bar`
736+
// }
737+
// let a = 2
738+
// return bar()
739+
// }
740+
//
741+
// As variable declarations mutate the current activation in place,
742+
// push a new activation, so that the mutations are not performed
743+
// on the captured activation.
744+
745+
interpreter.activations.PushNewWithCurrent()
746+
}
747+
721748
// make the function itself available inside the function
722749
lexicalScope.Set(identifier, variable)
723750

@@ -1297,6 +1324,8 @@ func (declarationInterpreter *Interpreter) declareNonEnumCompositeValue(
12971324
}
12981325
}
12991326

1327+
config := declarationInterpreter.SharedState.Config
1328+
13001329
wrapFunctions := func(ty *sema.InterfaceType, code WrapperCode) {
13011330

13021331
// Wrap initializer
@@ -1315,6 +1344,16 @@ func (declarationInterpreter *Interpreter) declareNonEnumCompositeValue(
13151344
// the order does not matter.
13161345

13171346
for name, functionWrapper := range code.FunctionWrappers { //nolint:maprange
1347+
// If there's a default implementation, then skip explicitly/separately
1348+
// running the conditions of that functions.
1349+
// Because the conditions also get executed when the default implementation is executed.
1350+
// This works because:
1351+
// - `code.Functions` only contains default implementations.
1352+
// - There is always only one default implementation (cannot override by other interfaces).
1353+
if code.Functions.Contains(name) {
1354+
continue
1355+
}
1356+
13181357
fn, ok := functions.Get(name)
13191358
// If there is a wrapper, there MUST be a body.
13201359
if !ok {
@@ -1354,8 +1393,6 @@ func (declarationInterpreter *Interpreter) declareNonEnumCompositeValue(
13541393

13551394
qualifiedIdentifier := compositeType.QualifiedIdentifier()
13561395

1357-
config := declarationInterpreter.SharedState.Config
1358-
13591396
constructorType := compositeType.ConstructorFunctionType()
13601397

13611398
constructorGenerator := func(address common.Address) *HostFunctionValue {
@@ -2457,7 +2494,11 @@ func (interpreter *Interpreter) functionConditionsWrapper(
24572494
lexicalScope *VariableActivation,
24582495
) FunctionWrapper {
24592496

2460-
if declaration.FunctionBlock == nil {
2497+
if declaration.FunctionBlock == nil ||
2498+
declaration.FunctionBlock.HasStatements() {
2499+
// If there's a default implementation (i.e: has statements),
2500+
// then skip explicitly/separately running the conditions of that functions.
2501+
// Because the conditions also get executed when the default implementation is executed.
24612502
return nil
24622503
}
24632504

interpreter/interpreter_expression.go

+23-1
Original file line numberDiff line numberDiff line change
@@ -1315,9 +1315,31 @@ func (interpreter *Interpreter) visitEntries(entries []ast.DictionaryEntry) []Di
13151315

13161316
func (interpreter *Interpreter) VisitFunctionExpression(expression *ast.FunctionExpression) Value {
13171317

1318-
// lexical scope: variables in functions are bound to what is visible at declaration time
1318+
// lexical scope: variables in functions are bound to what is visible at declaration time.
13191319
lexicalScope := interpreter.activations.CurrentOrNew()
13201320

1321+
// Variables which are declared after this function declaration
1322+
// should not be visible or even overwrite the variables captured by the closure
1323+
/// (e.g. through shadowing).
1324+
//
1325+
// For example:
1326+
//
1327+
// fun foo(a: Int): Int {
1328+
// let bar = fun(): Int {
1329+
// return a
1330+
// // ^ should refer to the `a` parameter of `foo`,
1331+
// // not to the `a` variable declared after `bar`
1332+
// }
1333+
// let a = 2
1334+
// return bar()
1335+
// }
1336+
//
1337+
// As variable declarations mutate the current activation in place,
1338+
// push a new activation, so that the mutations are not performed
1339+
// on the captured activation.
1340+
1341+
interpreter.activations.PushNewWithCurrent()
1342+
13211343
functionType := interpreter.Program.Elaboration.FunctionExpressionFunctionType(expression)
13221344

13231345
var preConditions []ast.Condition

interpreter/misc_test.go

+102
Original file line numberDiff line numberDiff line change
@@ -6864,6 +6864,108 @@ func TestInterpretClosure(t *testing.T) {
68646864
)
68656865
}
68666866

6867+
func TestInterpretClosureScopingFunctionExpression(t *testing.T) {
6868+
t.Parallel()
6869+
6870+
inter := parseCheckAndInterpret(t, `
6871+
fun test(a: Int): Int {
6872+
let bar = fun(): Int {
6873+
return a
6874+
}
6875+
let a = 2
6876+
return bar()
6877+
}
6878+
`)
6879+
6880+
actual, err := inter.Invoke("test",
6881+
interpreter.NewUnmeteredIntValueFromInt64(1),
6882+
)
6883+
require.NoError(t, err)
6884+
6885+
AssertValuesEqual(
6886+
t,
6887+
inter,
6888+
interpreter.NewUnmeteredIntValueFromInt64(1),
6889+
actual,
6890+
)
6891+
}
6892+
6893+
func TestInterpretClosureScopingInnerFunction(t *testing.T) {
6894+
t.Parallel()
6895+
6896+
inter := parseCheckAndInterpret(t, `
6897+
fun test(a: Int): Int {
6898+
fun bar(): Int {
6899+
return a
6900+
}
6901+
let a = 2
6902+
return bar()
6903+
}
6904+
`)
6905+
6906+
value, err := inter.Invoke("test",
6907+
interpreter.NewUnmeteredIntValueFromInt64(1),
6908+
)
6909+
require.NoError(t, err)
6910+
6911+
AssertValuesEqual(
6912+
t,
6913+
inter,
6914+
interpreter.NewUnmeteredIntValueFromInt64(1),
6915+
value,
6916+
)
6917+
}
6918+
6919+
func TestInterpretAssignmentAfterClosureFunctionExpression(t *testing.T) {
6920+
t.Parallel()
6921+
6922+
inter := parseCheckAndInterpret(t, `
6923+
fun test(): Int {
6924+
var a = 1
6925+
let bar = fun(): Int {
6926+
return a
6927+
}
6928+
a = 2
6929+
return bar()
6930+
}
6931+
`)
6932+
6933+
value, err := inter.Invoke("test")
6934+
require.NoError(t, err)
6935+
6936+
AssertValuesEqual(
6937+
t,
6938+
inter,
6939+
interpreter.NewUnmeteredIntValueFromInt64(2),
6940+
value,
6941+
)
6942+
}
6943+
6944+
func TestInterpretAssignmentAfterClosureInnerFunction(t *testing.T) {
6945+
t.Parallel()
6946+
6947+
inter := parseCheckAndInterpret(t, `
6948+
fun test(): Int {
6949+
var a = 1
6950+
fun bar(): Int {
6951+
return a
6952+
}
6953+
a = 2
6954+
return bar()
6955+
}
6956+
`)
6957+
6958+
value, err := inter.Invoke("test")
6959+
require.NoError(t, err)
6960+
6961+
AssertValuesEqual(
6962+
t,
6963+
inter,
6964+
interpreter.NewUnmeteredIntValueFromInt64(2),
6965+
value,
6966+
)
6967+
}
6968+
68676969
// TestInterpretCompositeFunctionInvocationFromImportingProgram checks
68686970
// that member functions of imported composites can be invoked from an importing program.
68696971
// See https://github.com/dapperlabs/flow-go/issues/838

0 commit comments

Comments
 (0)