rescript-association / reanalyze

Experimental analyses for ReScript and OCaml: globally dead values/types, exception analysis, and termination analysis.
MIT License
277 stars 20 forks source link

Fix first class module detection when a type alias is used for the module type #190

Closed LTibbetts closed 1 year ago

LTibbetts commented 1 year ago

I ran across a case where false positives were being generated for first class modules. In particular the false positive was only generated if a type alias was used for the module's type. I have recreated a minimal reproduction of the issue below and have implemented a fix for this false positive. I'm not well versed in OCaml or the overall structure of Reanalyze so any feedback would be appreciated!

Example First Class Module

module type ModuleType = {
  type t
}

module MakeWithType = (A: ModuleType): (ModuleType with type t = A.t) => {
  type t = A.t
}

let main = () => {
  let moduleA: module(ModuleType with type t = int) = module(
    MakeWithType({
      type t = int
    })
  )

  let module(A) = moduleA

  let intA: A.t = 1

  Js.log(intA)
}

main()

Example First Class Module with False Positive

module type ModuleType = {
  type t
}

module MakeWithType = (A: ModuleType): (ModuleType with type t = A.t) => {
  type t = A.t
}

type moduleAlias<'a> = module(ModuleType with type t = 'a)

let main = () => {
  // Marked as dead (transitively) due to type alias
  let moduleA: moduleAlias<int> = module(
    MakeWithType({
      type t = int
    })
  )

  // Marked as dead (false positive)
  let module(A) = moduleA

  // Live usage of module A
  let intA: A.t = 1

  Js.log(intA)
}

main()
LTibbetts commented 1 year ago

Upon further inspection this change causes more false positives to occur in our production codebase. I'll close the PR and post it as an issue instead...