From a6828173b90dbd276be593c1690aa34317c13c72 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gerg=C5=91=20=C3=89rdi?= <gergo@erdi.hu>
Date: Thu, 10 Aug 2023 09:11:41 +0100
Subject: [PATCH] If a defaulting plugin made progress, re-zonk wanteds before
 built-in defaulting

Fixes #23821.
---
 compiler/GHC/Tc/Solver.hs                     |  6 ++++
 testsuite/tests/plugins/Makefile              |  4 +++
 testsuite/tests/plugins/T23821.hs             | 12 +++++++
 testsuite/tests/plugins/all.T                 |  5 +++
 .../defaulting-plugin/DefaultInterference.hs  | 32 +++++++++++++++++++
 .../defaulting-plugin/defaulting-plugin.cabal |  2 +-
 6 files changed, 60 insertions(+), 1 deletion(-)
 create mode 100644 testsuite/tests/plugins/T23821.hs
 create mode 100644 testsuite/tests/plugins/defaulting-plugin/DefaultInterference.hs

diff --git a/compiler/GHC/Tc/Solver.hs b/compiler/GHC/Tc/Solver.hs
index a09d7b3346b7..094ee9d3d053 100644
--- a/compiler/GHC/Tc/Solver.hs
+++ b/compiler/GHC/Tc/Solver.hs
@@ -3598,6 +3598,12 @@ applyDefaultingRules wanteds
              ; return defaultedGroups
              }
 
+       -- If a defaulting plugin solves a tyvar, some of the wanteds
+       -- will have filled-in metavars by now (see #23281). So we
+       -- re-zonk to make sure the built-in defaulting rules don't try
+       -- to solve the same metavars.
+       ; wanteds <- if or plugin_defaulted then TcS.zonkWC wanteds else pure wanteds
+
        ; let groups = findDefaultableGroups info wanteds
 
        ; traceTcS "applyDefaultingRules {" $
diff --git a/testsuite/tests/plugins/Makefile b/testsuite/tests/plugins/Makefile
index 986acba47232..0811d378ae8f 100644
--- a/testsuite/tests/plugins/Makefile
+++ b/testsuite/tests/plugins/Makefile
@@ -172,6 +172,10 @@ test-defaulting-plugin:
 test-defaulting-plugin-fail:
 	-"$(TEST_HC)" $(TEST_HC_OPTS) $(ghcPluginWayFlags) -v0 test-defaulting-plugin-fail.hs -package-db defaulting-plugin/pkg.test-defaulting-plugin-fail/local.package.conf
 
+.PHONY: T23821
+T23821:
+	-"$(TEST_HC)" $(TEST_HC_OPTS) $(ghcPluginWayFlags) --make -v0 T23821.hs -package-db defaulting-plugin/pkg.test-defaulting-plugin/local.package.conf
+
 .PHONY: plugins-order
 plugins-order:
 	"$(TEST_HC)" $(TEST_HC_OPTS) $(ghcPluginWayFlags) --make -v0 plugins-order.hs -package-db plugin-recomp/pkg.plugins01/local.package.conf -fplugin ImpurePlugin -fplugin PurePlugin -fplugin-opt ImpurePlugin:First_Option -fplugin-opt PurePlugin:Second_Option -fplugin-opt PurePlugin:Second_Option_2 -fplugin FingerprintPlugin -fplugin-opt FingerprintPlugin:1
diff --git a/testsuite/tests/plugins/T23821.hs b/testsuite/tests/plugins/T23821.hs
new file mode 100644
index 000000000000..e0c3d50fc985
--- /dev/null
+++ b/testsuite/tests/plugins/T23821.hs
@@ -0,0 +1,12 @@
+{-# OPTIONS_GHC -fplugin DefaultInterference #-}
+{-# LANGUAGE ExtendedDefaultRules #-}
+module Main where
+
+class IsColor a where
+    op :: a -> ()
+
+instance IsColor (Int, Int, Int) where
+    op _ = ()
+
+main :: IO ()
+main = pure $ op (1, 2, 3)
diff --git a/testsuite/tests/plugins/all.T b/testsuite/tests/plugins/all.T
index 98c55bfc607b..4b4bb85a895b 100644
--- a/testsuite/tests/plugins/all.T
+++ b/testsuite/tests/plugins/all.T
@@ -280,6 +280,11 @@ test('test-defaulting-plugin-fail',
       pre_cmd('$MAKE -s --no-print-directory -C defaulting-plugin package.test-defaulting-plugin-fail TOP={top}')],
      makefile_test, [])
 
+test('T23821',
+     [extra_files(['defaulting-plugin/']),
+      pre_cmd('$MAKE -s --no-print-directory -C defaulting-plugin package.test-defaulting-plugin TOP={top}')],
+     makefile_test, [])
+
 test('plugins-order',
      [extra_files(['plugin-recomp/', 'plugin-recomp-test.hs']),
       pre_cmd('$MAKE -s --no-print-directory -C plugin-recomp package.plugins01 TOP={top}')
diff --git a/testsuite/tests/plugins/defaulting-plugin/DefaultInterference.hs b/testsuite/tests/plugins/defaulting-plugin/DefaultInterference.hs
new file mode 100644
index 000000000000..e4a51ce69cd1
--- /dev/null
+++ b/testsuite/tests/plugins/defaulting-plugin/DefaultInterference.hs
@@ -0,0 +1,32 @@
+module DefaultInterference(plugin) where
+
+import GHC.Driver.Plugins
+import GHC.Tc.Plugin
+import GHC.Tc.Types
+import GHC.Tc.Utils.TcType
+import GHC.Tc.Types.Constraint
+import GHC.Core.Predicate
+import GHC.Tc.Solver
+import GHC.Core.Type
+import GHC.Core.Class
+import GHC.Data.Bag
+import GHC.Builtin.Types (intTy)
+
+plugin :: Plugin
+plugin = defaultPlugin
+    { defaultingPlugin = \_ -> Just DefaultingPlugin
+        { dePluginInit = pure ()
+        , dePluginRun = \ _ -> defaultEverythingToInt
+        , dePluginStop = \ _ -> pure ()
+        }
+    }
+
+defaultEverythingToInt :: WantedConstraints -> TcPluginM [DefaultingProposal]
+defaultEverythingToInt wanteds = pure
+    [ DefaultingProposal tv [intTy] [ct]
+    | ct <- bagToList $ approximateWC True wanteds
+    , Just (cls, tys) <- pure $ getClassPredTys_maybe (ctPred ct)
+    , [ty] <- pure $ filterOutInvisibleTypes (classTyCon cls) tys
+    , Just tv <- pure $ getTyVar_maybe ty
+    , isMetaTyVar tv
+    ]
diff --git a/testsuite/tests/plugins/defaulting-plugin/defaulting-plugin.cabal b/testsuite/tests/plugins/defaulting-plugin/defaulting-plugin.cabal
index a8f69ab7a08d..60b500ac0a26 100644
--- a/testsuite/tests/plugins/defaulting-plugin/defaulting-plugin.cabal
+++ b/testsuite/tests/plugins/defaulting-plugin/defaulting-plugin.cabal
@@ -6,5 +6,5 @@ version: 0.1.0.0
 library
   default-language: Haskell2010
   build-depends: base, ghc, containers
-  exposed-modules: DefaultLifted
+  exposed-modules: DefaultLifted DefaultInterference
   ghc-options: -Wall
-- 
GitLab