Commit 7b443bb1 authored by kanetw's avatar kanetw Committed by Austin Seipp

Improve error messages for ambiguous type variables

Improved error messages are only printed when the old message would be
"No instance for...", since they're not as helpful for "Could not deduce..."

No special test case as error messages are tested by other tests already.
Signed-off-by: kanetw's avatarDavid Kraeutmann <kane@kane.cx>

Reviewed By: austin, goldfire

Differential Revision: https://phabricator.haskell.org/D1182

GHC Trac Issues: #10733
parent ea4df12f
......@@ -1062,7 +1062,7 @@ mkEqInfoMsg ct ty1 ty2
mb_fun2 = isTyFun_maybe ty2
ambig_msg | isJust mb_fun1 || isJust mb_fun2
= snd (mkAmbigMsg ct)
= snd (mkAmbigMsg False ct)
| otherwise = empty
tyfun_msg | Just tc1 <- mb_fun1
......@@ -1521,23 +1521,50 @@ mk_dict_err ctxt (ct, (matches, unifiers, unsafe_overlapped))
givens = getUserGivens ctxt
all_tyvars = all isTyVarTy tys
cannot_resolve_msg :: Ct -> SDoc -> SDoc
cannot_resolve_msg ct binds_msg
= vcat [ addArising orig no_inst_msg
= vcat [ no_inst_msg
, nest 2 extra_note
, vcat (pp_givens givens)
, ppWhen (has_ambig_tvs && not (null unifiers && null givens))
(vcat [ ambig_msg, binds_msg, potential_msg ])
(vcat [ ppUnless lead_with_ambig ambig_msg, binds_msg, potential_msg ])
, show_fixes (add_to_ctxt_fixes has_ambig_tvs ++ drv_fixes) ]
where
(has_ambig_tvs, ambig_msg) = mkAmbigMsg ct
orig = ctOrigin ct
potential_msg
= ppWhen (not (null unifiers) && want_potential orig) $
sdocWithDynFlags $ \dflags ->
getPprStyle $ \sty ->
pprPotentials dflags sty (ptext (sLit "Potential instances:")) unifiers
-- See Note [Highlighting ambiguous type variables]
lead_with_ambig = has_ambig_tvs && not (any isRuntimeUnkSkol ambig_tvs)
&& not (null unifiers) && null givens
(has_ambig_tvs, ambig_msg) = mkAmbigMsg lead_with_ambig ct
ambig_tvs = getAmbigTkvs ct
no_inst_msg
| lead_with_ambig
= ambig_msg <+> pprArising orig
$$ text "prevents the constraint" <+> quotes (pprParendType pred)
<+> text "from being solved."
| null givens
= addArising orig $ text "No instance for"
<+> pprParendType pred
| otherwise
= addArising orig $ text "Could not deduce"
<+> pprParendType pred
potential_msg
= ppWhen (not (null unifiers) && want_potential orig) $
sdocWithDynFlags $ \dflags ->
getPprStyle $ \sty ->
pprPotentials dflags sty potential_hdr unifiers
potential_hdr
= vcat [ ppWhen lead_with_ambig $
text "Probable fix: use a type annotation to specify what"
<+> pprQuotedList ambig_tvs <+> text "should be."
, ptext (sLit "These potential instance") <> plural unifiers
<+> text "exist:"]
-- Report "potential instances" only when the constraint arises
-- directly from the user's use of an overloaded function
......@@ -1557,10 +1584,6 @@ mk_dict_err ctxt (ct, (matches, unifiers, unsafe_overlapped))
ppr_skol (PatSkol dc _) = ptext (sLit "the data constructor") <+> quotes (ppr dc)
ppr_skol skol_info = ppr skol_info
no_inst_msg
| null givens && null matches = ptext (sLit "No instance for") <+> pprParendType pred
| otherwise = ptext (sLit "Could not deduce") <+> pprParendType pred
extra_note | any isFunTy (filterOut isKind tys)
= ptext (sLit "(maybe you haven't applied a function to enough arguments?)")
| className clas == typeableClassName -- Avoid mysterious "No instance for (Typeable T)
......@@ -1648,6 +1671,24 @@ mk_dict_err ctxt (ct, (matches, unifiers, unsafe_overlapped))
]
]
{- Note [Highlighting ambiguous type variables]
-----------------------------------------------
When we encounter ambiguous type variables (i.e. type variables
that remain metavariables after type inference), we need a few more
conditions before we can reason that *ambiguity* prevents constraints
from being solved:
- We can't have any givens, as encountering a typeclass error
with given constraints just means we couldn't deduce
a solution satisfying those constraints and as such couldn't
bind the type variable to a known type.
- If we don't have any unifiers, we don't even have potential
instances from which an ambiguity could arise.
- Lastly, I don't want to mess with error reporting for
unknown runtime types so we just fall back to the old message there.
Once these conditions are satisfied, we can safely say that ambiguity prevents
the constraint from being solved. -}
usefulContext :: ReportErrCtxt -> TcPredType -> [SkolemInfo]
usefulContext ctxt pred
= go (cec_encl ctxt)
......@@ -1814,19 +1855,19 @@ This test suggests -fprint-explicit-kinds when all the ambiguous type
variables are kind variables.
-}
mkAmbigMsg :: Ct -> (Bool, SDoc)
mkAmbigMsg ct
mkAmbigMsg :: Bool -- True when message has to be at beginning of sentence
-> Ct -> (Bool, SDoc)
mkAmbigMsg prepend_msg ct
| null ambig_tkvs = (False, empty)
| otherwise = (True, msg)
where
ambig_tkv_set = filterVarSet isAmbiguousTyVar (tyVarsOfCt ct)
ambig_tkvs = varSetElems ambig_tkv_set
ambig_tkvs = getAmbigTkvs ct
(ambig_kvs, ambig_tvs) = partition isKindVar ambig_tkvs
msg | any isRuntimeUnkSkol ambig_tkvs -- See Note [Runtime skolems]
= vcat [ ptext (sLit "Cannot resolve unknown runtime type") <> plural ambig_tvs
<+> pprQuotedList ambig_tvs
, ptext (sLit "Use :print or :force to determine these types")]
= vcat [ ptext (sLit "Cannot resolve unknown runtime type")
<> plural ambig_tvs <+> pprQuotedList ambig_tvs
, ptext (sLit "Use :print or :force to determine these types")]
| not (null ambig_tvs)
= pp_ambig (ptext (sLit "type")) ambig_tvs
......@@ -1836,6 +1877,11 @@ mkAmbigMsg ct
, sdocWithDynFlags suggest_explicit_kinds ]
pp_ambig what tkvs
| prepend_msg -- "Ambiguous type variable 't0'"
= ptext (sLit "Ambiguous") <+> what <+> ptext (sLit "variable")
<> plural tkvs <+> pprQuotedList tkvs
| otherwise -- "The type variable 't0' is ambiguous"
= ptext (sLit "The") <+> what <+> ptext (sLit "variable") <> plural tkvs
<+> pprQuotedList tkvs <+> is_or_are tkvs <+> ptext (sLit "ambiguous")
......@@ -1846,6 +1892,13 @@ mkAmbigMsg ct
| gopt Opt_PrintExplicitKinds dflags = empty
| otherwise = ptext (sLit "Use -fprint-explicit-kinds to see the kind arguments")
getAmbigTkvs :: Ct -> [Var]
getAmbigTkvs ct
= varSetElems ambig_tkv_set
where
ambig_tkv_set = filterVarSet isAmbiguousTyVar (tyVarsOfCt ct)
pprSkol :: SkolemInfo -> SrcLoc -> SDoc
pprSkol UnkSkol _
= ptext (sLit "is an unknown type variable")
......
annfail10.hs:9:1: error:
No instance for (Data a0) arising from an annotation
The type variable ‘a0’ is ambiguous
Potential instances:
Ambiguous type variable ‘a0’ arising from an annotation
prevents the constraint ‘(Data a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance (Data a, Data b) => Data (Either a b)
-- Defined in ‘Data.Data’
instance Data All -- Defined in ‘Data.Data’
......@@ -13,9 +14,10 @@ annfail10.hs:9:1: error:
In the annotation: {-# ANN f 1 #-}
annfail10.hs:9:11: error:
No instance for (Num a0) arising from the literal ‘1’
The type variable ‘a0’ is ambiguous
Potential instances:
Ambiguous type variable ‘a0’ arising from the literal ‘1’
prevents the constraint ‘(Num a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance forall (k :: BOX) (f :: k -> *) (a :: k).
Num (f a) =>
Num (Alt f a)
......
......@@ -4,7 +4,7 @@
Cannot resolve unknown runtime type ‘t1’
Use :print or :force to determine these types
Relevant bindings include it :: t1 (bound at <interactive>:5:1)
Potential instances:
These potential instances exist:
instance (Show a, Show b) => Show (Either a b)
-- Defined in ‘Data.Either’
instance Show All -- Defined in ‘Data.Monoid’
......@@ -21,7 +21,7 @@
Cannot resolve unknown runtime type ‘t1’
Use :print or :force to determine these types
Relevant bindings include it :: t1 (bound at <interactive>:7:1)
Potential instances:
These potential instances exist:
instance (Show a, Show b) => Show (Either a b)
-- Defined in ‘Data.Either’
instance Show All -- Defined in ‘Data.Monoid’
......
......@@ -4,7 +4,7 @@
Cannot resolve unknown runtime type ‘a1’
Use :print or :force to determine these types
Relevant bindings include it :: a1 (bound at <interactive>:10:1)
Potential instances:
These potential instances exist:
instance Show TyCon -- Defined in ‘Data.Typeable.Internal’
instance Show TypeRep -- Defined in ‘Data.Typeable.Internal’
instance Show Ordering -- Defined in ‘GHC.Show’
......
......@@ -67,9 +67,10 @@
In an equation for ‘j’: j = myOp 23
../../typecheck/should_run/Defer01.hs:43:10: warning:
No instance for (Num a1) arising from the literal ‘23’
The type variable ‘a1’ is ambiguous
Potential instances:
Ambiguous type variable ‘a1’ arising from the literal ‘23’
prevents the constraint ‘(Num a1)’ from being solved.
Probable fix: use a type annotation to specify what ‘a1’ should be.
These potential instances exist:
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Double -- Defined in ‘GHC.Float’
instance Num Float -- Defined in ‘GHC.Float’
......
......@@ -19,9 +19,10 @@ T4485.hs:50:15: error:
asChild b = asChild $ (genElement "foo")
T4485.hs:50:26: error:
No instance for (XMLGen m0) arising from a use of ‘genElement’
The type variable ‘m0’ is ambiguous
Potential instances:
Ambiguous type variable ‘m0’ arising from a use of ‘genElement’
prevents the constraint ‘(XMLGen m0)’ from being solved.
Probable fix: use a type annotation to specify what ‘m0’ should be.
These potential instance exist:
instance XMLGen (IdentityT m) -- Defined at T4485.hs:37:10
In the second argument of ‘($)’, namely ‘(genElement "foo")’
In the expression: asChild $ (genElement "foo")
......
overloadedlistsfail01.hs:5:8: error:
No instance for (Show a0) arising from a use of ‘print’
The type variable ‘a0’ is ambiguous
Potential instances:
Ambiguous type variable ‘a0’ arising from a use of ‘print’
prevents the constraint ‘(Show a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance [safe] Show Version -- Defined in ‘Data.Version’
instance Show Ordering -- Defined in ‘GHC.Show’
instance Show Integer -- Defined in ‘GHC.Show’
......@@ -12,9 +13,10 @@ overloadedlistsfail01.hs:5:8: error:
In an equation for ‘main’: main = print [1]
overloadedlistsfail01.hs:5:14: error:
No instance for (IsList a0) arising from an overloaded list
The type variable ‘a0’ is ambiguous
Potential instances:
Ambiguous type variable ‘a0’ arising from an overloaded list
prevents the constraint ‘(IsList a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance IsList Version -- Defined in ‘GHC.Exts’
instance IsList [a] -- Defined in ‘GHC.Exts’
In the first argument of ‘print’, namely ‘[1]’
......@@ -22,9 +24,10 @@ overloadedlistsfail01.hs:5:14: error:
In an equation for ‘main’: main = print [1]
overloadedlistsfail01.hs:5:15: error:
No instance for (Num (Item a0)) arising from the literal ‘1’
The type variable ‘a0’ is ambiguous
Potential instances:
Ambiguous type variable ‘a0’ arising from the literal ‘1’
prevents the constraint ‘(Num (Item a0))’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Double -- Defined in ‘GHC.Float’
instance Num Float -- Defined in ‘GHC.Float’
......
TH_localname.hs:3:11: error:
No instance for (Lift t0) arising from a use of ‘lift’
The type variable ‘t0’ is ambiguous
Ambiguous type variable ‘t0’ arising from a use of ‘lift’
prevents the constraint ‘(Lift t0)’ from being solved.
Relevant bindings include
y :: t0 (bound at TH_localname.hs:3:6)
x :: t0 -> ExpQ (bound at TH_localname.hs:3:1)
Potential instances:
Probable fix: use a type annotation to specify what ‘t0’ should be.
These potential instances exist:
instance (Lift a, Lift b) => Lift (Either a b)
-- Defined in ‘Language.Haskell.TH.Syntax’
instance Lift Int16 -- Defined in ‘Language.Haskell.TH.Syntax’
......
rebindable6.hs:106:17: error:
No instance for (HasSeq (IO a -> t0 -> IO b))
arising from a do statement
Ambiguous type variable ‘t0’ arising from a do statement
prevents the constraint ‘(HasSeq
(IO a -> t0 -> IO b))’ from being solved.
(maybe you haven't applied a function to enough arguments?)
The type variable ‘t0’ is ambiguous
Relevant bindings include
g :: IO (Maybe b) (bound at rebindable6.hs:104:19)
f :: IO a (bound at rebindable6.hs:104:17)
test_do :: IO a -> IO (Maybe b) -> IO b
(bound at rebindable6.hs:104:9)
Potential instances:
Probable fix: use a type annotation to specify what ‘t0’ should be.
These potential instance exist:
instance HasSeq (IO a -> IO b -> IO b)
-- Defined at rebindable6.hs:52:18
In a stmt of a 'do' block: f
......@@ -24,11 +25,12 @@ rebindable6.hs:106:17: error:
return b }
rebindable6.hs:107:17: error:
No instance for (HasFail ([Char] -> t1))
arising from a do statement
Ambiguous type variable ‘t1’ arising from a do statement
prevents the constraint ‘(HasFail
([Char] -> t1))’ from being solved.
(maybe you haven't applied a function to enough arguments?)
The type variable ‘t1’ is ambiguous
Potential instances:
Probable fix: use a type annotation to specify what ‘t1’ should be.
These potential instance exist:
instance HasFail (String -> IO a)
-- Defined at rebindable6.hs:57:18
In a stmt of a 'do' block: Just (b :: b) <- g
......@@ -43,16 +45,16 @@ rebindable6.hs:107:17: error:
return b }
rebindable6.hs:108:17: error:
No instance for (HasReturn (b -> t1))
arising from a use of ‘return’
Ambiguous type variable ‘t1’ arising from a use of ‘return’
prevents the constraint ‘(HasReturn (b -> t1))’ from being solved.
(maybe you haven't applied a function to enough arguments?)
The type variable ‘t1’ is ambiguous
Relevant bindings include
b :: b (bound at rebindable6.hs:107:23)
g :: IO (Maybe b) (bound at rebindable6.hs:104:19)
test_do :: IO a -> IO (Maybe b) -> IO b
(bound at rebindable6.hs:104:9)
Potential instances:
Probable fix: use a type annotation to specify what ‘t1’ should be.
These potential instance exist:
instance HasReturn (a -> IO a) -- Defined at rebindable6.hs:42:18
In a stmt of a 'do' block: return b
In the expression:
......
mc14.hs:14:16: error:
No instance for (Functor t0) arising from a use of ‘fmap’
The type variable ‘t0’ is ambiguous
Potential instances:
Ambiguous type variable ‘t0’ arising from a use of ‘fmap’
prevents the constraint ‘(Functor t0)’ from being solved.
Probable fix: use a type annotation to specify what ‘t0’ should be.
These potential instances exist:
instance Functor IO -- Defined in ‘GHC.Base’
instance Functor Maybe -- Defined in ‘GHC.Base’
instance Functor ((->) r) -- Defined in ‘GHC.Base’
......
holes2.hs:3:5: warning:
No instance for (Show a0) arising from a use of ‘show’
The type variable ‘a0’ is ambiguous
Potential instances:
Ambiguous type variable ‘a0’ arising from a use of ‘show’
prevents the constraint ‘(Show a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Show Ordering -- Defined in ‘GHC.Show’
instance Show Integer -- Defined in ‘GHC.Show’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
......
T4921.hs:10:9: error:
No instance for (C a0 b1) arising from a use of ‘f’
The type variables ‘b1’, ‘a0’ are ambiguous
Ambiguous type variables ‘b1’, ‘a0’ arising from a use of ‘f’
prevents the constraint ‘(C a0 b1)’ from being solved.
Relevant bindings include x :: a0 (bound at T4921.hs:10:1)
Potential instances:
Probable fix: use a type annotation to specify what ‘b1’, ‘a0’ should be.
These potential instance exist:
instance C Int Char -- Defined at T4921.hs:7:10
In the first argument of ‘fst’, namely ‘f’
In the expression: fst f
In an equation for ‘x’: x = fst f
T4921.hs:12:9: error:
No instance for (C Int b0) arising from a use of ‘f’
The type variable ‘b0’ is ambiguous
Potential instances:
Ambiguous type variable ‘b0’ arising from a use of ‘f’
prevents the constraint ‘(C Int b0)’ from being solved.
Probable fix: use a type annotation to specify what ‘b0’ should be.
These potential instance exist:
instance C Int Char -- Defined at T4921.hs:7:10
In the first argument of ‘fst’, namely ‘f’
In the expression: fst f :: Int
......
T5858.hs:11:7: error:
No instance for (InferOverloaded ([t0], [t1]))
arising from a use of ‘infer’
The type variables ‘t0’, ‘t1’ are ambiguous
Potential instances:
Ambiguous type variables ‘t0’, ‘t1’ arising from a use of ‘infer’
prevents the constraint ‘(InferOverloaded
([t0], [t1]))’ from being solved.
Probable fix: use a type annotation to specify what ‘t0’, ‘t1’ should be.
These potential instance exist:
instance (t1 ~ String) => InferOverloaded (t1, t1)
-- Defined at T5858.hs:8:10
In the expression: infer ([], [])
......
......@@ -5,7 +5,7 @@ T7857.hs:8:11: error:
bound by the inferred type of g :: PrintfArg t => t -> b
at T7857.hs:8:1-21
The type variable ‘r0’ is ambiguous
Potential instances:
These potential instances exist:
instance [safe] (a ~ ()) => PrintfType (IO a)
-- Defined in ‘Text.Printf’
instance [safe] (PrintfArg a, PrintfType r) => PrintfType (a -> r)
......
tcfail008.hs:3:5: error:
No instance for (Num a0) arising from the literal ‘1’
The type variable ‘a0’ is ambiguous
Ambiguous type variable ‘a0’ arising from the literal ‘1’
prevents the constraint ‘(Num a0)’ from being solved.
Relevant bindings include o :: [a0] (bound at tcfail008.hs:3:1)
Potential instances:
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Double -- Defined in ‘GHC.Float’
instance Num Float -- Defined in ‘GHC.Float’
......
tcfail040.hs:19:5: error:
No instance for (ORD a0) arising from a use of ‘<<’
The type variable ‘a0’ is ambiguous
Potential instances:
Ambiguous type variable ‘a0’ arising from a use of ‘<<’
prevents the constraint ‘(ORD a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instance exist:
instance ORD (a -> b) -- Defined at tcfail040.hs:17:10
In the first argument of ‘(===)’, namely ‘(<<)’
In the expression: (<<) === (<<)
......
tcfail043.hs:38:17: error:
No instance for (Ord_ a0) arising from a use of ‘gt’
The type variable ‘a0’ is ambiguous
Ambiguous type variable ‘a0’ arising from a use of ‘gt’
prevents the constraint ‘(Ord_ a0)’ from being solved.
Relevant bindings include
bs :: [a0] (bound at tcfail043.hs:38:8)
a :: a0 (bound at tcfail043.hs:38:6)
search :: a0 -> [a0] -> Bool (bound at tcfail043.hs:37:1)
Potential instances:
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instance exist:
instance Ord_ Int -- Defined at tcfail043.hs:34:10
In the expression: gt (hd bs) a
In the expression:
......@@ -22,13 +23,14 @@ tcfail043.hs:38:17: error:
if eq a (hd bs) then True else search a (tl bs)
tcfail043.hs:40:25: error:
No instance for (Eq_ a0) arising from a use of ‘eq’
The type variable ‘a0’ is ambiguous
Ambiguous type variable ‘a0’ arising from a use of ‘eq’
prevents the constraint ‘(Eq_ a0)’ from being solved.
Relevant bindings include
bs :: [a0] (bound at tcfail043.hs:38:8)
a :: a0 (bound at tcfail043.hs:38:6)
search :: a0 -> [a0] -> Bool (bound at tcfail043.hs:37:1)
Potential instances:
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Eq_ Int -- Defined at tcfail043.hs:20:10
instance Eq_ a => Eq_ [a] -- Defined at tcfail043.hs:23:10
In the expression: eq a (hd bs)
......
......@@ -6,7 +6,7 @@ tcfail072.hs:23:13: error:
g :: (Ord p, Ord q) => AB p q -> Bool
at tcfail072.hs:22:6-38
The type variable ‘p0’ is ambiguous
Potential instances:
These potential instances exist:
instance Ord Ordering -- Defined in ‘GHC.Classes’
instance Ord Integer
-- Defined in ‘integer-gmp-1.0.0.0:GHC.Integer.Type’
......
tcfail128.hs:18:16: error:
No instance for (Data.Array.Base.MArray b0 FlatVector IO)
arising from a use of ‘thaw’
The type variable ‘b0’ is ambiguous
Potential instances:
Ambiguous type variable ‘b0’ arising from a use of ‘thaw’
prevents the constraint ‘(Data.Array.Base.MArray
b0 FlatVector IO)’ from being solved.
Probable fix: use a type annotation to specify what ‘b0’ should be.
These potential instance exist:
instance Data.Array.Base.MArray IOArray e IO
-- Defined in ‘Data.Array.Base’
In a stmt of a 'do' block: v <- thaw tmp
......
......@@ -3,9 +3,10 @@ tcfail133.hs:2:61: warning:
-XDatatypeContexts is deprecated: It was widely considered a misfeature, and has been removed from the Haskell language.
tcfail133.hs:68:7: error:
No instance for (Show r0) arising from a use of ‘show’
The type variable ‘r0’ is ambiguous
Potential instances:
Ambiguous type variable ‘r0’ arising from a use of ‘show’
prevents the constraint ‘(Show r0)’ from being solved.
Probable fix: use a type annotation to specify what ‘r0’ should be.
These potential instances exist:
instance Show Ordering -- Defined in ‘GHC.Show’
instance Show Integer -- Defined in ‘GHC.Show’
instance (Show a, Show b, Number a, Digit b) => Show (a :@ b)
......
......@@ -6,7 +6,7 @@ tcfail181.hs:17:9: error:
wog :: Monad m => t -> Something (m Bool) e
at tcfail181.hs:17:1-30
The type variable ‘m0’ is ambiguous
Potential instances:
These potential instances exist:
instance Monad IO -- Defined in ‘GHC.Base’
instance Monad Maybe -- Defined in ‘GHC.Base’
instance Monad ((->) r) -- Defined in ‘GHC.Base’
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment