Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
9 changes: 5 additions & 4 deletions src/arrange.ml
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ let rec exp e = match e.it with
| VarE i -> "VarE" $$ [id i]
| 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
| DotE (e, sr, n) -> "DotE" $$ [exp e; obj_sort' !sr; name n]
| DotE (e, sr, n) -> "DotE" $$ [exp e; obj_sort' !sr; name n]
| AssignE (e1, e2) -> "AssignE" $$ [exp e1; exp e2]
| ArrayE (m, es) -> "ArrayE" $$ [mut m] @ List.map exp es
| IdxE (e1, e2) -> "IdxE" $$ [exp e1; exp e2]
| CallE (e1, ts, e2) -> "CallE" $$ [exp e1] @ List.map typ ts @ [exp e2]
| BlockE (ds, ot) -> "BlockE" $$ List.map dec ds @ [operator_type (!ot)]
| BlockE (ds, ot) -> "BlockE" $$ List.map dec ds @ [operator_type (!ot)]
| NotE e -> "NotE" $$ [exp e]
| AndE (e1, e2) -> "AndE" $$ [exp e1; exp e2]
| OrE (e1, e2) -> "OrE" $$ [exp e1; exp e2]
Expand Down Expand Up @@ -48,6 +48,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
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 ce ctxt desc pat t sets =
| AltP (pat1, pat2) ->
sets.alts <- AtSet.add pat1.at (AtSet.add pat2.at sets.alts);
match_pat ce (InAlt1 (ctxt, pat1.at, pat2, t)) desc pat1 t sets
| AnnotP (pat1, _) ->
| AnnotP (pat1, _)
| ParP pat1 ->
match_pat ce ctxt desc pat1 t sets

and match_lit ce 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 @@ -126,7 +126,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
1 change: 1 addition & 0 deletions src/desugar.ml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ and pat' = function
| S.OptP p -> I.OptP (pat p)
| S.AltP (p1, p2) -> I.AltP (pat p1, pat p2)
| S.AnnotP (p, _) -> pat' p.it
| 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
7 changes: 5 additions & 2 deletions src/interpret.ml
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ and declare_pat pat : val_env =
| OptP pat1 -> declare_pat pat1

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.

this could be folded into the lines 437,438

| AltP (pat1, pat2) -> declare_pat pat1

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.

Not my change, but this looks a bit weird, pat2 is not declared.

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.

It's probably ok because neither pat1 nor pat2 is allowed to bind any variables... In fact, it could probably just return () without declaring pat1 either (although you'd need to double check that's the case….)

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.

Both patterns concurrently bind the same set of variables (currently empty), so it would be wrong to declare them twice.

| AnnotP (pat1, _typ) -> declare_pat pat1
| ParP pat1 -> declare_pat pat1

and declare_pats pats ve : val_env =
match pats with
Expand Down Expand Up @@ -464,6 +465,7 @@ and define_pat env pat v =
| _ -> assert false
)
| AnnotP (pat1, _typ) -> define_pat env pat1 v
| ParP pat -> define_pat env pat v

and define_pats env pats vs =
List.iter2 (define_pat env) pats vs
Expand Down Expand Up @@ -509,8 +511,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
26 changes: 21 additions & 5 deletions src/parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -475,18 +475,34 @@ exp_field :

(* Patterns *)

pat_nullary :
(* pat_top is needed because top-level parentheses around tuples (unit, 1-ary, pair, etc.)
are significant in function arguments, signifying a unary function which matches on a
first-class (i.e. consed) tuple *)
pat_top :

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.

Explain why we do this

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.

Done.

Comment thread
ggreif marked this conversation as resolved.
Outdated
| LPAR p=pat RPAR
{ match p.it with
| TupP _ -> { p with it = ParP p }
| _ -> p
}
| p=pat_simple
{ p }

pat_simple :
| UNDERSCORE
{ WildP @! at $sloc }
| x=id
{ VarP(x) @! at $sloc }
| l=lit
{ LitP(ref l) @! at $sloc }
| LPAR p=pat RPAR
{ p }
| LPAR ps=seplist1(pat_bin, COMMA) RPAR
{ TupP(ps) @! at $sloc }

pat_nullary :
| LPAR p=pat RPAR
{ p }
| p=pat_simple
{ p }

pat_un :
| p=pat_nullary
{ p }
Expand Down Expand Up @@ -535,7 +551,7 @@ dec_nonvar :
{ (fd s (xf "func" $sloc)).it @? at $sloc }
| TYPE x=con_id tps=typ_params_opt EQ t=typ
{ TypD(x, tps, t) @? at $sloc }
| s=obj_sort_opt CLASS xf=id_opt tps=typ_params_opt p=pat_nullary xefs=class_body
| s=obj_sort_opt CLASS xf=id_opt tps=typ_params_opt p=pat_top xefs=class_body
{ let x, efs = xefs in
let efs' =
if s.it = Type.Object Type.Local
Expand Down Expand Up @@ -572,7 +588,7 @@ dec :
LetD(p, ObjE(s, x, efs') @? r) @? r }

func_dec :
| tps=typ_params_opt p=pat_nullary rt=return_typ? fb=func_body
| tps=typ_params_opt p=pat_top rt=return_typ? fb=func_body
{ let t = Lib.Option.get rt (TupT([]) @! no_region) in
(* This is a hack to support local func declarations that return a computed async.
These should be defined using RHS syntax EQ e to avoid the implicit AsyncE introduction
Expand Down
1 change: 1 addition & 0 deletions src/syntax.ml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ and pat' =
| OptP of pat (* option *)
| AltP of pat * pat (* disjunctive *)
| AnnotP of pat * typ (* type annotation *)
| ParP of pat (* toplevel parenthesis *)
Comment thread
ggreif marked this conversation as resolved.
Outdated
(*
| AsP of pat * pat (* conjunctive *)
| ObjP of pat_field list (* object *)
Expand Down
4 changes: 3 additions & 1 deletion src/typing.ml
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,8 @@ and gather_pat env ve0 pat : val_env =
| AltP (pat1, pat2) ->
go ve pat1
| OptP pat1
| AnnotP (pat1, _) ->
| AnnotP (pat1, _)
| ParP pat1 ->
go ve pat1
in T.Env.adjoin ve0 (go T.Env.empty pat)

Expand Down Expand Up @@ -761,6 +762,7 @@ 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
Comment thread
ggreif marked this conversation as resolved.
Outdated

and infer_pats at env pats ts ve : T.typ list * val_env =
match pats with
Expand Down
60 changes: 57 additions & 3 deletions test/run/empty-tup-args.as
Original file line number Diff line number Diff line change
@@ -1,9 +1,63 @@
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(());
gh[0](());
gh[1](());
Comment thread
ggreif marked this conversation as resolved.

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];