#8854 closed bug (invalid)

Missing class method not caught at compile time, program freezes when missing method is called.

Reported by: RaminHAL9001 Owned by:
Priority: normal Milestone:
Component: Compiler Version: 7.6.3
Keywords: Cc:
Operating System: Linux Architecture: x86_64 (amd64)
Type of failure: Runtime crash Test Case:
Blocked By: Blocking:
Related Tickets: #7633 Differential Revisions:

Description

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.

Change History (3)

comment:1 follow-up: Changed 12 months ago by simonpj

If you specify the minimal complete definition, using the MINIMAL pragma (which is part of 7.8, but not 7.6) then it should warn. If it doesn't, that's a bug. Can you give the complete source code for a program that exhibits the bug... if we can't reproduce it we can't fix it.

Thanks

Simon

comment:2 in reply to: ↑ 1 Changed 12 months ago by RaminHAL9001

My full source code repository is here:
https://github.com/RaminHAL9001/dao

You can clone the repository and just run "make" on Linux, it should compile fine with GHC 7.6.1. It will produce two executable programs, "./dao" and "./debug/test".

In the "Dao.Interpreter" module, there is an instance:

instance HasRandGen [Comment] where { randO = return []; defaultO = return []; }

If you remove the "defaultO" instance and re-compile, that should reproduce the bug, This was the exact instance I had not defined that was causing it to freeze.

To run the test suite to cause the error to occur, change to the "debug" directory and run the "test" program. It works a bit like QuickCheck, but you can specify random seed values on the command line to produce the same random test cases every time, for example:

./test 8 14

I found seeds 8 and 14 crashed reliably, both compiled and in GHCi. But I don't know if the random number generator will produce the same test cases on your hardware as it will on mine so you may have to run a few dozen tests before you find one that freezes.

./test $(seq 1 100)

The source code is under active development, so things may change. However for the time being I don't expect there to be any changes to the part of the code that lets you reproduce the bug.

I did see the {-# MINIMAL #-} pragma in #7633, but I am using GHC 7.6.1 for now.

Replying to simonpj:

If you specify the minimal complete definition, using the MINIMAL pragma (which is part of 7.8, but not 7.6) then it should warn. If it doesn't, that's a bug. Can you give the complete source code for a program that exhibits the bug... if we can't reproduce it we can't fix it.

Thanks

Simon

comment:3 Changed 12 months ago by simonpj

  • Resolution set to invalid
  • Status changed from new to closed

Oh well, that's easy! This situation is precisely what the MINIMAL pragma is for. There is not, and never was, the slightest guarantee that anything else will work. If you get <<loop>> you are just lucky! It's not a bug in 7.6; it just a feature that doesn't exist, but now does.

Simon

Note: See TracTickets for help on using tickets.