Simon Jakobi pushed to branch wip/sjakobi/T2057 at Glasgow Haskell Compiler / GHC

Commits:

12 changed files:

Changes:

  • testsuite/tests/driver/T2057/.gitignore
    1
    +work/

  • testsuite/tests/driver/T2057/Makefile
    1
    +TOP=../../..
    
    2
    +include $(TOP)/mk/boilerplate.mk
    
    3
    +include $(TOP)/mk/test.mk
    
    4
    +
    
    5
    +WORK = work
    
    6
    +PKGDB = $(WORK)/pkgdb
    
    7
    +PKGA1 = $(WORK)/pkgA1
    
    8
    +PKGA2 = $(WORK)/pkgA2
    
    9
    +PKGB = $(WORK)/pkgB
    
    10
    +APP = $(WORK)/app
    
    11
    +OUT = $(WORK)/T2057.out
    
    12
    +BASE_ID := $(shell "$(GHC_PKG)" field base id --simple-output)
    
    13
    +
    
    14
    +.PHONY: T2057 clean
    
    15
    +
    
    16
    +clean:
    
    17
    +	rm -rf $(WORK)
    
    18
    +
    
    19
    +# Dependency graph:
    
    20
    +#   pkgA is first registered from the pkgA1 source tree, where A exports f1.
    
    21
    +#   pkgB is built against this pkgA.
    
    22
    +#   We then rebuild that same package from the pkgA2 source tree, where A
    
    23
    +#   instead exports f2. Reading B.hi therefore finds an unfolding for g that
    
    24
    +#   still refers to f1, and compiling Main against pkgB should stop at the
    
    25
    +#   interface error.
    
    26
    +T2057: clean
    
    27
    +
    
    28
    +	# Create an isolated package DB and output directories for the repro.
    
    29
    +	mkdir -p '$(PKGA1)' '$(PKGA2)' '$(PKGB)' '$(APP)'
    
    30
    +	'$(GHC_PKG)' init '$(PKGDB)'
    
    31
    +
    
    32
    +	# Build and register pkgA from the pkgA1 sources.
    
    33
    +	'$(TEST_HC)' $(TEST_HC_OPTS) -v0 -package-db '$(PKGDB)' \
    
    34
    +	    -this-unit-id pkgA -O -c pkgA1/A.hs -outputdir '$(PKGA1)'
    
    35
    +	ar q '$(PKGA1)/libHSpkgA.a' '$(PKGA1)/A.o' >/dev/null 2>&1
    
    36
    +	sed "s|@BASE_ID@|$(BASE_ID)|g" pkgA1/pkg.conf.in >'$(WORK)/pkgA1.conf'
    
    37
    +	'$(GHC_PKG)' --package-db '$(PKGDB)' register '$(WORK)/pkgA1.conf' >/dev/null
    
    38
    +
    
    39
    +	# Build and register pkgB against pkgA so B.hi records the unfolding of g = f1.
    
    40
    +	'$(TEST_HC)' $(TEST_HC_OPTS) -v0 -package-db '$(PKGDB)' \
    
    41
    +	    -package pkgA -this-unit-id pkgB -O -c pkgB/B.hs \
    
    42
    +	    -outputdir '$(PKGB)'
    
    43
    +	ar q '$(PKGB)/libHSpkgB.a' '$(PKGB)/B.o' >/dev/null 2>&1
    
    44
    +	sed "s|@BASE_ID@|$(BASE_ID)|g" pkgB/pkg.conf.in >'$(WORK)/pkgB.conf'
    
    45
    +	'$(GHC_PKG)' --package-db '$(PKGDB)' register '$(WORK)/pkgB.conf' >/dev/null
    
    46
    +
    
    47
    +	# Rebuild pkgA from the pkgA2 source tree, replacing f1 with f2.
    
    48
    +	'$(TEST_HC)' $(TEST_HC_OPTS) -v0 -package-db '$(PKGDB)' \
    
    49
    +	    -this-unit-id pkgA -O -c pkgA2/A.hs -outputdir '$(PKGA2)'
    
    50
    +	ar q '$(PKGA2)/libHSpkgA.a' '$(PKGA2)/A.o' >/dev/null 2>&1
    
    51
    +	sed "s|@BASE_ID@|$(BASE_ID)|g" pkgA2/pkg.conf.in >'$(WORK)/pkgA2.conf'
    
    52
    +	'$(GHC_PKG)' --package-db '$(PKGDB)' update '$(WORK)/pkgA2.conf' >/dev/null
    
    53
    +
    
    54
    +	# Compiling Main against pkgB should now fail while loading the stale B.hi.
    
    55
    +	! '$(TEST_HC)' $(TEST_HC_OPTS) -v0 --make app/Main.hs \
    
    56
    +	    -O -fforce-recomp -package-db '$(PKGDB)' -package pkgB \
    
    57
    +	    >'$(OUT)' 2>&1 || { echo "expected compilation failure" >&2; exit 1; }
    
    58
    +
    
    59
    +	# Strip the absolute test directory prefix before comparing against T2057.stderr.
    
    60
    +	sed "s#$(CURDIR)/##g" '$(OUT)' >&2

  • testsuite/tests/driver/T2057/README.md
    1
    +`T2057` checks that GHC stops after an interface-file error instead of
    
    2
    +continuing into the linker.
    
    3
    +
    
    4
    +The test constructs a stale package dependency on purpose. The two directories
    
    5
    +`pkgA1/` and `pkgA2/` are just two source trees for the same registered
    
    6
    +package, `pkgA`.
    
    7
    +
    
    8
    +The Makefile first registers `pkgA` from `pkgA1/`, where module `A` exports
    
    9
    +`f1`. It then builds `pkgB` against that package, so `B.hi` records an
    
    10
    +unfolding `g = f1`.
    
    11
    +
    
    12
    +After that, the Makefile updates the same package `pkgA` from `pkgA2/`, where
    
    13
    +module `A` exports `f2` instead. When `Main` imports `B`, GHC has to load
    
    14
    +`B.hi`, sees the stale reference to `f1`, and should fail while loading
    
    15
    +interfaces.
    
    16
    +
    
    17
    +The golden [`T2057.stderr`](T2057.stderr) captures the fixed behaviour:
    
    18
    +diagnose the missing declaration in the stale interface and then stop with
    
    19
    +`Cannot continue after interface file error`. Any linker output would be a
    
    20
    +regression.

  • testsuite/tests/driver/T2057/T2057.stderr
    1
    +work/pkgB/B.hi
    
    2
    +Declaration for g
    
    3
    +Unfolding of g:
    
    4
    +  f1 ErrorWithoutFlag
    
    5
    +                           Can't find interface-file declaration for variable f1
    
    6
    +                             Probable cause: bug in .hi-boot file, or inconsistent .hi file
    
    7
    +                             Use -ddump-if-trace to get an idea of which file caused the error
    
    8
    +<no location info>:
    
    9
    +    Cannot continue after interface file error

  • testsuite/tests/driver/T2057/all.T
    1
    +test(
    
    2
    +  'T2057',
    
    3
    +  [ extra_files(['pkgA1', 'pkgA2', 'pkgB', 'app', 'README.md'])
    
    4
    +  , when(opsys('mingw32'), skip)
    
    5
    +  , js_skip
    
    6
    +  , wasm_skip
    
    7
    +  , ignore_stdout
    
    8
    +  ],
    
    9
    +  makefile_test,
    
    10
    +  []
    
    11
    +)

  • testsuite/tests/driver/T2057/app/Main.hs
    1
    +module Main where
    
    2
    +
    
    3
    +import B
    
    4
    +
    
    5
    +main :: IO ()
    
    6
    +main = print (g 41)

  • testsuite/tests/driver/T2057/pkgA1/A.hs
    1
    +module A (f1) where
    
    2
    +
    
    3
    +{-# INLINE f1 #-}
    
    4
    +f1 :: Int -> Int
    
    5
    +f1 x = x + 1

  • testsuite/tests/driver/T2057/pkgA1/pkg.conf.in
    1
    +name: pkgA
    
    2
    +version: 1.0
    
    3
    +id: pkgA
    
    4
    +key: pkgA
    
    5
    +exposed: True
    
    6
    +exposed-modules: A
    
    7
    +import-dirs: ${pkgroot}/pkgA1
    
    8
    +library-dirs: ${pkgroot}/pkgA1
    
    9
    +dynamic-library-dirs: ${pkgroot}/pkgA1
    
    10
    +hs-libraries: HSpkgA
    
    11
    +depends: @BASE_ID@

  • testsuite/tests/driver/T2057/pkgA2/A.hs
    1
    +module A (f2) where
    
    2
    +
    
    3
    +f2 :: Int -> Int
    
    4
    +f2 x = x + 100

  • testsuite/tests/driver/T2057/pkgA2/pkg.conf.in
    1
    +name: pkgA
    
    2
    +version: 1.0
    
    3
    +id: pkgA
    
    4
    +key: pkgA
    
    5
    +exposed: True
    
    6
    +exposed-modules: A
    
    7
    +import-dirs: ${pkgroot}/pkgA2
    
    8
    +library-dirs: ${pkgroot}/pkgA2
    
    9
    +dynamic-library-dirs: ${pkgroot}/pkgA2
    
    10
    +hs-libraries: HSpkgA
    
    11
    +depends: @BASE_ID@

  • testsuite/tests/driver/T2057/pkgB/B.hs
    1
    +module B (g) where
    
    2
    +
    
    3
    +import A
    
    4
    +
    
    5
    +{-# INLINE g #-}
    
    6
    +g :: Int -> Int
    
    7
    +g x = f1 x

  • testsuite/tests/driver/T2057/pkgB/pkg.conf.in
    1
    +name: pkgB
    
    2
    +version: 1.0
    
    3
    +id: pkgB
    
    4
    +key: pkgB
    
    5
    +exposed: True
    
    6
    +exposed-modules: B
    
    7
    +import-dirs: ${pkgroot}/pkgB
    
    8
    +library-dirs: ${pkgroot}/pkgB
    
    9
    +dynamic-library-dirs: ${pkgroot}/pkgB
    
    10
    +hs-libraries: HSpkgB
    
    11
    +depends: pkgA @BASE_ID@