Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack overflow on pattern matching against record #4327

Closed
alex35mil opened this issue Apr 24, 2020 · 20 comments
Closed

Stack overflow on pattern matching against record #4327

alex35mil opened this issue Apr 24, 2020 · 20 comments

Comments

@alex35mil
Copy link
Contributor

Reproduction: https://github.com/alexfedoseev/bs-stack-overflow
Problematic match: https://github.com/alexfedoseev/bs-stack-overflow/blob/master/src/X.re#L42-L85

$ bsb -clean-world -make-world
Cleaning... 2 files.
[3/3] Building src/X.cmj
FAILED: src/X.cmj src/X.cmi /Users/Alex/Dev/Sandboxes/bs-stack-overflow/src/X.bs.js
/Users/Alex/Dev/Sandboxes/bs-stack-overflow/node_modules/bs-platform/darwin/bsc.exe -bs-package-name bs-stack-overflow  -bs-package-output es6:src -color always -bs-suffix -I src -w +A  -o src/X.cmj src/X.reast
Fatal error: exception Stack overflow
FAILED: subcommand failed.
error Command failed with exit code 2.

It compiles with a lesser number of fields (try remove 1). But perf is not great though.

@yawaramin
Copy link
Contributor

Would be good to see how the upstream compiler does with this pattern match.

@cristianoc
Copy link
Collaborator

I don't know what this config option is, but that's what triggers the stack overflow:

  "warnings": {
    "number": "+A"
  }

@alex35mil
Copy link
Contributor Author

I’m afk but it most likely +9 which triggers it. The thing is that PM is written this way to satisfy this warning.

@yawaramin
Copy link
Contributor

You can satisfy the warning also by explicitly ignoring the rest of the fields:

| {
    additionalRequirements: Pristine | Dirty(_, _),
    _, // this explicitly ignores all other fields
  } => true

@alex35mil
Copy link
Contributor Author

Yep, but it invalidates the point of this warning, doesn't it?

@yawaramin
Copy link
Contributor

In some cases, potentially yes. However in this specific code, no; because you have already specified all record fields in the first switch branch. If you add or remove any fields from the record type, that pattern will catch the change. The second pattern doesn't need to worry about it.

@bobzhang
Copy link
Member

is this type checking failure or code generation failure?

@alex35mil
Copy link
Contributor Author

Type checking. In the repro case there is no code generation. Though I bumped into it through generated code.

@bobzhang
Copy link
Member

I can reproduce it and it seems to be a bug in type checking, can you narrow down it a little bit and file it upstream

@alex35mil
Copy link
Contributor Author

You mean it’s OCaml bug and to file an issue at https://github.com/ocaml/ocaml?

@bobzhang
Copy link
Member

bobzhang commented Apr 25, 2020

@alexfedoseev yes, it is most likely an ocaml bug, but we need some way to help them reproduce it

@alex35mil
Copy link
Contributor Author

Gotcha, yeah, will do today.

@yawaramin
Copy link
Contributor

Btw, I could not reproduce this locally on OCaml native compiler ocamlopt version 4.08. Here is the code:

type visibility = Shown | Hidden

type ('outputValue, 'message) fieldStatus =
| Pristine
| Dirty of ('outputValue, 'message) result * visibility

include struct
  type message = string

  type fieldsStatuses = {
    iaasStorageConfigurations :
      iaasStorageConfigurationFieldsStatuses array;
  }

  and iaasStorageConfigurationFieldsStatuses = {
    startDate : (int, message) fieldStatus;
    term : (int, message) fieldStatus;
    rawStorageCapacity : (int, message) fieldStatus;
    diskType : (string option, message) fieldStatus;
    connectivityMethod : (string option, message) fieldStatus;
    getRequest : (int option, message) fieldStatus;
    getRequestUnit : (string option, message) fieldStatus;
    putRequest : (int option, message) fieldStatus;
    putRequestUnit : (string option, message) fieldStatus;
    transferOut : (int option, message) fieldStatus;
    transferOutUnit : (string option, message) fieldStatus;
    region : (string option, message) fieldStatus;
    cloudType : (string option, message) fieldStatus;
    description : (string option, message) fieldStatus;
    features : (string array, message) fieldStatus;
    accessTypes : (string array, message) fieldStatus;
    certifications : (string array, message) fieldStatus;
    additionalRequirements : (string option, message) fieldStatus;
  }

  type interface = { dirty : unit -> bool }

  let useForm () = {
    dirty = fun () ->
      Array.for_all
        (fun item ->
          match item with
          | {
              additionalRequirements = Pristine;
              certifications = Pristine;
              accessTypes = Pristine;
              features = Pristine;
              description = Pristine;
              cloudType = Pristine;
              region = Pristine;
              transferOutUnit = Pristine;
              transferOut = Pristine;
              putRequestUnit = Pristine;
              putRequest = Pristine;
              getRequestUnit = Pristine;
              getRequest = Pristine;
              connectivityMethod = Pristine;
              diskType = Pristine;
              rawStorageCapacity = Pristine;
              term = Pristine;
              startDate = Pristine;
            } ->
            false
          | {
              additionalRequirements = Pristine | Dirty (_, _);
              certifications = Pristine | Dirty (_, _);
              accessTypes = Pristine | Dirty (_, _);
              features = Pristine | Dirty (_, _);
              description = Pristine | Dirty (_, _);
              cloudType = Pristine | Dirty (_, _);
              region = Pristine | Dirty (_, _);
              transferOutUnit = Pristine | Dirty (_, _);
              transferOut = Pristine | Dirty (_, _);
              putRequestUnit = Pristine | Dirty (_, _);
              putRequest = Pristine | Dirty (_, _);
              getRequestUnit = Pristine | Dirty (_, _);
              getRequest = Pristine | Dirty (_, _);
              connectivityMethod = Pristine | Dirty (_, _);
              diskType = Pristine | Dirty (_, _);
              rawStorageCapacity = Pristine | Dirty (_, _);
              term = Pristine | Dirty (_, _);
              startDate = Pristine | Dirty (_, _);
            } ->
            true)
        [||]
  }
end

@cristianoc
Copy link
Collaborator

Does it repro on 4.06.1 ?

@yawaramin
Copy link
Contributor

Cannot repro on 4.06.1:

$ ocamlopt -version
4.06.1
$ time ocamlopt pattern.ml

real	0m0.280s
user	0m0.197s
sys	0m0.065s

@alex35mil
Copy link
Contributor Author

I didn't manage to get to my laptop today. @yawaramin Just to confirm, this is with +A enabled, right?

@yawaramin
Copy link
Contributor

yawaramin commented Apr 25, 2020

Agh, sorry, you're right, my mistake:

$ time ocamlopt -w +A pattern.ml 
Segmentation fault: 11

real	0m4.868s
user	0m4.253s
sys	0m0.290s

Edit: can also repro on locally installed switch 4.09.0:

$ opam switch 4.09.0
$ time ocamlopt -w +A pattern.ml
Fatal error: exception Stack overflow

real	0m5.121s
user	0m4.464s
sys	0m0.379s

@yawaramin
Copy link
Contributor

yawaramin commented Apr 25, 2020

There is a large backtrace from the stack overflow exception as well. the first few lines are:

Fatal error: exception Stack overflow
Raised at file "parsing/builtin_attributes.ml", line 241, characters 4-13
Called from file "typing/typecore.ml", line 2290, characters 4-156
Called from file "typing/typecore.ml", line 4411, characters 10-69
Called from file "list.ml", line 92, characters 20-23

Edit: L92 is the recursive case of List.map

@bobzhang
Copy link
Member

moved to ocaml/ocaml#9498

@bobzhang
Copy link
Member

will cherry pick if the fix is not too invasive

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants