Repository: hylang/hy

Issue:
Can't `match` on dotted type names
```hy
(let [a '+]
  (match a 
    '+ 1
    _ 2))
```
expected output: 2
got:
```
Traceback (most recent call last):
  File "stdin-494d70fad1aae99772cfec1259def00daabbaec3", line 1, in <module>
    (let [a '+]
NameError: name 'quote' is not defined
```
here's the generated code:
```py
import hy
_hy_let_a_1 = hy.models.Symbol('+')
_hy_anon_var_2 = None
match _hy_let_a_1:
    case quote(hyx_Xplus_signX):
        _hy_anon_var_2 = 1
    case _:
        _hy_anon_var_2 = 2
```
manual fix is not working:
```hy
(match '+ 
   (hy.models.Symbol "+") 1 
    _ 2)
```
output:
```
Traceback (most recent call last):
  File "stdin-8930588efcee2a15eccc4f2c562960b45e187fcb", line 2
    (hy.models.Symbol "+") 1 
    ^
hy.errors.HySyntaxError: parse error for pattern macro 'match': got unexpected token: hy.models.Expression([
  hy.models.Symbol('quote'),
  hy.models.Symbol('+')]), expected: some(...)
```

for now I'm falling back to if&elif.


Discussion:
The forbiddance of `quote` is not a bug: `match` restricts what is allowed in patterns, as documented. [This particular case is discussed here.](https://github.com/hylang/hy/discussions/2366)

The second form wouldn't do what you want even if it was allowed. However, the fact that it's forbidden is in fact a bug, and is probably a regression I introduced in #2382. For example, this Python works

```python
import dataclasses

@dataclasses.dataclass
class C:
    x: int
C.C = C

match C.C(1):
   case C.C(1):
        print('match')
```

but the equivalent Hy fails while compiling `match`:

```hy
(import dataclasses)

(defclass [dataclasses.dataclass] C []
  #^int x)
(setv C.C C)

(match (C.C 1)
  (C.C 1)
    (print "match"))
```

Resolving patch:
diff --git a/hy/core/result_macros.py b/hy/core/result_macros.py
--- a/hy/core/result_macros.py
+++ b/hy/core/result_macros.py
@@ -1084,7 +1084,8 @@ def compile_with_expression(compiler, expr, root, args, body):
         | pexpr(keepsym("|"), many(_pattern))
         | braces(many(LITERAL + _pattern), maybe(pvalue("unpack-mapping", SYM)))
         | pexpr(
-            notsym(".", "|", "unpack-mapping", "unpack-iterable"),
+            pexpr(keepsym("."), oneplus(SYM))
+            | notsym(".", "|", "unpack-mapping", "unpack-iterable"),
             many(parse_if(lambda x: not isinstance(x, Keyword), _pattern)),
             many(KEYWORD + _pattern),
         )
@@ -1219,11 +1220,15 @@ def compile_pattern(compiler, pattern):
             )
         )
     elif isinstance(value, Expression):
-        root, args, kwargs = value
+        head, args, kwargs = value
         keywords, values = zip(*kwargs) if kwargs else ([], [])
         return asty.MatchClass(
             value,
-            cls=compiler.scope.access(asty.Name(root, id=mangle(root), ctx=ast.Load())),
+            cls=compiler.compile(
+              # `head` could be a symbol or a dotted form.
+                (head[:1] + head[1]).replace(head)
+                if type(head) is Expression
+                else head).expr,
             patterns=[compile_pattern(compiler, v) for v in args],
             kwd_attrs=[kwd.name for kwd in keywords],
             kwd_patterns=[compile_pattern(compiler, value) for value in values],


Test patch:
diff --git a/tests/native_tests/match.hy b/tests/native_tests/match.hy
--- a/tests/native_tests/match.hy
+++ b/tests/native_tests/match.hy
@@ -5,6 +5,10 @@
         dataclasses [dataclass]
         hy.errors [HySyntaxError])
 
+(defclass [dataclass] Point []
+    (#^int x)
+    (#^int y))
+
 (defn test-pattern-matching []
   (assert (is (match 0
                      0 :if False False
@@ -78,10 +82,6 @@
        0)
     0))
 
-  (defclass [dataclass] Point []
-    (#^int x)
-    (#^int y))
-
   (assert (= 0 (match (Point 1 0) (Point 1 :y var) var)))
   (assert (is None (match (Point 0 0) (Point 1 :y var) var)))
 
@@ -130,6 +130,12 @@
     (hy.eval '(match 1
                      1 :if True :as x x))))
 
+(defn test-dotted-constructor []
+  ; https://github.com/hylang/hy/issues/2404
+  (defclass C [Point]
+    (setv C Point))
+  (assert (= (match (Point 1 2) (C.C 1 2) "ok") "ok")))
+
 (defn test-matching-side-effects []
   (setv x 0)
   (defn foo []
@@ -165,10 +171,6 @@
     (assert (= [x y] [5 6])))
   (assert (= [x y] [3 4])))
 
-(defclass [dataclass] Point []
-  (#^int x)
-  (#^int y))
-
 (defn test-let-match-pattern []
   (setv [x y] [1 2]
         p (Point 5 6))