Repository: edgedb/edgedb

Issue:
InvalidDefinitionError: property [...] is defined recursively
While creating schema with computeds, I got the following error:

```
$ edgedb migration create
edgedb error: InvalidDefinitionError: property 'name' of object type 'default::Member' is defined recursively
```

Here is a schema to reproduce the error:

```esdl
module default {
        type Person {
            required property `name` -> str;
            required property `surname` -> str;
            property `fullname` := .name ++ ' ' ++ .surname;
        }

        type Member {
            property `name` := .person.fullname;
            required link `person` -> Person;
        }
}
```

Idea is that `Member` have computed property `name` which is a computed property of a `Person`.

Creating this schema fails. If I rename Member's `name` into `fullname` (or something else) - it works, but I don't understand where exactly recursion is happening here.

I tried to add `__subject__` in computeds:

```esdl
module default {
        type Person {
            required property `name` -> str;
            required property `surname` -> str;
            property `fullname` := __subject__.name ++ ' ' ++ __subject__.surname;
        }

        type Member {
            property `fullname` := __subject__.person.fullname;
            required link `person` -> Person;
        }
}

```

but it fails with similarly strange error:
```
edgedb migration create
error: __subject__ cannot be used in this expression
  ┌─ dbschema/default.esdl:5:36
  │
5 │             property `fullname` := __subject__.name ++ ' ' ++ __subject__.surname;
  │                                    ^^^^^^^^^^^ error
```

- EdgeDB Version: "2.11+222b19e"
- EdgeDB CLI Version: EdgeDB CLI 2.2.6+7eabbf9
- OS Version: MacOS Ventura 13.1 (22C65)

What am I doing wrong here?



Discussion:

Resolving patch:
diff --git a/edb/edgeql/tracer.py b/edb/edgeql/tracer.py
--- a/edb/edgeql/tracer.py
+++ b/edb/edgeql/tracer.py
@@ -885,7 +885,13 @@ def trace_Path(
                                 # We haven't computed the target yet,
                                 # so try computing it now.
                                 ctx.visited.add(ptr)
-                                ptr_target = trace(ptr.target_expr, ctx=ctx)
+
+                                target_ctx = _fork_context(ctx)
+                                target_ctx.path_prefix = sname
+                                ptr_target = trace(
+                                    ptr.target_expr, ctx=target_ctx
+                                )
+
                                 if isinstance(ptr_target, (Type,
                                                            s_types.Type)):
                                     tip = ptr.target = ptr_target


Test patch:
diff --git a/tests/test_schema.py b/tests/test_schema.py
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -4186,6 +4186,23 @@ def test_schema_get_migration_54(self):
 
         self._assert_migration_consistency(schema)
 
+    def test_schema_get_migration_55(self):
+        # In Bar, `.foo.b` refers to `.a`. Ensure that when tracing, `.a` is
+        # correctly `Foo.a`, otherwise a recurisve definition is found.
+        schema = r'''
+            type Foo {
+                property a -> str;
+                property b := .a;
+            }
+
+            type Bar {
+                property a := .foo.b;
+                link foo -> Foo;
+            }
+        '''
+
+        self._assert_migration_consistency(schema)
+
     def test_schema_get_migration_multi_module_01(self):
         schema = r'''
             # The two declared types declared are from different
@@ -6420,8 +6437,8 @@ def test_schema_migrations_equivalence_recursive_02(self):
         with self.assertRaisesRegex(
             errors.InvalidDefinitionError,
             "definition dependency cycle between "
-            "property 'val' of object type 'default::Bar' and "
-            "property 'val' of object type 'default::Foo'"
+            "property 'val' of object type 'default::Foo' and "
+            "property 'val' of object type 'default::Bar'"
         ):
             self._assert_migration_equivalence([r"""
                 type Foo {