Repository: edgedb/edgedb

Issue:
Link property with the same name as a property on the object doesn't work with double splat
Currently having a link property with the same name as a property on the object kills the compiler when trying to use double splat. Is this intended?
![image](https://github.com/edgedb/edgedb/assets/50501825/ca9ae720-a38e-46f8-befa-245607bc5ec2)

- EdgeDB Version: "5.0-rc.1+873d354"
- EdgeDB CLI Version:
- OS Version:

Schema:
```
module default {
	type X {
		created_at: datetime
	}
	type Y {
		x: X {
			created_at: datetime
		}
	}
}
```


Discussion:

Resolving patch:
diff --git a/edb/edgeql/compiler/viewgen.py b/edb/edgeql/compiler/viewgen.py
--- a/edb/edgeql/compiler/viewgen.py
+++ b/edb/edgeql/compiler/viewgen.py
@@ -289,7 +289,8 @@ def _process_view(
     }
 
     # Now look for any splats and expand them.
-    splat_descs: dict[str, ShapeElementDesc] = {}
+    # Track descriptions by name and whether they are link properties.
+    splat_descs: dict[Tuple[str, bool], ShapeElementDesc] = {}
     for shape_el in elements:
         if not isinstance(shape_el.expr.steps[0], qlast.Splat):
             continue
@@ -369,7 +370,8 @@ def _process_view(
             desc = _shape_el_ql_to_shape_el_desc(
                 splat_el, source=view_scls, s_ctx=s_ctx, ctx=ctx
             )
-            if old_desc := splat_descs.get(desc.ptr_name):
+            desc_key: Tuple[str, bool] = (desc.ptr_name, desc.is_linkprop)
+            if old_desc := splat_descs.get(desc_key):
                 # If pointers appear in multiple splats, we take the
                 # one from the ancestor class. If neither class is an
                 # ancestor, we reject it.
@@ -390,7 +392,7 @@ def _process_view(
                     pass
                 elif old_source.issubclass(ctx.env.schema, new_source):
                     # Take the new one
-                    splat_descs[desc.ptr_name] = desc
+                    splat_descs[desc_key] = desc
                 else:
                     vn1 = old_source.get_verbosename(schema=ctx.env.schema)
                     vn2 = new_source.get_verbosename(schema=ctx.env.schema)
@@ -401,7 +403,7 @@ def _process_view(
                     )
 
             else:
-                splat_descs[desc.ptr_name] = desc
+                splat_descs[desc_key] = desc
 
     shape_desc.extend(splat_descs.values())
 


Test patch:
diff --git a/tests/test_edgeql_select.py b/tests/test_edgeql_select.py
--- a/tests/test_edgeql_select.py
+++ b/tests/test_edgeql_select.py
@@ -1937,6 +1937,29 @@ async def test_edgeql_select_splat_05(self):
                 order by .name;
             ''')
 
+    async def test_edgeql_select_splat_06(self):
+        # Target property and link property with same name
+        await self.con.execute('''
+            CREATE TYPE X {
+                CREATE PROPERTY a: int64;
+            };
+            CREATE TYPE Y {
+                CREATE LINK x: X {
+                    CREATE PROPERTY a: int64;
+                };
+            };
+            insert Y { x := ( insert X { a := 1 } ){ @a := 2 } };
+        ''')
+
+        await self.assert_query_result(
+            r'''
+            select Y {**}
+            ''',
+            tb.bag([
+                {'x': {'a': 1, '@a': 2}}
+            ]),
+        )
+
     async def test_edgeql_select_id_01(self):
         # allow assigning id to a computed (#4781)
         await self.con.query('SELECT schema::Type { XYZ := .id};')