Repository: edgedb/edgedb

Issue:
Backlinks don't work in computables when the link is defined in an abstract class
<!-- Please search existing issues to avoid creating duplicates. -->
- EdgeDB Version: Alpha 5
- OS Version: Mac OS 10.15.6

Steps to Reproduce:

Create a schema where a computable link uses a backlink for a link defined in an abstract class

For example the following schema:
```
abstract type Action{
    required property txid -> str{
        constraint exclusive;
    }
    required property timestamp -> int32;
    
        required link user -> User;

    
}

type Post extending Action{
  ...
}

type User {
  ...
  posts := .<user[IS Post];
}
```
produces the error: `InternalServerError: reference to an undefined item default::Post@user in default::User@posts`

If I don't include the IS intersect, i.e.:
```
type User {
  ...
  posts := .<user;
}
```
I get the following error: `SchemaError: cannot get 'name' value: item '3b64a89d-50f4-e418-acaa-c19c327e41d9' is not present in the schema <Schema gen:4106 at 0x7f857a0be820>`



Discussion:
Two bugs here:

1. SDL dependency sorter gets confused about inheritance.
2. Opaque union types produced by naked backlinks don't get added to the schema properly (I opened #1830).


Resolving patch:
diff --git a/edb/edgeql/declarative.py b/edb/edgeql/declarative.py
--- a/edb/edgeql/declarative.py
+++ b/edb/edgeql/declarative.py
@@ -308,7 +308,7 @@ def sdl_to_ddl(
                     ctx.objects[fq_name] = qltracer.ObjectType(fq_name)
 
                 elif isinstance(decl_ast, qlast.CreateScalarType):
-                    ctx.objects[fq_name] = qltracer.Type(fq_name)
+                    ctx.objects[fq_name] = qltracer.ScalarType(fq_name)
 
                 elif isinstance(decl_ast, (qlast.CreateLink,
                                            qlast.CreateProperty)):
@@ -1148,9 +1148,11 @@ def _get_tracer_and_real_type(
     tracer_type: Optional[Type[qltracer.NamedObject]] = None
     real_type: Optional[Type[s_obj.Object]] = None
 
-    if isinstance(decl, (qlast.CreateObjectType,
-                         qlast.CreateScalarType)):
-        tracer_type = qltracer.Type
+    if isinstance(decl, qlast.CreateObjectType):
+        tracer_type = qltracer.ObjectType
+        real_type = s_types.Type
+    elif isinstance(decl, qlast.CreateScalarType):
+        tracer_type = qltracer.ScalarType
         real_type = s_types.Type
     elif isinstance(decl, (qlast.CreateConstraint,
                            qlast.CreateConcreteConstraint)):
diff --git a/edb/edgeql/tracer.py b/edb/edgeql/tracer.py
--- a/edb/edgeql/tracer.py
+++ b/edb/edgeql/tracer.py
@@ -76,7 +76,13 @@ class Annotation(NamedObject):
 
 
 class Type(NamedObject):
-    pass
+    def is_scalar(self) -> bool:
+        return False
+
+
+class ScalarType(Type):
+    def is_scalar(self) -> bool:
+        return True
 
 
 TypeLike = Union[Type, s_types.Type]
@@ -140,6 +146,9 @@ class ObjectType(Type, Source):
     def is_pointer(self) -> bool:
         return False
 
+    def is_scalar(self) -> bool:
+        return False
+
 
 class UnionType(Type):
 
@@ -578,8 +587,23 @@ def trace_Path(
                         # process it on the next step.
                         pass
                     else:
-                        # otherwise we cannot say anything about the target,
-                        # so bail.
+                        # No type intersection, so the only type that
+                        # it can be is "Object", which is trivial.
+                        # However, we need to make it dependent on
+                        # every link of the same name now.
+                        for fqname, obj in ctx.objects.items():
+                            # Ignore what appears to not be a link.
+                            if isinstance(obj, (s_pointers.Pointer,
+                                                Pointer)):
+                                target = obj.get_target(ctx.schema)
+                                if (target is not None and
+                                    not target.is_scalar() and
+                                    fqname.name.split('@', 1)[1] ==
+                                        step.ptr.name):
+                                    # Record link with matching short
+                                    # name.
+                                    ctx.refs.add(fqname)
+
                         return None
                 else:
                     if isinstance(tip, (Source, s_sources.Source)):


Test patch:
diff --git a/tests/test_edgeql_data_migration.py b/tests/test_edgeql_data_migration.py
--- a/tests/test_edgeql_data_migration.py
+++ b/tests/test_edgeql_data_migration.py
@@ -8806,6 +8806,48 @@ async def test_edgeql_migration_union_01(self):
             }
         ''')
 
+    async def test_edgeql_migration_backlink_01(self):
+        await self.migrate('''
+            type User {
+                link posts := .<user;
+            }
+
+            abstract type Action {
+                required link user -> User;
+            }
+
+            type Post extending Action;
+        ''')
+
+        # Make sure that the objects can actually be created and
+        # queried.
+        await self.con.execute('''
+            SET MODULE test;
+            INSERT User;
+        ''')
+        post = await self.con.query_one('''
+            INSERT Post {
+                user := (SELECT User LIMIT 1),
+            };
+        ''')
+
+        await self.assert_query_result(
+            r'''
+                SELECT User{
+                    id,
+                    posts: {
+                        id
+                    } LIMIT 1  # this LIMIT is needed as a workaround
+                               # for another bug
+                }
+            ''',
+            [
+                {
+                    'posts': [{'id': str(post.id)}],
+                },
+            ],
+        )
+
     async def test_edgeql_migration_misplaced_commands(self):
         async with self.assertRaisesRegexTx(
             edgedb.QueryError,