Missing class method not caught at compile time, program freezes when missing method is called.
|Reported by:||RaminHAL9001||Owned by:|
|Operating System:||Linux||Architecture:||x86_64 (amd64)|
|Type of failure:||Runtime crash||Test Case:|
|Related Tickets:||#7633||Differential Rev(s):|
I created a type class where two of four functions must be satisfied for a minimal complete definition. GHC did not catch an instance where the minimal complete definition was not satisfied, the program compiled without an error or warning.
When running the compiled program it would freeze when calling the missing method. Also when running the program in GHCi and stepping up to the use of the missing method, GHCi would freeze before reaching the missing function. This made it very difficult to debug. It did not throw a "NoMethodError" exception, and there was no segment violation caught by the operating system, nor does the program terminate with the "<<loop>>" message output on the command line.
Compiling with language extensions:
-XTemplateHaskell -XScopedTypeVariables -XRankNTypes -XMultiParamTypeClasses -XFunctionalDependencies -XFlexibleInstances -XFlexibleContexts -XDeriveDataTypeable
Using compiler flags:
-prof -fprof-auto -threaded -Wall -fno-warn-name-shadowing -fno-warn-unused-do-bind -fno-warn-auto-orphans
To Reproduce the Issue
I tried to reproduce the issue with a miniature program that isolates the problem, but GHC successfully detects a problem and terminates with the "<<loop>>" message written on the command line. My program is much larger and simply froze. However the code I wrote looks similar to the following situation...
First I tried this, which gave me no problems at all:
class A x where f1 :: x f1 = f2 f2 :: x f2 = f1
We have two functions f1 and f2, if f1 is not defined it defaults to f2, if f2 is not defined it defaults to f1, so you must define either f1 or f2, or you could also define both.
Then I add two more methods:
class A x where f1 :: x f1 = f2 f2 :: x f2 = f1 g1 :: x g1 = g2 g2 :: x g2 = g1
Just like with f1 f2, the g1 g2 functions each use their counterpart to define their default method. But this caused a huge problem.
The minimal complete definition should be (f1 | f2) & (g1 | g2), but the compiler does not report any warning when I have not satisfied the minimal complete definition. Maybe one of my compiler flags disabled a would-be warning, I don't know yet. This was a red-flag to me, but I went on to test my program.
As it turns out, there was a place in my code where I forgot to write an instance for g1 or g2 but I had not realize it. I only found out when I ran my test program and it froze. No "unsatisfied method" exception, not segment violations, it just froze. Although the program was still running and consuming a small amount of CPU resources, and I could cancel with CTRL-C in the command line.