Repository: edgedb/edgedb

Issue:
Cannot access inherited property in computed property
schema
```
type Class extending HasAvailability {
      property schedule := (
        SELECT (.<class[IS Course]).scheduledAt
      );
    }
    abstract type HasAvailability {
     multi link availableAt -> TimeSpot;
    }
    abstract type HasSchedule {
     multi link scheduledAt -> TimeSpot;
    }
    type Course extending HasSchedule{
      required property name -> str;
    }
```

```
object type 'default::Course' has no link or property 'scheduledAt'
```

Note that it works when used in a query


Discussion:
Seems like an SDL ordering bug.

Resolving patch:
diff --git a/edb/edgeql/tracer.py b/edb/edgeql/tracer.py
--- a/edb/edgeql/tracer.py
+++ b/edb/edgeql/tracer.py
@@ -65,7 +65,7 @@ def __init__(self, name):
         super().__init__(name)
         self.pointers = {}
 
-    def is_pointer(self):
+    def is_pointer(self) -> bool:
         return False
 
     def getptr(self, schema, name):
@@ -86,7 +86,7 @@ def __init__(self, name, *, source=None, target=None):
         self.target = target
         self.pointers = {}
 
-    def is_pointer(self):
+    def is_pointer(self) -> bool:
         return True
 
     def getptr(self, schema, name):
@@ -95,6 +95,9 @@ def getptr(self, schema, name):
     def get_target(self, schema):
         return self.target
 
+    def get_source(self, schema):
+        return self.source
+
     def get_name(self, schema):
         return self.name
 
@@ -425,7 +428,8 @@ def trace_Path(node: qlast.Path, *,
                     tip_name = tip.get_name(ctx.schema)
                     ctx.refs.add(f'{tip_name}@{prev_step.ptr.name}')
 
-                tip = ptr.get_target(ctx.schema)
+                # This is a backwards link, so we need the source.
+                tip = ptr.get_source(ctx.schema)
 
         else:
             tr = trace(step, ctx=ctx)
diff --git a/edb/schema/pointers.py b/edb/schema/pointers.py
--- a/edb/schema/pointers.py
+++ b/edb/schema/pointers.py
@@ -751,9 +751,18 @@ def _parse_computable(
             )
 
             if is_ptr_alias:
-                schema, base = irtyputils.ptrcls_from_ptrref(
+                new_schema, base = irtyputils.ptrcls_from_ptrref(
                     expr_rptr.ptrref, schema=schema
                 )
+                # Only pointers coming from the same source as the
+                # alias should be "inherited" (in order to preserve
+                # link props). Random paths coming from other sources
+                # get treated same as any other arbitrary expression
+                # in a computable.
+                if base.get_source(new_schema) != source:
+                    base = None
+                else:
+                    schema = new_schema
 
         self.set_attribute_value('expr', expression)
         required, card = expression.irast.cardinality.to_schema_value()


Test patch:
diff --git a/tests/test_schema.py b/tests/test_schema.py
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -1612,6 +1612,31 @@ def test_get_migration_32(self):
 
         self._assert_migration_consistency(schema)
 
+    def test_get_migration_33(self):
+        # Make sure that Course is defined before the schedule
+        # computable is defined.
+        #
+        # Issue #1383.
+        schema = r'''
+        type Class extending HasAvailability {
+            link schedule :=
+                .<class[IS Course].scheduledAt;
+        }
+        abstract type HasAvailability {
+            multi link availableAt -> TimeSpot;
+        }
+        abstract type HasSchedule {
+            multi link scheduledAt -> TimeSpot;
+        }
+        type Course extending HasSchedule{
+            required property name -> str;
+            link class -> Class;
+        }
+        type TimeSpot;
+        '''
+
+        self._assert_migration_consistency(schema)
+
     def test_get_migration_multi_module_01(self):
         schema = r'''
             # The two declared types declared are from different
diff --git a/tests/test_type_coverage.py b/tests/test_type_coverage.py
--- a/tests/test_type_coverage.py
+++ b/tests/test_type_coverage.py
@@ -208,7 +208,7 @@ def test_cqa_type_coverage_common_markup(self) -> None:
         self.assertFunctionCoverage(EDB_DIR / "common" / "markup", 0)
 
     def test_cqa_type_coverage_edgeql(self) -> None:
-        self.assertFunctionCoverage(EDB_DIR / "edgeql", 44.70)
+        self.assertFunctionCoverage(EDB_DIR / "edgeql", 44.82)
 
     def test_cqa_type_coverage_edgeqlcompiler(self) -> None:
         self.assertFunctionCoverage(EDB_DIR / "edgeql" / "compiler", 100.00)