Repository: edgedb/edgedb

Issue:
Need to disallow overloading functions on an optional argument
If we add these functions to the `cards` database:
```
function overload(user: optional User) -> str using (
     (user.name ?? "!")
);
create function overload(user: optional Bot) -> str using (
    (user.name ?? "!")
);
```

Then we can get
```
scope> select overload(<User>{});
edgedb error: InternalServerError: unhandled object type  in overloaded function
  Hint: This is most likely a bug in EdgeDB. Please consider opening an issue ticket at https://github.com/edgedb/edgedb/issues/new?template=bug_report.md
  Server traceback:
      edb.errors.InternalServerError: unhandled object type  in overloaded function
```

It doesn't make any sense to do function object overloading on an optional argument, since there is no way to decide which branch to take. Currently we allow it and then ISE because we can't select a branch since the object is NULL.

We should just disallow it.


Discussion:

Resolving patch:
diff --git a/edb/schema/functions.py b/edb/schema/functions.py
--- a/edb/schema/functions.py
+++ b/edb/schema/functions.py
@@ -1417,6 +1417,39 @@ def find_object_param_overloads(
                         )
                     )
 
+                if not all(
+                    new_p.get_typemod(schema)
+                    == ext_p.get_typemod(schema)
+                    for new_p, ext_p in zip(new_params, ext_params)
+                ):
+                    # And also _all_ parameter names must match due to
+                    # current implementation constraints.
+                    my_sig = self.get_signature_as_str(schema)
+                    other_sig = f.get_signature_as_str(schema)
+                    raise errors.UnsupportedFeatureError(
+                        f'cannot create the `{my_sig}` '
+                        f'function: overloading an object type-receiving '
+                        f'function with differences in the type modifiers of '
+                        f'parameters is not supported',
+                        context=srcctx,
+                        details=(
+                            f"Other function is defined as `{other_sig}`"
+                        )
+                    )
+
+                if (
+                    new_params[this_diff_param].get_typemod(schema) !=
+                    ft.TypeModifier.SingletonType
+                ):
+                    my_sig = self.get_signature_as_str(schema)
+                    raise errors.UnsupportedFeatureError(
+                        f'cannot create the `{my_sig}` function: '
+                        f'object type-receiving '
+                        f'functions may not be overloaded on an OPTIONAL '
+                        f'parameter',
+                        context=srcctx,
+                    )
+
                 diff_param = this_diff_param
                 overloads.append(f)
 


Test patch:
diff --git a/tests/test_edgeql_ddl.py b/tests/test_edgeql_ddl.py
--- a/tests/test_edgeql_ddl.py
+++ b/tests/test_edgeql_ddl.py
@@ -4707,6 +4707,32 @@ async def test_edgeql_ddl_function_32(self):
                     USING ('bar');
             """)
 
+        async with self.assertRaisesRegexTx(
+            edgedb.UnsupportedFeatureError,
+            r"cannot create the .* function: overloading an object "
+            r"type-receiving function with differences in the type modifiers "
+            r"of parameters is not supported",
+        ):
+            await self.con.execute(r"""
+                CREATE FUNCTION func32_a(obj: Foo, a: int32) -> str
+                    USING ('foo');
+                CREATE FUNCTION func32_a(obj: Bar, a: OPTIONAL int32) -> str
+                    USING ('bar');
+            """)
+
+        async with self.assertRaisesRegexTx(
+            edgedb.UnsupportedFeatureError,
+            r"cannot create the .* function: object "
+            r"type-receiving functions may not be overloaded on an OPTIONAL "
+            r"parameter"
+        ):
+            await self.con.execute(r"""
+                CREATE FUNCTION func32_a(obj: OPTIONAL Foo) -> str
+                    USING ('foo');
+                CREATE FUNCTION func32_a(obj: OPTIONAL Bar) -> str
+                    USING ('bar');
+            """)
+
     async def test_edgeql_ddl_function_33(self):
         await self.con.execute(r"""
             CREATE TYPE Parent;