Skip to content
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
08e32c3
AST-38: add failing test
ggreif Feb 19, 2019
921c06a
add Int arguments
ggreif Feb 19, 2019
7fcb0a5
AST-38: introduce ParP to indicate unary function
ggreif Feb 20, 2019
67135be
no more type errors, TODO: non-fail
ggreif Feb 20, 2019
3d3ee75
fix parser to remember significant parenthesis
ggreif Feb 20, 2019
4fe6e09
test successful
ggreif Feb 20, 2019
2d8eac1
beef up the testing, discovering a subtle issue in wasm
ggreif Feb 20, 2019
1383827
review feedback
ggreif Feb 20, 2019
3f2e405
classes are basically functions and as such inherit the top-level ari…
ggreif Feb 20, 2019
da9c08b
Factor out `pat_simple`
ggreif Feb 20, 2019
0d797e6
more tests (classes)
ggreif Feb 21, 2019
53cb2ad
consolidate tests
ggreif Feb 21, 2019
d31dcac
fix switch syntax in README
ggreif Feb 21, 2019
b5b9925
simplify
ggreif Feb 21, 2019
71ccf02
tests for redundant parens deeper down in patterns
ggreif Feb 21, 2019
de8f3a2
more simplifications
ggreif Feb 21, 2019
6f4ee64
roll back parser complications, materialising parens around tuples in…
ggreif Feb 21, 2019
7ca3979
Merge branch 'master' into gabor/one-tuples
ggreif Feb 21, 2019
0d4844b
merge snafu
ggreif Feb 21, 2019
7daa160
desugar toplevel parentheses as TupP
ggreif Feb 22, 2019
82f4f42
accept IR problems for now
ggreif Feb 22, 2019
922f757
WIP: it appears that running non-canonical patterns is still a problem
ggreif Feb 22, 2019
88d6270
remove .ok files
ggreif Feb 22, 2019
e646c9c
fix nits
ggreif Feb 24, 2019
f02396b
Merge remote-tracking branch 'origin/master' into gabor/one-tuples
ggreif Feb 24, 2019
02fd5ee
apply custom lowering to unary functions, stripping the outermost TupP
ggreif Feb 25, 2019
10547cb
typo
ggreif Feb 25, 2019
f9ccad9
transform the type notes like the patterns
ggreif Feb 25, 2019
7cbdb57
grammar in comment
ggreif Feb 25, 2019
9c29a48
more precise Invalid_argument messages
ggreif Feb 26, 2019
2688aeb
use augmented source location
ggreif Feb 26, 2019
0b42e5e
test AST-56
ggreif Feb 26, 2019
92ea6b6
review feedback: fold 'fix_unary' into 'param'
ggreif Feb 28, 2019
c67a012
Revert "more precise Invalid_argument messages"
ggreif Feb 28, 2019
1052fbb
Merge remote-tracking branch 'origin/master' into gabor/one-tuples
ggreif Mar 4, 2019
f93ecdc
transform FuncE parameters in exp'
ggreif Mar 4, 2019
bffcea3
extend tests to include function expressions
ggreif Mar 4, 2019
e4e7268
fix infer_pat' descent into PatP
ggreif Mar 4, 2019
c392ade
experimental: always create ParP
ggreif Mar 4, 2019
81200bb
review feedback: add negative tests
ggreif Mar 5, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ and open the path printed on the last line of that command.
* Conditionals and switches
- `if b ...`
- `if b ... else ...`
- `switch x case 1 ... case 2 ... case _ ...`
- `switch x { case 1 ...; case 2 ...; case _ ...}`

* While loops and iterations
- `while (p()) ...`
Expand Down
5 changes: 3 additions & 2 deletions src/arrange.ml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ let rec exp e = match e.it with
| VarE x -> "VarE" $$ [id x]
| LitE l -> "LitE" $$ [lit !l]
| UnE (ot, uo, e) -> "UnE" $$ [operator_type !ot; unop uo; exp e]
| BinE (ot, e1, bo, e2) -> "BinE" $$ [operator_type !ot; exp e1; binop bo; exp e2]
| RelE (ot, e1, ro, e2) -> "RelE" $$ [operator_type !ot; exp e1; relop ro; exp e2]
| BinE (ot, e1, bo, e2) -> "BinE" $$ [operator_type !ot; exp e1; binop bo; exp e2]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whitespace only

| RelE (ot, e1, ro, e2) -> "RelE" $$ [operator_type !ot; exp e1; relop ro; exp e2]
| TupE es -> "TupE" $$ List.map exp es
| ProjE (e, i) -> "ProjE" $$ [exp e; Atom (string_of_int i)]
| ObjE (s, i, efs) -> "ObjE" $$ [obj_sort s; id i] @ List.map exp_field efs
Expand Down Expand Up @@ -49,6 +49,7 @@ and pat p = match p.it with
| SignP (uo, l) -> "SignP" $$ [unop uo ; lit !l]
| OptP p -> "OptP" $$ [pat p]
| AltP (p1,p2) -> "AltP" $$ [pat p1; pat p2]
| ParP p -> "ParP" $$ [pat p]

and lit (l:lit) = match l with
| NullLit -> Atom "NullLit"
Expand Down
2 changes: 1 addition & 1 deletion src/check_ir.ml
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,7 @@ and check_block_exps env t decs at =
| [dec] ->
check_dec env dec;
check env at (T.is_unit t || T.sub (typ dec) t)
"declaration does not produce expect type"
"declaration does not produce expected type"
| dec::decs' ->
check_dec env dec;
check_block_exps env t decs' at
Expand Down
3 changes: 2 additions & 1 deletion src/coverage.ml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ let rec match_pat ctxt desc pat t sets =
| AltP (pat1, pat2) ->
sets.alts <- AtSet.add pat1.at (AtSet.add pat2.at sets.alts);
match_pat (InAlt1 (ctxt, pat1.at, pat2, t)) desc pat1 t sets
| AnnotP (pat1, _) ->
| AnnotP (pat1, _)
| ParP pat1 ->
match_pat ctxt desc pat1 t sets

and match_lit ctxt desc v t sets =
Expand Down
3 changes: 2 additions & 1 deletion src/definedness.ml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ and pat msgs p : fd = match p.it with
| WildP -> (M.empty, S.empty)
| VarP i -> (M.empty, S.singleton i.it)
| TupP ps -> pats msgs ps
| AnnotP (p, t) -> pat msgs p
| AnnotP (p, _)
| ParP p -> pat msgs p
| LitP l -> (M.empty, S.empty)
| SignP (uo, l) -> (M.empty, S.empty)
| OptP p -> pat msgs p
Expand Down
16 changes: 12 additions & 4 deletions src/desugar.ml
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,20 @@ and decs ds =
| _ -> (phrase' dec' d) :: (decs ds)

and dec d = phrase' dec' d
and dec' at n d = match d with
and dec' at n d =
let fix_unary p = match p.it, p.note with

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, it looks fishy to me to only apply such a special case in one place. If the solution is to essentially remove unary tuples then the right place to do this normalisation is in the parser, in the productions for tuples, so that the AST never contains a unary tuple type/exp/pat in the first place.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I wrote this, my intention was not to equate values with the unary tuple formed from them. I was bending to the (empirical) observation that the later stages (i.e. coverage analyser, interpreter) seem to insist that single arguments should not be wrapped by an extra layer of TupP. Please note that at this point both the type system as well as the runtime values do have the notion of unary tuple, the only place where I strip them is here for unary function arguments, with the fix_unary function. The separation I chose arose from the consideration, that I wanted to make clear that we first lower to a canonical form (i.e. TupP around arguments) and then fix the single argument case that causes trouble downstream. As already discussed with @crusso I plan to write a new story for cleaning up the status quo. For this PR I'll fold the two levels into one.

| S.TupP [p1], Type.Tup [n] -> { p with it = p1.it; note = n }
| _ -> p in
let param p = match p.it with

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: avoid nesting functions unless they actually close over something.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, this seems to be a stylistic issue. Are there any such conventions written down somewhere? My notion used to be "define utilities in the closest possible scope", which I do here. I'll try to lift this out to top-level when folding the two functions into one. I wonder however, if that will export the function from the module?

| S.ParP p1 -> pat (fix_unary { p with it = S.TupP [p1]; note = Type.Tup [p.note] })

@crusso crusso Feb 27, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the S.ParP p1 case, would it not be make more sense to just continue with pat p1 - or is there something more subtle going on here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed with @crusso, not sure why there are two levels of special casing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fold. There is nothing subtle going on besides "separation of concerns". See my previous explanation.

| _ -> pat (fix_unary p)
in match d with
| S.ExpD e -> I.ExpD (exp e)
| S.LetD (p, e) -> I.LetD (pat p, exp e)
| S.VarD (i, e) -> I.VarD (i, exp e)
| S.FuncD (s, i, tbs, p, ty, e) ->
let cc = Value.call_conv_of_typ n.S.note_typ in
I.FuncD (cc, i, typ_binds tbs, pat p, ty.note, exp e)
I.FuncD (cc, i, typ_binds tbs, param p, ty.note, exp e)
| S.TypD (id, typ_bind, t) ->
let c = Lib.Option.value id.note in
I.TypD c
Expand All @@ -199,7 +206,7 @@ and dec' at n d = match d with
T.promote (T.open_ inst rng)
| _ -> assert false
in
I.FuncD (cc, id', typ_binds tbs, pat p, obj_typ, (* TBR *)
I.FuncD (cc, id', typ_binds tbs, param p, obj_typ, (* TBR *)
{ it = obj at s (Some id') self_id es obj_typ;
at = at;
note = { S.note_typ = obj_typ; S.note_eff = T.Triv } })
Expand All @@ -222,7 +229,8 @@ and pat' = function
| S.TupP ps -> I.TupP (pats ps)
| S.OptP p -> I.OptP (pat p)
| S.AltP (p1, p2) -> I.AltP (pat p1, pat p2)
| S.AnnotP (p, _) -> pat' p.it
| S.AnnotP (p, _)
| S.ParP p -> pat' p.it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rossberg @crusso @nomeata I think this desugaring is buggy.
I think we can agree that

func p(()) {};
func q((),) {};

should have identical desugarings, because they both have arity 1 and both match unit. However currently they desugar thus:

  (FuncD ( 1 -> 0) p (TupP) () (BlockE ()))
  (FuncD ( 1 -> 0) q (TupP (TupP)) () (BlockE ()))

which means different calling conventions. So this is a no-go. I fixed this here as

  | S.AnnotP (p, _) -> pat' p.it
  | S.ParP p -> pat' (S.TupP [p])

and now I get the correct

  (FuncD ( 1 -> 0) p (TupP (TupP)) () (BlockE ()))
  (FuncD ( 1 -> 0) q (TupP (TupP)) () (BlockE ()))

I only started looking at the desugarings late yesterday, so this slipped my attention. Then of course, ParP desugaring like this must only happen at top-level, which brings us back to square 1. If I am not totally mistaken, this means also that parser has to be more careful not inserting ParP but on toplevel. This was my big disconnect all the way.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be fixed in multiple ways, I think. I think the simplest is to only apply your fix above for toplevel parameter patterns, i.e., tweak desugaring such that you invoke an auxiliary:

let param = function
  | ParP p -> TupP [pat p]
  | p -> pat p

and invoke that in place of directly calling pat for func and class (replacing your fix above).

@crusso crusso Feb 22, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this is kind of why I was wondering if we could just have a separate production for argument patterns that retains ParP and leaves the existing rules unchanged. The ParP could only occur outermost in argument patterns.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but since this arguably is an issue of typing not parsing, handling it in the parser seems less appropriate to me.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed why don't we define ast node:

arg_pat = ParA pat | PatA pat
pat = ...

and leave the exisiting pat type ParP free.

with arg_pat used for class and function arguments.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rossberg That's what I would have expected in a language with multi-arity functions.
I see currently:

  (ExpD (CallE ( 1 -> 0) (VarE g) (TupE)))
  (ExpD (CallE ( 1 -> 0) (VarE h) (TupE)))

where g and h are defined thus (using my current desugaring):

  (FuncD ( 1 -> 0) g (TupP (TupP)) () (BlockE ()))
  (FuncD ( 1 -> 0) h (VarP u) () (BlockE ()))

and the IR-interpreter chokes on the call to g but is accepting the call to h.
(The AST interpreter has no problems here, but that might be caused by shallow testing.)

At one point @nomeata suggested to consider the function's arity. When 1, we could adapt the convention that there is no wrapping of the argument in TupP, but for all other arities the argument patterns are enclosed in a TupP. This could be sensible to avoid too much breakage while we are transitioning to the "always TupP schema" you are proposing. Anyway, in the long run we need a clean specification that the execution engines/passes can adhere to.

@crusso crusso Feb 22, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gabor What is the source for g and h? I'm hoping there will be a type error with my recent fix.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@crusso from test/run/empty-tup-args.as

func g(()) {};
func h(u:()) {};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could even go a step further and make the inner pattern list part of the FuncD itself in the IR, corresponding to what we do for function types.

This would simplify the backend (it currently matches tuple patterns directly, but has to recreate the tuple if there is a different kind of pattern. Annoying logic to maintain).

@crusso crusso Feb 27, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This make sense to me, but as a separate PR


and prog p = (decs p.it,
{ I.has_await = true
Expand Down
15 changes: 9 additions & 6 deletions src/interpret.ml
Original file line number Diff line number Diff line change
Expand Up @@ -430,9 +430,10 @@ and declare_pat pat : val_env =
| WildP | LitP _ | SignP _ -> V.Env.empty
| VarP id -> declare_id id
| TupP pats -> declare_pats pats V.Env.empty
| OptP pat1 -> declare_pat pat1
| AltP (pat1, pat2) -> declare_pat pat1
| AnnotP (pat1, _typ) -> declare_pat pat1
| OptP pat1
| AltP (pat1, _) (* both have empty binders *)
| AnnotP (pat1, _)
| ParP pat1 -> declare_pat pat1

and declare_pats pats ve : val_env =
match pats with
Expand Down Expand Up @@ -461,7 +462,8 @@ and define_pat env pat v =
trap pat.at "value %s does not match pattern" (V.string_of_val v)
| _ -> assert false
)
| AnnotP (pat1, _typ) -> define_pat env pat1 v
| AnnotP (pat1, _)
| ParP pat1 -> define_pat env pat1 v

and define_pats env pats vs =
List.iter2 (define_pat env) pats vs
Expand Down Expand Up @@ -507,8 +509,9 @@ and match_pat pat v : val_env option =
| None -> match_pat pat2 v
| some -> some
)
| AnnotP (pat1, _typ) ->
match_pat pat1 v
| AnnotP (pat1, _)
| ParP pat1 ->
match_pat pat1 v

and match_pats pats vs ve : val_env option =
match pats, vs with
Expand Down
8 changes: 4 additions & 4 deletions src/operator.ml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ let word_unop fword8 fword16 fword32 fword64 = function
| T.Word16 -> fun v -> Word16 (fword16 (as_word16 v))
| T.Word32 -> fun v -> Word32 (fword32 (as_word32 v))
| T.Word64 -> fun v -> Word64 (fword64 (as_word64 v))
| _ -> raise (Invalid_argument "unop")
| _ -> raise (Invalid_argument "word_unop")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these unrelated changes that snuck into the branch? I wouldn't bother reverting, just wanted to draw your attention...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer reverting that change, since word_unop is an implementation detail of the module, and only unop is meaningful outside.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am happy to revert. I did this because there are two raise (Invalid_argument "unop") defaults in that file, and when I made an attempt to always wrap ParP around inner patterns I got this exception. Here is the dump:

switch: [tc] [run] [run-low] [run-ir] [wasm]
--- switch.tc (expected)
+++ switch.tc (actual)
@@ -1,6 +1,4 @@
-switch.as:87.33-87.34: warning, this pattern is never matched
-switch.as:75.11-77.2: warning, the cases in this switch do not cover all possible values
-switch.as:81.3-81.14: warning, the cases in this switch do not cover all possible values
-switch.as:87.3-87.40: warning, the cases in this switch do not cover all possible values
-switch.as:92.11-94.2: warning, the cases in this switch do not cover all possible values
-switch.as:97.11-99.2: warning, the cases in this switch do not cover all possible values
+(unknown location): internal error, Invalid_argument("unop")
+
+Last environment:
+

It is kind-of hard to figure which function threw, when two are throwing the same thing :-)
(Cursory debugging of the issue did not give any helpful clues, only that it is not word_unop who is throwing.)

Anyway, if we go forward (with a distinct PR) to equate unary tuples and values in the parser, then the ParP wrapping will be moot anyway, so I prefer to leave the parser as-is for now.

Also my (not pushed) experiment to always add ParP resulted in a bunch of changed source locations (parens around AltP) making the reporting less precise:

-coverage.as:5.8-5.14: warning, this pattern does not cover all possible values
+coverage.as:5.7-5.15: warning, this pattern does not cover all possible values

I could fix that by inheriting the inner srcloc, but I don't see the benefit.


let num_unop fint fword8 fword16 fword32 fword64 ffloat = function
| T.Int -> fun v -> Int (fint (as_int v))
Expand All @@ -37,14 +37,14 @@ let unop t op =

let text_binop ftext = function
| T.Text -> fun v1 v2 -> Text (ftext (as_text v1) (as_text v2))
| _ -> raise (Invalid_argument "binop")
| _ -> raise (Invalid_argument "text_binop")

let word_binop fword8 fword16 fword32 fword64 = function
| T.Word8 -> fun v1 v2 -> Word8 (fword8 (as_word8 v1) (as_word8 v2))
| T.Word16 -> fun v1 v2 -> Word16 (fword16 (as_word16 v1) (as_word16 v2))
| T.Word32 -> fun v1 v2 -> Word32 (fword32 (as_word32 v1) (as_word32 v2))
| T.Word64 -> fun v1 v2 -> Word64 (fword64 (as_word64 v1) (as_word64 v2))
| _ -> raise (Invalid_argument "binop")
| _ -> raise (Invalid_argument "word_binop")

let num_binop fnat fint fword8 fword16 fword32 fword64 ffloat = function
| T.Nat -> fun v1 v2 -> Int (fnat (as_int v1) (as_int v2))
Expand Down Expand Up @@ -82,7 +82,7 @@ let word_relop fword8 fword16 fword32 fword64 = function
| T.Word16 -> fun v1 v2 -> Bool (fword16 (as_word16 v1) (as_word16 v2))
| T.Word32 -> fun v1 v2 -> Bool (fword32 (as_word32 v1) (as_word32 v2))
| T.Word64 -> fun v1 v2 -> Bool (fword64 (as_word64 v1) (as_word64 v2))
| _ -> raise (Invalid_argument "relop")
| _ -> raise (Invalid_argument "word_relop")

let num_relop fnat fint fword8 fword16 fword32 fword64 ffloat = function
| T.Nat -> fun v1 v2 -> Bool (fnat (as_int v1) (as_int v2))
Expand Down
5 changes: 4 additions & 1 deletion src/parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,10 @@ pat_nullary :
| l=lit
{ LitP(ref l) @! at $sloc }
| LPAR p=pat RPAR
{ p }
{ match p.it with

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need the case distinction, just do { ParP p @! at $sloc }.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I don't quite understand why you only preserve the ParP around TupP - unless you somewhere pattern match on ParP(Tup(P)) and want to avoid having to dig through several nested ParP(ParP...(TupP(...)))?

@ggreif ggreif Feb 28, 2019

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to get away with the distinction, but it caused the

+(unknown location): internal error, Invalid_argument("unop")

failure. I have analysed it and it originates from:

 let x2 : Int = switch (-3) {
   case (0) 0;
   case (-1) 2; // <<< problem
   case (-3) 1;
   case (x) x;
 };

so the unop is -. The switch expression case legs end up with (ParP ...). There is a bug somewhere handling this case. Seems like unop does not handle Type.Pre.
Note that the parse tree now contains all the extra ParPs:

  (LetD
    (VarP x2)
    (AnnotE
      (SwitchE
        (UnE ??? NegOp (LitE (PreLit 3 Nat)))
        (case (ParP (LitP (PreLit 0 Nat))) (LitE (PreLit 0 Nat)))
        (case (ParP (SignP NegOp (PreLit 1 Nat))) (LitE (PreLit 2 Nat)))
        (case (ParP (SignP NegOp (PreLit 3 Nat))) (LitE (PreLit 1 Nat)))
        (case (ParP (VarP x)) (VarE x))
      )
      (VarT Int)
    )
  )

Update: cracked it. infer_pat' should always call infer_pat when descending, but I was calling infer_pat' from the PatP case, thus not updating references.

Also, in the desugarer I am assuming that ParP only wraps (TupP ...). See the param (and fix_unary conversation above). The new param is indifferent.

The other reason for not using { ParP p @! at $sloc } is the loss of precision when pointing out non-coverage warnings. I explained this in my previous comment.

| TupP _ -> { p with it = ParP p; at = at $sloc }
| _ -> p
}
| LPAR ps=seplist1(pat_bin, COMMA) RPAR
{ TupP(ps) @! at $sloc }

Expand Down
1 change: 1 addition & 0 deletions src/syntax.ml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ and pat' =
| OptP of pat (* option *)
| AltP of pat * pat (* disjunctive *)
| AnnotP of pat * typ (* type annotation *)
| ParP of pat (* parenthesis *)
(*
| AsP of pat * pat (* conjunctive *)
| ObjP of pat_field list (* object *)
Expand Down
11 changes: 9 additions & 2 deletions src/typing.ml
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,8 @@ and infer_pat' env pat : T.typ * val_env =
| AnnotP (pat1, typ) ->
let t = check_typ env typ in
t, check_pat env t pat1
| ParP pat1 ->
infer_pat env pat1

and infer_pats at env pats ts ve : T.typ list * val_env =
match pats with
Expand Down Expand Up @@ -798,6 +800,8 @@ and check_pat' env t pat : val_env =
if ve1 <> T.Env.empty || ve2 <> T.Env.empty then
error env pat.at "variables are not allowed in pattern alternatives";
T.Env.empty
| ParP pat1 ->
check_pat' env t pat1
| _ ->
let t', ve = infer_pat env pat in
if not (T.sub t t') then
Expand Down Expand Up @@ -848,7 +852,9 @@ and pub_pat pat xs : region T.Env.t * region T.Env.t =
| TupP pats -> List.fold_right pub_pat pats xs
| AltP (pat1, _)
| OptP pat1
| AnnotP (pat1, _) -> pub_pat pat1 xs
| AnnotP (pat1, _)
| ParP pat1
-> pub_pat pat1 xs

and pub_typ_id id (xs, ys) : region T.Env.t * region T.Env.t =
(T.Env.add id.it id.at xs, ys)
Expand Down Expand Up @@ -1091,7 +1097,8 @@ and gather_pat env ve pat : val_env =
List.fold_left (gather_pat env) ve pats
| AltP (pat1, _)
| OptP pat1
| AnnotP (pat1, _) ->
| AnnotP (pat1, _)
| ParP pat1 ->
gather_pat env ve pat1

and gather_id env ve id : val_env =
Expand Down
6 changes: 6 additions & 0 deletions test/run/AST-56.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
func p((),) {};
func p1(()) {};
func p2((())) {};
func p3(((()))) {};

let ps : [((),) -> ()] = [p, p1, p2, p3];
84 changes: 81 additions & 3 deletions test/run/empty-tup-args.as
Original file line number Diff line number Diff line change
@@ -1,9 +1,87 @@
func f() {};
func g(()) {};
func g1((())) {}; // only top-level parenthesis signifies arity
func h(u:()) {};
func h1((u:())) {}; // dito, intermediate parentheses are redundant

let _ = f : () -> ();
let _ = g : () -> ();
let _ = g : (()) -> ();
let _ = h : (()) -> ();

let _ : [() -> ()] = [f];
let gh : [(()) -> ()] = [g, g1, h, h1];

f();
g();
h();
g(); // accepted, because "At calls there is an implicit coercion
// between tuples and n-ary arguments"
h(); // dito
gh[0](); // dito
gh[1](); // dito

// Correct:
g(());
h(());
h1(());
gh[0](());
gh[1](());
Comment thread
ggreif marked this conversation as resolved.
gh[2](());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice tests! Would it make sense to add a couple of negative ones in test/fail too, eg distinguishing unary functions on pairs from binary functions etc

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do a little follow-on for this, as I actually have a few stashed tests that fail lying around.

gh[3](());

func j(a:Int) {};
func k(a:(Int)) {};
func l(a:((Int))) {};

let jkl : [Int -> ()] = [j, k, l];

j(5);
k(8);
l(13);
jkl[1](21);

func m((a:Int, b:Bool)) {};
func m1((a:Int, b:Bool,)) {};
let ms : [((Int, Bool)) -> ()] = [m, m1];

func n(a:Int, b:Bool) {};
func n1(a:Int, b:Bool,) {};

let ns : [(Int, Bool) -> ()] = [n, n1];

func o((a:Int,)) {};
let os : [((Int,)) -> ()] = [o];

// class tests, these have the same argument regime
class F() {};
class G(()) {};
class H(a:()) {};
class G1((())) {};
class H1((u:())) {};
let _ : [() -> F] = [F];
let _ : [(()) -> G] = [G];
let _ : [(()) -> G1] = [G1];
let _ : [(()) -> H] = [H];
let _ : [(()) -> H1] = [H1];

func annih<A,B>(f : A -> B) : (A -> ()) { func (a : A) { ignore (f a) } };
let _ : [(()) -> ()] = [annih<(),G> G, annih<(),G1> G1, annih<(),H> H, annih<(),H1> H1];


// test that parens are not significant deeper into the pattern,
// even around tuples

func p(a:Int, () ) {};
func q(a:Int, (())) {};
func r(a:Int, (b:Int,) ) {};
func s(a:Int, ((b:Int,))) {};
let _ : [(Int, ()) -> ()] = [p, q];
let _ : [(Int, (Int,)) -> ()] = [r, s];

// redundant parens for anonymous functions

let _ = (func (a:Nat, ()) : Nat { a }) (42, ());
let _ = (func ((a:Nat, (()))) : Nat { a }) (42, ());

// test that switch expressions also behave correctly with redundant parens

let _ = switch (42,) { case (3,) 3; case ((5,)) 5; case (a,) a};
let _ = switch (42,) { case (3,) 3; case ((5,)) 5; case (((a,))) a};