From a38d66533122153077e834371ce80a390ec9ea3a Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 1 Aug 2023 13:50:36 -0400 Subject: [PATCH 1/2] properly inherit interface members --- runtime/sema/type.go | 10 ++++ runtime/tests/checker/interface_test.go | 66 +++++++++++++++++++++ runtime/tests/interpreter/interface_test.go | 34 +++++++++++ 3 files changed, 110 insertions(+) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 0f35a03296..537159e621 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -4951,6 +4951,16 @@ func (t *InterfaceType) initializeMemberResolvers() { t.memberResolversOnce.Do(func() { members := MembersMapAsResolvers(t.Members) + // add any inherited members from up the inheritance chain + for _, conformance := range t.EffectiveInterfaceConformances() { + for name, member := range conformance.InterfaceType.GetMembers() { + if _, ok := members[name]; !ok { + members[name] = member + } + } + + } + t.memberResolvers = withBuiltinMembers(t, members) }) } diff --git a/runtime/tests/checker/interface_test.go b/runtime/tests/checker/interface_test.go index ca37150ed8..1b2d237bb6 100644 --- a/runtime/tests/checker/interface_test.go +++ b/runtime/tests/checker/interface_test.go @@ -4095,6 +4095,72 @@ func TestCheckInterfaceTypeDefinitionInheritance(t *testing.T) { } +func TestInheritedInterfaceMembers(t *testing.T) { + t.Parallel() + + t.Run("inherited interface field", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource interface A { + let foo: String + } + resource interface B: A {} + resource C: B { + let foo: String + init() { + self.foo = "" + } + } + fun test() { + let c: @{B} <- create C() + c.foo + destroy c + } + `) + + require.NoError(t, err) + }) + + t.Run("inherited interface function", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource interface A { + fun foo () + } + resource interface B: A {} + fun test(c: @{B}) { + c.foo() + destroy c + } + `) + + require.NoError(t, err) + }) + + t.Run("doubly inherited interface function", func(t *testing.T) { + + t.Parallel() + + _, err := ParseAndCheck(t, ` + resource interface A { + fun foo () + } + resource interface B: A {} + resource interface C: B {} + fun test(c: @{C}) { + c.foo() + destroy c + } + `) + + require.NoError(t, err) + }) +} + func TestCheckInterfaceEventsInheritance(t *testing.T) { t.Parallel() diff --git a/runtime/tests/interpreter/interface_test.go b/runtime/tests/interpreter/interface_test.go index 1cef9ce9d3..833357cd0a 100644 --- a/runtime/tests/interpreter/interface_test.go +++ b/runtime/tests/interpreter/interface_test.go @@ -160,6 +160,40 @@ func TestInterpretInterfaceDefaultImplementation(t *testing.T) { array.Get(inter, interpreter.EmptyLocationRange, 1), ) }) + + t.Run("inherited interface function", func(t *testing.T) { + + t.Parallel() + + inter := parseCheckAndInterpret(t, ` + + struct interface I { + fun test(): Int { + return 3 + } + } + + struct interface J: I {} + + struct S: J {} + + fun foo(_ s: {J}): Int { + return s.test() + } + + fun main(): Int { + return foo(S()) + } + `) + + value, err := inter.Invoke("main") + require.NoError(t, err) + + assert.Equal(t, + interpreter.NewUnmeteredIntValueFromInt64(3), + value, + ) + }) } func TestInterpretInterfaceDefaultImplementationWhenOverriden(t *testing.T) { From 88806dfa1c6408bab1c91391b49988c717580ff9 Mon Sep 17 00:00:00 2001 From: Daniel Sainati Date: Tue, 1 Aug 2023 13:51:30 -0400 Subject: [PATCH 2/2] lint --- runtime/sema/type.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/sema/type.go b/runtime/sema/type.go index 537159e621..2e665098dc 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -4953,7 +4953,7 @@ func (t *InterfaceType) initializeMemberResolvers() { // add any inherited members from up the inheritance chain for _, conformance := range t.EffectiveInterfaceConformances() { - for name, member := range conformance.InterfaceType.GetMembers() { + for name, member := range conformance.InterfaceType.GetMembers() { //nolint:maprange if _, ok := members[name]; !ok { members[name] = member }