Skip to content

Commit cdba6be

Browse files
authored
Value constraint pattern parens (#2913)
* type-constrained alias patterns must be parenthesized * add more cases on value-constraint-alias * Fix assertion failure when expression and binding both have type constraints * Fix rtopIntegration test for Windows (#2914) * test(rtop): capture stderr in integration test for Windows * ci: setup tmate * test(rtop): avoid running rtop in windows 4.14.* * ci: enable 5.4.0 in windows * ci: remove action-tmate * ci: revert all changes, keep building * ci: add -preasonrtop in esy-ci * ci: update cache keys * ci: fix Windows CI failures - Disable rtopIntegration test on Windows due to lambda-term segfault when stdout is not a real console handle (ConPTY issue on GH Actions). See: ocaml-community/lambda-term#125 - Add setup-mingw step in esy-ci for Windows to provide x86_64-w64-mingw32-gcc needed to compile lambda-term C stubs. - Bump esy cache keys to v0.0.2 to avoid stale caches. * ci: replace setup-mingw action with direct choco install The egor-tensin/setup-mingw@v2 action fails on MinGW 15.2.0 because its cleanup script tries to remove libpthread.dll.a which no longer exists in UCRT-based builds. Use choco directly and add the bin directory to PATH manually. * ci: esy-ci doesn't run rtop tests on windows * test: remove comment for rtopIntegration
1 parent a712a32 commit cdba6be

File tree

9 files changed

+136
-14
lines changed

9 files changed

+136
-14
lines changed

.github/workflows/esy-ci.yml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ jobs:
3636
with:
3737
node-version: 20
3838

39+
- name: Set up MinGW (Windows)
40+
if: runner.os == 'Windows'
41+
shell: pwsh
42+
run: |
43+
choco upgrade mingw -y --no-progress
44+
echo "C:\ProgramData\mingw64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
45+
3946
- name: Install esy
4047
run: npm install -g esy@0.9.0-beta.1
4148

@@ -44,7 +51,7 @@ jobs:
4451
uses: actions/cache/restore@v4
4552
with:
4653
path: ~/.esy/source
47-
key: esy-source-${{ matrix.os }}-${{ matrix.ocaml-compiler }}-${{ hashFiles('esy.lock.json') }}
54+
key: v0.0.2-esy-source-${{ matrix.os }}-${{ matrix.ocaml-compiler }}-${{ hashFiles('esy.lock.json') }}
4855

4956
- name: Print esy cache
5057
id: print_esy_cache
@@ -57,8 +64,8 @@ jobs:
5764
path: |
5865
${{ steps.print_esy_cache.outputs.ESY_CACHE }}
5966
_export
60-
key: esy-build-${{ matrix.os }}-${{ matrix.ocaml-compiler }}-${{ hashFiles('esy.lock.json') }}
61-
restore-keys: esy-build-${{ matrix.os }}-
67+
key: v0.0.2-esy-build-${{ matrix.os }}-${{ matrix.ocaml-compiler }}-${{ hashFiles('esy.lock.json') }}
68+
restore-keys: v0.0.2-esy-build-${{ matrix.os }}-
6269

6370
- name: Install dependencies
6471
run: esy install
@@ -94,7 +101,7 @@ jobs:
94101
if: steps.global-cache.outputs.cache-hit != 'true'
95102
with:
96103
path: ~/.esy/source
97-
key: esy-source-${{ matrix.os }}-${{ matrix.ocaml-compiler }}-${{ hashFiles('esy.lock.json') }}
104+
key: v0.0.2-esy-source-${{ matrix.os }}-${{ matrix.ocaml-compiler }}-${{ hashFiles('esy.lock.json') }}
98105

99106
- name: Save dependencies cache
100107
if: steps.deps-cache.outputs.cache-hit != 'true'
@@ -103,7 +110,7 @@ jobs:
103110
path: |
104111
${{ steps.print_esy_cache.outputs.ESY_CACHE }}
105112
_export
106-
key: esy-build-${{ matrix.os }}-${{ matrix.ocaml-compiler }}-${{ hashFiles('esy.lock.json') }}
113+
key: v0.0.2-esy-build-${{ matrix.os }}-${{ matrix.ocaml-compiler }}-${{ hashFiles('esy.lock.json') }}
107114

108115
# Cleanup build cache in case dependencies have changed
109116
- name: Cleanup

.github/workflows/opam-ci.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ jobs:
3232
# OCaml >= 4.13
3333
# https://github.com/ocaml/setup-ocaml/issues/822#issuecomment-2215525942
3434
- {ocaml-compiler: '4.14.x', os: windows-latest}
35+
- {ocaml-compiler: 'ocaml-base-compiler.5.4.0', os: windows-latest}
3536

3637
runs-on: ${{ matrix.setup.os }}
3738

@@ -51,15 +52,15 @@ jobs:
5152
uses: actions/cache/restore@v4
5253
with:
5354
path: ~/.opam
54-
key: opam-${{ matrix.setup.os }}-${{ matrix.setup.ocaml-compiler }}-${{ hashFiles('*.opam') }}
55+
key: v0.0.1-opam-${{ matrix.setup.os }}-${{ matrix.setup.ocaml-compiler }}-${{ hashFiles('*.opam') }}
5556

5657
- name: Load opam cache when Windows
5758
if: runner.os == 'Windows'
5859
id: opam-cache-windows
5960
uses: actions/cache/restore@v4
6061
with:
6162
path: _opam
62-
key: opam-${{ matrix.setup.os }}-${{ matrix.setup.ocaml-compiler }}-${{ hashFiles('**.opam') }}
63+
key: v0.0.1-opam-${{ matrix.setup.os }}-${{ matrix.setup.ocaml-compiler }}-${{ hashFiles('**.opam') }}
6364

6465
- name: Install dependencies
6566
run: opam install . --deps-only
@@ -87,12 +88,12 @@ jobs:
8788
if: steps.opam-cache.outputs.cache-hit != 'true' && runner.os != 'Windows'
8889
with:
8990
path: ~/.opam
90-
key: opam-${{ matrix.setup.os }}-${{ matrix.setup.ocaml-compiler }}-${{ hashFiles('**.opam') }}
91+
key: v0.0.1-opam-${{ matrix.setup.os }}-${{ matrix.setup.ocaml-compiler }}-${{ hashFiles('**.opam') }}
9192

9293
- name: Save cache when Windows
9394
uses: actions/cache/save@v4
9495
if: steps.opam-cache-windows.outputs.cache-hit != 'true' && runner.os == 'Windows'
9596
with:
9697
path: _opam
97-
key: opam-${{ matrix.setup.os }}-${{ matrix.setup.ocaml-compiler }}-${{ hashFiles('**.opam') }}
98+
key: v0.0.1-opam-${{ matrix.setup.os }}-${{ matrix.setup.ocaml-compiler }}-${{ hashFiles('**.opam') }}
9899

src/reason-parser/reason_pprint_ast.ml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5913,8 +5913,7 @@ let createFormatter () =
59135913
(pattern :: patternAux)
59145914
in
59155915
match argsList, return.pexp_desc with
5916-
| [], Pexp_constraint (e, ct) ->
5917-
assert (vbct = None);
5916+
| [], Pexp_constraint (e, ct) when vbct = None ->
59185917
let typeLayout =
59195918
source_map
59205919
~loc:ct.ptyp_loc
@@ -6244,7 +6243,18 @@ let createFormatter () =
62446243
~loc:pat.ppat_loc
62456244
(match vbct with
62466245
| Some _ ->
6247-
self#pattern_with_precedence ~attrs:pat.ppat_attributes pat
6246+
(match pat.ppat_desc with
6247+
| Ppat_alias _ ->
6248+
makeList
6249+
~wrap:("(", ")")
6250+
[ self#pattern_with_precedence
6251+
~attrs:pat.ppat_attributes
6252+
pat
6253+
]
6254+
| _ ->
6255+
self#pattern_with_precedence
6256+
~attrs:pat.ppat_attributes
6257+
pat)
62486258
| None -> self#pattern pat)
62496259
in
62506260
let appTerms = self#unparseExprApplicationItems expr in

test/dune

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@
1515
(cram
1616
(applies_to rtopIntegration)
1717
(package rtop)
18+
(enabled_if (= %{os_type} Unix))
1819
(deps %{bin:ocamlc} %{bin:rtop}))
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* Array literal with type constraint */
2+
let x: array(int) = ([|1, 2|]: array(int));
3+
4+
/* List literal with type constraint */
5+
let x: list(int) = ([1, 2, 3]: list(int));
6+
7+
/* Tuple with type constraint */
8+
let x: (int, string) = ((1, "a"): (int, string));
9+
10+
/* Function expression with type constraint */
11+
let f: int => int = ((x => x + 1): int => int);
12+
13+
/* Record with type constraint */
14+
type t = {a: int};
15+
let x: t = ({a: 1}: t);
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
Format expression type constraints when binding also has a type constraint
2+
$ refmt ./input.re | tee formatted.re
3+
/* Array literal with type constraint */
4+
let x: array(int) = ([|1, 2|]: array(int));
5+
6+
/* List literal with type constraint */
7+
let x: list(int) = ([1, 2, 3]: list(int));
8+
9+
/* Tuple with type constraint */
10+
let x: (int, string) = (
11+
(1, "a"): (int, string)
12+
);
13+
14+
/* Function expression with type constraint */
15+
let f: int => int = (x => x + 1: int => int);
16+
17+
/* Record with type constraint */
18+
type t = {a: int};
19+
let x: t = ({ a: 1 }: t);
20+
21+
Idempotency check
22+
$ refmt ./formatted.re | tee formatted_back.re
23+
/* Array literal with type constraint */
24+
let x: array(int) = ([|1, 2|]: array(int));
25+
26+
/* List literal with type constraint */
27+
let x: list(int) = ([1, 2, 3]: list(int));
28+
29+
/* Tuple with type constraint */
30+
let x: (int, string) = (
31+
(1, "a"): (int, string)
32+
);
33+
34+
/* Function expression with type constraint */
35+
let f: int => int = (x => x + 1: int => int);
36+
37+
/* Record with type constraint */
38+
type t = {a: int};
39+
let x: t = ({ a: 1 }: t);
40+
41+
$ diff formatted.re formatted_back.re

test/rtopIntegration.t

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ always error.
1111
Given the above, we're gonna test that utop integration works by piping code
1212
into it and asserting the existence of some output.
1313

14-
$ echo "let f = a => a;" | rtop | grep -o "let f: 'a => 'a = <fun>;"
14+
$ echo "let f = a => a;" | rtop 2>&1 | grep -o "let f: 'a => 'a = <fun>;"
1515
let f: 'a => 'a = <fun>;
1616

17-
$ echo "let f = (a) => 1 + \"hi\";" | rtop | grep -o "has type"
17+
$ echo "let f = (a) => 1 + \"hi\";" | rtop 2>&1 | grep -o "has type"
1818
has type
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
let x: t = x;
2+
let (x as y): t = x;
3+
let ((x, y) as pair): t = x;
4+
let (Some(x) as opt): t = opt;
5+
let ({url, mode} as target): t = x;
6+
let ({url, mode, protocol} as target): TargetT.Safe.t =
7+
multiTarget->MultiTargetT.toIgnorableTargetT;
8+
let ({url, mode}): t = x;
9+
let ({url: u, mode: m} as target): t = x;
10+
let Foo.{url, mode}: t = x;
11+
let (Foo.{url, mode} as target): t = x;
12+
let ([x, y] as listPair): t = value;
13+
let (_ as anyValue): t = value;
14+
let ((x as y: u): t) = value;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
$ refmt ./input.re | tee formatted.re
2+
let x: t = x;
3+
let (x as y): t = x;
4+
let ((x, y) as pair): t = x;
5+
let (Some(x) as opt): t = opt;
6+
let ({ url, mode } as target): t = x;
7+
let ({ url, mode, protocol } as target): TargetT.Safe.t =
8+
multiTarget->MultiTargetT.toIgnorableTargetT;
9+
let { url, mode }: t = x;
10+
let ({ url: u, mode: m } as target): t = x;
11+
let Foo.{ url, mode } : t = x;
12+
let (Foo.{ url, mode } as target): t = x;
13+
let ([x, y] as listPair): t = value;
14+
let (_ as anyValue): t = value;
15+
let ((x as y: u): t) = value;
16+
17+
$ refmt ./formatted.re | tee formatted_back.re
18+
let x: t = x;
19+
let (x as y): t = x;
20+
let ((x, y) as pair): t = x;
21+
let (Some(x) as opt): t = opt;
22+
let ({ url, mode } as target): t = x;
23+
let ({ url, mode, protocol } as target): TargetT.Safe.t =
24+
multiTarget->MultiTargetT.toIgnorableTargetT;
25+
let { url, mode }: t = x;
26+
let ({ url: u, mode: m } as target): t = x;
27+
let Foo.{ url, mode } : t = x;
28+
let (Foo.{ url, mode } as target): t = x;
29+
let ([x, y] as listPair): t = value;
30+
let (_ as anyValue): t = value;
31+
let ((x as y: u): t) = value;
32+
33+
$ diff formatted.re formatted_back.re

0 commit comments

Comments
 (0)