summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Gundry <adam@well-typed.com>2015-11-17 15:50:33 +0100
committerBen Gamari <ben@smart-cactus.org>2015-11-17 16:58:49 +0100
commit7b962bab384e2ae85b41d30f503c3d0295b0214f (patch)
treeaa93fb85a17988e6abdcaea362fbe6ae64a478d7
parentacce37f38bc3867f86cf717694915746bb2f278e (diff)
downloadhaskell-7b962bab384e2ae85b41d30f503c3d0295b0214f.tar.gz
Implement OverloadedLabels
See https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields/OverloadedLabels for the big picture. Reviewers: goldfire, simonpj, austin, hvr, bgamari Reviewed By: simonpj, bgamari Subscribers: kosmikus, thomie, mpickering Differential Revision: https://phabricator.haskell.org/D1331
-rw-r--r--compiler/deSugar/Coverage.hs1
-rw-r--r--compiler/deSugar/DsExpr.hs1
-rw-r--r--compiler/deSugar/DsMeta.hs1
-rw-r--r--compiler/deSugar/Match.hs1
-rw-r--r--compiler/hsSyn/HsExpr.hs5
-rw-r--r--compiler/main/DynFlags.hs2
-rw-r--r--compiler/parser/Lexer.x17
-rw-r--r--compiler/parser/Parser.y9
-rw-r--r--compiler/prelude/PrelNames.hs15
-rw-r--r--compiler/rename/RnExpr.hs3
-rw-r--r--compiler/typecheck/TcEvidence.hs9
-rw-r--r--compiler/typecheck/TcExpr.hs37
-rw-r--r--compiler/typecheck/TcHsSyn.hs3
-rw-r--r--compiler/typecheck/TcRnTypes.hs3
-rw-r--r--libraries/base/GHC/OverloadedLabels.hs48
-rw-r--r--libraries/base/base.cabal1
-rw-r--r--testsuite/tests/driver/T4437.hs3
-rw-r--r--testsuite/tests/overloadedrecflds/ghci/all.T1
-rw-r--r--testsuite/tests/overloadedrecflds/ghci/overloadedlabelsghci01.script12
-rw-r--r--testsuite/tests/overloadedrecflds/ghci/overloadedlabelsghci01.stdout4
-rw-r--r--testsuite/tests/overloadedrecflds/should_fail/all.T1
-rw-r--r--testsuite/tests/overloadedrecflds/should_fail/overloadedlabelsfail01.hs15
-rw-r--r--testsuite/tests/overloadedrecflds/should_fail/overloadedlabelsfail01.stderr31
-rw-r--r--testsuite/tests/overloadedrecflds/should_run/OverloadedLabelsRun04_A.hs8
-rw-r--r--testsuite/tests/overloadedrecflds/should_run/all.T6
-rw-r--r--testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun01.hs29
-rw-r--r--testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun01.stdout3
-rw-r--r--testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun02.hs61
-rw-r--r--testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun02.stdout3
-rw-r--r--testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun03.hs21
-rw-r--r--testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun03.stdout2
-rw-r--r--testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun04.hs13
-rw-r--r--testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun04.stdout1
33 files changed, 365 insertions, 5 deletions
diff --git a/compiler/deSugar/Coverage.hs b/compiler/deSugar/Coverage.hs
index aec2a3fada..e1b45a721f 100644
--- a/compiler/deSugar/Coverage.hs
+++ b/compiler/deSugar/Coverage.hs
@@ -465,6 +465,7 @@ addTickHsExpr e@(HsVar id) = do freeVar id; return e
addTickHsExpr (HsUnboundVar {}) = panic "addTickHsExpr.HsUnboundVar"
addTickHsExpr e@(HsIPVar _) = return e
addTickHsExpr e@(HsOverLit _) = return e
+addTickHsExpr e@(HsOverLabel _) = return e
addTickHsExpr e@(HsLit _) = return e
addTickHsExpr (HsLam matchgroup) = liftM HsLam (addTickMatchGroup True matchgroup)
addTickHsExpr (HsLamCase ty mgs) = liftM (HsLamCase ty) (addTickMatchGroup True mgs)
diff --git a/compiler/deSugar/DsExpr.hs b/compiler/deSugar/DsExpr.hs
index 886961c4d0..dbe3bc69de 100644
--- a/compiler/deSugar/DsExpr.hs
+++ b/compiler/deSugar/DsExpr.hs
@@ -199,6 +199,7 @@ dsExpr (ExprWithTySigOut e _) = dsLExpr e
dsExpr (HsVar var) = return (varToCoreExpr var) -- See Note [Desugaring vars]
dsExpr (HsUnboundVar {}) = panic "dsExpr: HsUnboundVar" -- Typechecker eliminates them
dsExpr (HsIPVar _) = panic "dsExpr: HsIPVar"
+dsExpr (HsOverLabel _) = panic "dsExpr: HsOverLabel"
dsExpr (HsLit lit) = dsLit lit
dsExpr (HsOverLit lit) = dsOverLit lit
diff --git a/compiler/deSugar/DsMeta.hs b/compiler/deSugar/DsMeta.hs
index c0f0ba0db1..b61d670cc5 100644
--- a/compiler/deSugar/DsMeta.hs
+++ b/compiler/deSugar/DsMeta.hs
@@ -1072,6 +1072,7 @@ repE (HsVar x) =
Just (DsSplice e) -> do { e' <- dsExpr e
; return (MkC e') } }
repE e@(HsIPVar _) = notHandled "Implicit parameters" (ppr e)
+repE e@(HsOverLabel _) = notHandled "Overloaded labels" (ppr e)
repE e@(HsRecFld f) = case f of
Unambiguous _ x -> repE (HsVar x)
diff --git a/compiler/deSugar/Match.hs b/compiler/deSugar/Match.hs
index e23f223d76..40b50331e8 100644
--- a/compiler/deSugar/Match.hs
+++ b/compiler/deSugar/Match.hs
@@ -986,6 +986,7 @@ viewLExprEq (e1,_) (e2,_) = lexp e1 e2
-- the instance for IPName derives using the id, so this works if the
-- above does
exp (HsIPVar i) (HsIPVar i') = i == i'
+ exp (HsOverLabel l) (HsOverLabel l') = l == l'
exp (HsOverLit l) (HsOverLit l') =
-- Overloaded lits are equal if they have the same type
-- and the data is the same.
diff --git a/compiler/hsSyn/HsExpr.hs b/compiler/hsSyn/HsExpr.hs
index a0a9907079..8a733adec4 100644
--- a/compiler/hsSyn/HsExpr.hs
+++ b/compiler/hsSyn/HsExpr.hs
@@ -138,6 +138,8 @@ data HsExpr id
| HsRecFld (AmbiguousFieldOcc id) -- ^ Variable pointing to record selector
+ | HsOverLabel FastString -- ^ Overloaded label (See Note [Overloaded labels]
+ -- in GHC.OverloadedLabels)
| HsIPVar HsIPName -- ^ Implicit parameter
| HsOverLit (HsOverLit id) -- ^ Overloaded literals
@@ -626,6 +628,7 @@ ppr_expr :: forall id. OutputableBndr id => HsExpr id -> SDoc
ppr_expr (HsVar v) = pprPrefixOcc v
ppr_expr (HsUnboundVar v) = pprPrefixOcc v
ppr_expr (HsIPVar v) = ppr v
+ppr_expr (HsOverLabel l) = char '#' <> ppr l
ppr_expr (HsLit lit) = ppr lit
ppr_expr (HsOverLit lit) = ppr lit
ppr_expr (HsPar e) = parens (ppr_lexpr e)
@@ -844,6 +847,7 @@ hsExprNeedsParens (HsOverLit {}) = False
hsExprNeedsParens (HsVar {}) = False
hsExprNeedsParens (HsUnboundVar {}) = False
hsExprNeedsParens (HsIPVar {}) = False
+hsExprNeedsParens (HsOverLabel {}) = False
hsExprNeedsParens (ExplicitTuple {}) = False
hsExprNeedsParens (ExplicitList {}) = False
hsExprNeedsParens (ExplicitPArr {}) = False
@@ -865,6 +869,7 @@ isAtomicHsExpr (HsVar {}) = True
isAtomicHsExpr (HsLit {}) = True
isAtomicHsExpr (HsOverLit {}) = True
isAtomicHsExpr (HsIPVar {}) = True
+isAtomicHsExpr (HsOverLabel {}) = True
isAtomicHsExpr (HsUnboundVar {}) = True
isAtomicHsExpr (HsWrap _ e) = isAtomicHsExpr e
isAtomicHsExpr (HsPar e) = isAtomicHsExpr (unLoc e)
diff --git a/compiler/main/DynFlags.hs b/compiler/main/DynFlags.hs
index f91857f112..5f63b1048e 100644
--- a/compiler/main/DynFlags.hs
+++ b/compiler/main/DynFlags.hs
@@ -648,6 +648,7 @@ data ExtensionFlag
| Opt_BinaryLiterals
| Opt_NegativeLiterals
| Opt_DuplicateRecordFields
+ | Opt_OverloadedLabels
| Opt_EmptyCase
| Opt_PatternSynonyms
| Opt_PartialTypeSignatures
@@ -3184,6 +3185,7 @@ xFlags = [
flagSpec "NumDecimals" Opt_NumDecimals,
flagSpec' "OverlappingInstances" Opt_OverlappingInstances
setOverlappingInsts,
+ flagSpec "OverloadedLabels" Opt_OverloadedLabels,
flagSpec "OverloadedLists" Opt_OverloadedLists,
flagSpec "OverloadedStrings" Opt_OverloadedStrings,
flagSpec "PackageImports" Opt_PackageImports,
diff --git a/compiler/parser/Lexer.x b/compiler/parser/Lexer.x
index 041ad74aa0..8f29a270e0 100644
--- a/compiler/parser/Lexer.x
+++ b/compiler/parser/Lexer.x
@@ -260,7 +260,8 @@ $tab { warnTab }
-- with {-#, then we'll assume it's a pragma we know about and go for do_bol.
<bol> {
\n ;
- ^\# (line)? { begin line_prag1 }
+ ^\# line { begin line_prag1 }
+ ^\# / { followedByDigit } { begin line_prag1 }
^\# pragma .* \n ; -- GCC 3.3 CPP generated, apparently
^\# \! .* \n ; -- #!, for scripts
() { do_bol }
@@ -402,6 +403,11 @@ $tab { warnTab }
}
<0> {
+ "#" @varid / { ifExtension overloadedLabelsEnabled }
+ { skip_one_varid ITlabelvarid }
+}
+
+<0> {
"(#" / { ifExtension unboxedTuplesEnabled }
{ token IToubxparen }
"#)" / { ifExtension unboxedTuplesEnabled }
@@ -633,6 +639,7 @@ data Token
| ITqconsym (FastString,FastString)
| ITdupipvarid FastString -- GHC extension: implicit param: ?x
+ | ITlabelvarid FastString -- Overloaded label: #x
| ITchar SourceText Char -- Note [Literal source text] in BasicTypes
| ITstring SourceText FastString -- Note [Literal source text] in BasicTypes
@@ -906,6 +913,10 @@ notFollowedBySymbol :: AlexAccPred ExtsBitmap
notFollowedBySymbol _ _ _ (AI _ buf)
= nextCharIsNot buf (`elem` "!#$%&*+./<=>?@\\^|-~")
+followedByDigit :: AlexAccPred ExtsBitmap
+followedByDigit _ _ _ (AI _ buf)
+ = afterOptionalSpace buf (\b -> nextCharIs b (`elem` ['0'..'9']))
+
-- We must reject doc comments as being ordinary comments everywhere.
-- In some cases the doc comment will be selected as the lexeme due to
-- maximal munch, but not always, because the nested comment rule is
@@ -1984,6 +1995,7 @@ data ExtBits
| ArrowsBit
| ThBit
| IpBit
+ | OverloadedLabelsBit -- #x overloaded labels
| ExplicitForallBit -- the 'forall' keyword and '.' symbol
| BangPatBit -- Tells the parser to understand bang-patterns
-- (doesn't affect the lexer)
@@ -2023,6 +2035,8 @@ thEnabled :: ExtsBitmap -> Bool
thEnabled = xtest ThBit
ipEnabled :: ExtsBitmap -> Bool
ipEnabled = xtest IpBit
+overloadedLabelsEnabled :: ExtsBitmap -> Bool
+overloadedLabelsEnabled = xtest OverloadedLabelsBit
explicitForallEnabled :: ExtsBitmap -> Bool
explicitForallEnabled = xtest ExplicitForallBit
bangPatEnabled :: ExtsBitmap -> Bool
@@ -2113,6 +2127,7 @@ mkPState flags buf loc =
.|. ThBit `setBitIf` xopt Opt_TemplateHaskell flags
.|. QqBit `setBitIf` xopt Opt_QuasiQuotes flags
.|. IpBit `setBitIf` xopt Opt_ImplicitParams flags
+ .|. OverloadedLabelsBit `setBitIf` xopt Opt_OverloadedLabels flags
.|. ExplicitForallBit `setBitIf` xopt Opt_ExplicitForAll flags
.|. BangPatBit `setBitIf` xopt Opt_BangPatterns flags
.|. HaddockBit `setBitIf` gopt Opt_Haddock flags
diff --git a/compiler/parser/Parser.y b/compiler/parser/Parser.y
index 795c4d2f49..7b40574307 100644
--- a/compiler/parser/Parser.y
+++ b/compiler/parser/Parser.y
@@ -449,6 +449,7 @@ output it generates.
QCONSYM { L _ (ITqconsym _) }
IPDUPVARID { L _ (ITdupipvarid _) } -- GHC extension
+ LABELVARID { L _ (ITlabelvarid _) }
CHAR { L _ (ITchar _ _) }
STRING { L _ (ITstring _ _) }
@@ -2267,6 +2268,7 @@ aexp2 :: { LHsExpr RdrName }
: qvar { sL1 $1 (HsVar $! unLoc $1) }
| qcon { sL1 $1 (HsVar $! unLoc $1) }
| ipvar { sL1 $1 (HsIPVar $! unLoc $1) }
+ | overloaded_label { sL1 $1 (HsOverLabel $! unLoc $1) }
| literal { sL1 $1 (HsLit $! unLoc $1) }
-- This will enable overloaded strings permanently. Normally the renamer turns HsString
-- into HsOverLit when -foverloaded-strings is on.
@@ -2723,6 +2725,12 @@ ipvar :: { Located HsIPName }
: IPDUPVARID { sL1 $1 (HsIPName (getIPDUPVARID $1)) }
-----------------------------------------------------------------------------
+-- Overloaded labels
+
+overloaded_label :: { Located FastString }
+ : LABELVARID { sL1 $1 (getLABELVARID $1) }
+
+-----------------------------------------------------------------------------
-- Warnings and deprecations
name_boolformula_opt :: { LBooleanFormula (Located RdrName) }
@@ -3141,6 +3149,7 @@ getQCONID (L _ (ITqconid x)) = x
getQVARSYM (L _ (ITqvarsym x)) = x
getQCONSYM (L _ (ITqconsym x)) = x
getIPDUPVARID (L _ (ITdupipvarid x)) = x
+getLABELVARID (L _ (ITlabelvarid x)) = x
getCHAR (L _ (ITchar _ x)) = x
getSTRING (L _ (ITstring _ x)) = x
getINTEGER (L _ (ITinteger _ x)) = x
diff --git a/compiler/prelude/PrelNames.hs b/compiler/prelude/PrelNames.hs
index 74c3118a71..346f3a382d 100644
--- a/compiler/prelude/PrelNames.hs
+++ b/compiler/prelude/PrelNames.hs
@@ -321,6 +321,9 @@ basicKnownKeyNames
-- Type-level naturals
knownNatClassName, knownSymbolClassName,
+ -- Overloaded labels
+ isLabelClassName,
+
-- Source locations
callStackDataConName, callStackTyConName,
srcLocDataConName,
@@ -478,6 +481,9 @@ gHC_STATICPTR = mkBaseModule (fsLit "GHC.StaticPtr")
gHC_FINGERPRINT_TYPE :: Module
gHC_FINGERPRINT_TYPE = mkBaseModule (fsLit "GHC.Fingerprint.Type")
+gHC_OVER_LABELS :: Module
+gHC_OVER_LABELS = mkBaseModule (fsLit "GHC.OverloadedLabels")
+
mAIN, rOOT_MAIN :: Module
mAIN = mkMainModule_ mAIN_NAME
rOOT_MAIN = mkMainModule (fsLit ":Main") -- Root module for initialisation
@@ -1271,6 +1277,11 @@ knownNatClassName = clsQual gHC_TYPELITS (fsLit "KnownNat") knownNatClassNam
knownSymbolClassName :: Name
knownSymbolClassName = clsQual gHC_TYPELITS (fsLit "KnownSymbol") knownSymbolClassNameKey
+-- Overloaded labels
+isLabelClassName :: Name
+isLabelClassName
+ = clsQual gHC_OVER_LABELS (fsLit "IsLabel") isLabelClassNameKey
+
-- Source Locations
callStackDataConName, callStackTyConName, srcLocDataConName :: Name
callStackDataConName
@@ -1407,6 +1418,9 @@ knownSymbolClassNameKey = mkPreludeClassUnique 43
ghciIoClassKey :: Unique
ghciIoClassKey = mkPreludeClassUnique 44
+isLabelClassNameKey :: Unique
+isLabelClassNameKey = mkPreludeClassUnique 45
+
---------------- Template Haskell -------------------
-- THNames.hs: USES ClassUniques 200-299
-----------------------------------------------------
@@ -2037,6 +2051,7 @@ toDynIdKey = mkPreludeMiscIdUnique 509
bitIntegerIdKey :: Unique
bitIntegerIdKey = mkPreludeMiscIdUnique 510
+
{-
************************************************************************
* *
diff --git a/compiler/rename/RnExpr.hs b/compiler/rename/RnExpr.hs
index 5764765fd3..d748bf0bc0 100644
--- a/compiler/rename/RnExpr.hs
+++ b/compiler/rename/RnExpr.hs
@@ -114,6 +114,9 @@ rnExpr (HsVar v)
rnExpr (HsIPVar v)
= return (HsIPVar v, emptyFVs)
+rnExpr (HsOverLabel v)
+ = return (HsOverLabel v, emptyFVs)
+
rnExpr (HsLit lit@(HsString src s))
= do { opt_OverloadedStrings <- xoptM Opt_OverloadedStrings
; if opt_OverloadedStrings then
diff --git a/compiler/typecheck/TcEvidence.hs b/compiler/typecheck/TcEvidence.hs
index a56739bf4b..98db87fed3 100644
--- a/compiler/typecheck/TcEvidence.hs
+++ b/compiler/typecheck/TcEvidence.hs
@@ -1150,9 +1150,12 @@ instance Outputable EvTypeable where
-- Helper functions for dealing with IP newtype-dictionaries
----------------------------------------------------------------------
--- | Create a 'Coercion' that unwraps an implicit-parameter dictionary
--- to expose the underlying value. We expect the 'Type' to have the form
--- `IP sym ty`, return a 'Coercion' `co :: IP sym ty ~ ty`.
+-- | Create a 'Coercion' that unwraps an implicit-parameter or
+-- overloaded-label dictionary to expose the underlying value. We
+-- expect the 'Type' to have the form `IP sym ty` or `IsLabel sym ty`,
+-- and return a 'Coercion' `co :: IP sym ty ~ ty` or
+-- `co :: IsLabel sym ty ~ Proxy# sym -> ty`. See also
+-- Note [Type-checking overloaded labels] in TcExpr.
unwrapIP :: Type -> Coercion
unwrapIP ty =
case unwrapNewTyCon_maybe tc of
diff --git a/compiler/typecheck/TcExpr.hs b/compiler/typecheck/TcExpr.hs
index a97c75424e..b69b3e626f 100644
--- a/compiler/typecheck/TcExpr.hs
+++ b/compiler/typecheck/TcExpr.hs
@@ -57,6 +57,7 @@ import TysWiredIn
import TysPrim( intPrimTy )
import PrimOp( tagToEnumKey )
import PrelNames
+import MkId ( proxyHashId )
import DynFlags
import SrcLoc
import Util
@@ -212,6 +213,22 @@ tcExpr (HsIPVar x) res_ty
fromDict ipClass x ty = HsWrap $ mkWpCast $ TcCoercion $
unwrapIP $ mkClassPred ipClass [x,ty]
+tcExpr (HsOverLabel l) res_ty -- See Note [Type-checking overloaded labels]
+ = do { let origin = OverLabelOrigin l
+ ; isLabelClass <- tcLookupClass isLabelClassName
+ ; alpha <- newFlexiTyVarTy openTypeKind
+ ; let lbl = mkStrLitTy l
+ pred = mkClassPred isLabelClass [lbl, alpha]
+ ; loc <- getSrcSpanM
+ ; var <- emitWanted origin pred
+ ; let proxy_arg = L loc (mkHsWrap (mkWpTyApps [typeSymbolKind, lbl])
+ (HsVar proxyHashId))
+ tm = L loc (fromDict pred (HsVar var)) `HsApp` proxy_arg
+ ; tcWrapResult tm alpha res_ty }
+ where
+ -- Coerces a dictionary for `IsLabel "x" t` into `Proxy# x -> t`.
+ fromDict pred = HsWrap $ mkWpCast $ TcCoercion $ unwrapIP pred
+
tcExpr (HsLam match) res_ty
= do { (co_fn, match') <- tcMatchLambda match res_ty
; return (mkHsWrap co_fn (HsLam match')) }
@@ -252,6 +269,26 @@ tcExpr (HsType ty) _
-- Can't eliminate it altogether from the parser, because the
-- same parser parses *patterns*.
+
+{-
+Note [Type-checking overloaded labels]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Recall that (in GHC.OverloadedLabels) we have
+
+ class IsLabel (x :: Symbol) a where
+ fromLabel :: Proxy# x -> a
+
+When we see an overloaded label like `#foo`, we generate a fresh
+variable `alpha` for the type and emit an `IsLabel "foo" alpha`
+constraint. Because the `IsLabel` class has a single method, it is
+represented by a newtype, so we can coerce `IsLabel "foo" alpha` to
+`Proxy# "foo" -> alpha` (just like for implicit parameters). We then
+apply it to `proxy#` of type `Proxy# "foo"`.
+
+That is, we translate `#foo` to `fromLabel (proxy# :: Proxy# "foo")`.
+-}
+
+
{-
************************************************************************
* *
diff --git a/compiler/typecheck/TcHsSyn.hs b/compiler/typecheck/TcHsSyn.hs
index a11f9d6370..88c4d9c7c3 100644
--- a/compiler/typecheck/TcHsSyn.hs
+++ b/compiler/typecheck/TcHsSyn.hs
@@ -589,6 +589,9 @@ zonkExpr env (HsVar id)
zonkExpr _ (HsIPVar id)
= return (HsIPVar id)
+zonkExpr _ (HsOverLabel l)
+ = return (HsOverLabel l)
+
zonkExpr env (HsLit (HsRat f ty))
= do new_ty <- zonkTcTypeToType env ty
return (HsLit (HsRat f new_ty))
diff --git a/compiler/typecheck/TcRnTypes.hs b/compiler/typecheck/TcRnTypes.hs
index d81727a41d..66635a0e6c 100644
--- a/compiler/typecheck/TcRnTypes.hs
+++ b/compiler/typecheck/TcRnTypes.hs
@@ -2215,6 +2215,7 @@ data CtOrigin
CtOrigin -- originally arising from this
| IPOccOrigin HsIPName -- Occurrence of an implicit parameter
+ | OverLabelOrigin FastString -- Occurrence of an overloaded label
| LiteralOrigin (HsOverLit Name) -- Occurrence of a literal
| NegateOrigin -- Occurrence of syntactic negation
@@ -2324,6 +2325,8 @@ pprCtO (OccurrenceOf name) = hsep [ptext (sLit "a use of"), quotes (ppr name)]
pprCtO (OccurrenceOfRecSel name) = hsep [ptext (sLit "a use of"), quotes (ppr name)]
pprCtO AppOrigin = ptext (sLit "an application")
pprCtO (IPOccOrigin name) = hsep [ptext (sLit "a use of implicit parameter"), quotes (ppr name)]
+pprCtO (OverLabelOrigin l) = hsep [ptext (sLit "the overloaded label")
+ ,quotes (char '#' <> ppr l)]
pprCtO RecordUpdOrigin = ptext (sLit "a record update")
pprCtO ExprSigOrigin = ptext (sLit "an expression type signature")
pprCtO PatSigOrigin = ptext (sLit "a pattern type signature")
diff --git a/libraries/base/GHC/OverloadedLabels.hs b/libraries/base/GHC/OverloadedLabels.hs
new file mode 100644
index 0000000000..f4a76cf8ea
--- /dev/null
+++ b/libraries/base/GHC/OverloadedLabels.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE NoImplicitPrelude
+ , MultiParamTypeClasses
+ , MagicHash
+ , KindSignatures
+ , DataKinds
+ #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : GHC.OverloadedLabels
+-- Copyright : (c) Adam Gundry 2015
+-- License : see libraries/base/LICENSE
+--
+-- Maintainer : cvs-ghc@haskell.org
+-- Stability : internal
+-- Portability : non-portable (GHC extensions)
+--
+-- This module defines the `IsLabel` class is used by the
+-- OverloadedLabels extension. See the
+-- <https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields/OverloadedLabels wiki page>
+-- for more details.
+--
+-- The key idea is that when GHC sees an occurrence of the new
+-- overloaded label syntax @#foo@, it is replaced with
+--
+-- > fromLabel (proxy# :: Proxy# "foo") :: alpha
+--
+-- plus a wanted constraint @IsLabel "foo" alpha@.
+--
+-----------------------------------------------------------------------------
+
+-- Note [Overloaded labels]
+-- ~~~~~~~~~~~~~~~~~~~~~~~~
+-- An overloaded label is represented by the 'HsOverLabel' constructor
+-- of 'HsExpr', which stores a 'FastString'. It is passed through
+-- unchanged by the renamer, and the type-checker transforms it into a
+-- call to 'fromLabel'. See Note [Type-checking overloaded labels] in
+-- TcExpr for more details in how type-checking works.
+
+module GHC.OverloadedLabels
+ ( IsLabel(..)
+ ) where
+
+import GHC.Base ( Symbol )
+import GHC.Exts ( Proxy# )
+
+class IsLabel (x :: Symbol) a where
+ fromLabel :: Proxy# x -> a
diff --git a/libraries/base/base.cabal b/libraries/base/base.cabal
index 7c89be4cfa..190309799e 100644
--- a/libraries/base/base.cabal
+++ b/libraries/base/base.cabal
@@ -247,6 +247,7 @@ Library
GHC.Natural
GHC.Num
GHC.OldList
+ GHC.OverloadedLabels
GHC.PArr
GHC.Pack
GHC.Profiling
diff --git a/testsuite/tests/driver/T4437.hs b/testsuite/tests/driver/T4437.hs
index f345ce6b1f..f76dc34354 100644
--- a/testsuite/tests/driver/T4437.hs
+++ b/testsuite/tests/driver/T4437.hs
@@ -32,7 +32,8 @@ check title expected got
expectedGhcOnlyExtensions :: [String]
expectedGhcOnlyExtensions = ["RelaxedLayout",
"AlternativeLayoutRule",
- "AlternativeLayoutRuleTransitional"]
+ "AlternativeLayoutRuleTransitional",
+ "OverloadedLabels"]
expectedCabalOnlyExtensions :: [String]
expectedCabalOnlyExtensions = ["Generics",
diff --git a/testsuite/tests/overloadedrecflds/ghci/all.T b/testsuite/tests/overloadedrecflds/ghci/all.T
index f114c0fdfa..c67d42f1a8 100644
--- a/testsuite/tests/overloadedrecflds/ghci/all.T
+++ b/testsuite/tests/overloadedrecflds/ghci/all.T
@@ -1 +1,2 @@
test('overloadedrecfldsghci01', combined_output, ghci_script, ['overloadedrecfldsghci01.script'])
+test('overloadedlabelsghci01', combined_output, ghci_script, ['overloadedlabelsghci01.script'])
diff --git a/testsuite/tests/overloadedrecflds/ghci/overloadedlabelsghci01.script b/testsuite/tests/overloadedrecflds/ghci/overloadedlabelsghci01.script
new file mode 100644
index 0000000000..3b5dde1800
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/ghci/overloadedlabelsghci01.script
@@ -0,0 +1,12 @@
+:set -XOverloadedLabels
+:t #x
+:m + GHC.OverloadedLabels
+:seti -XFlexibleInstances -XFlexibleContexts -XTypeFamilies -XMultiParamTypeClasses
+instance IsLabel x [Char] where fromLabel _ = "hello"
+instance (s ~ [Char], t ~ [Char]) => IsLabel x (s -> t) where fromLabel _ = (++ " world")
+#x :: String
+#x #y
+:{
+#x
+"goodbye"
+:}
diff --git a/testsuite/tests/overloadedrecflds/ghci/overloadedlabelsghci01.stdout b/testsuite/tests/overloadedrecflds/ghci/overloadedlabelsghci01.stdout
new file mode 100644
index 0000000000..08a34c0bdd
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/ghci/overloadedlabelsghci01.stdout
@@ -0,0 +1,4 @@
+#x :: IsLabel "x" t => t
+"hello"
+"hello world"
+"goodbye world"
diff --git a/testsuite/tests/overloadedrecflds/should_fail/all.T b/testsuite/tests/overloadedrecflds/should_fail/all.T
index 5ff61e2735..a9c7426c78 100644
--- a/testsuite/tests/overloadedrecflds/should_fail/all.T
+++ b/testsuite/tests/overloadedrecflds/should_fail/all.T
@@ -22,3 +22,4 @@ test('overloadedrecfldsfail12',
multimod_compile_fail, ['overloadedrecfldsfail12', ''])
test('overloadedrecfldsfail13', normal, compile_fail, [''])
test('overloadedrecfldsfail14', normal, compile_fail, [''])
+test('overloadedlabelsfail01', normal, compile_fail, [''])
diff --git a/testsuite/tests/overloadedrecflds/should_fail/overloadedlabelsfail01.hs b/testsuite/tests/overloadedrecflds/should_fail/overloadedlabelsfail01.hs
new file mode 100644
index 0000000000..361da45086
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_fail/overloadedlabelsfail01.hs
@@ -0,0 +1,15 @@
+{-# LANGUAGE OverloadedLabels, DataKinds, FlexibleContexts #-}
+
+import GHC.OverloadedLabels
+
+-- No instance for (OverloadedLabel "x" t0)
+a = #x
+
+-- No instance for (OverloadedLabel "x" (t0 -> t1), OverloadedLabel "y" t0)
+b = #x #y
+
+-- Could not deduce (OverloadedLabel "y" t) from (OverloadedLabel "x" t)
+c :: IsLabel "x" t => t
+c = #y
+
+main = return ()
diff --git a/testsuite/tests/overloadedrecflds/should_fail/overloadedlabelsfail01.stderr b/testsuite/tests/overloadedrecflds/should_fail/overloadedlabelsfail01.stderr
new file mode 100644
index 0000000000..1631c6de6d
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_fail/overloadedlabelsfail01.stderr
@@ -0,0 +1,31 @@
+
+overloadedlabelsfail01.hs:6:5: error:
+ No instance for (IsLabel "x" t2)
+ arising from the overloaded label ‘#x’
+ In the expression: #x
+ In an equation for ‘a’: a = #x
+
+overloadedlabelsfail01.hs:9:5: error:
+ No instance for (IsLabel "x" (t0 -> t1))
+ arising from the overloaded label ‘#x’
+ (maybe you haven't applied a function to enough arguments?)
+ In the expression: #x
+ In the expression: #x #y
+ In an equation for ‘b’: b = #x #y
+
+overloadedlabelsfail01.hs:9:8: error:
+ No instance for (IsLabel "y" t0)
+ arising from the overloaded label ‘#y’
+ In the first argument of ‘#x’, namely ‘#y’
+ In the expression: #x #y
+ In an equation for ‘b’: b = #x #y
+
+overloadedlabelsfail01.hs:13:5: error:
+ Could not deduce (IsLabel "y" t)
+ arising from the overloaded label ‘#y’
+ from the context: IsLabel "x" t
+ bound by the type signature for:
+ c :: IsLabel "x" t => t
+ at overloadedlabelsfail01.hs:12:6-23
+ In the expression: #y
+ In an equation for ‘c’: c = #y
diff --git a/testsuite/tests/overloadedrecflds/should_run/OverloadedLabelsRun04_A.hs b/testsuite/tests/overloadedrecflds/should_run/OverloadedLabelsRun04_A.hs
new file mode 100644
index 0000000000..e3b38c245e
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_run/OverloadedLabelsRun04_A.hs
@@ -0,0 +1,8 @@
+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TemplateHaskell #-}
+module OverloadedLabelsRun04_A where
+
+import GHC.OverloadedLabels
+import Language.Haskell.TH
+
+instance IsLabel x (Q [Dec]) where
+ fromLabel _ = [d| main = putStrLn "Ok" |]
diff --git a/testsuite/tests/overloadedrecflds/should_run/all.T b/testsuite/tests/overloadedrecflds/should_run/all.T
index 3d7cef2c54..21391ac646 100644
--- a/testsuite/tests/overloadedrecflds/should_run/all.T
+++ b/testsuite/tests/overloadedrecflds/should_run/all.T
@@ -8,3 +8,9 @@ test('overloadedrecfldsrun03', normal, compile_and_run, [''])
test('overloadedrecfldsrun04', normal, compile_and_run, [''])
test('overloadedrecfldsrun05', normal, compile_and_run, [''])
test('overloadedrecfldsrun06', normal, compile_and_run, [''])
+test('overloadedlabelsrun01', normal, compile_and_run, [''])
+test('overloadedlabelsrun02', normal, compile_and_run, [''])
+test('overloadedlabelsrun03', normal, compile_and_run, [''])
+test('overloadedlabelsrun04',
+ extra_clean(['OverloadedLabelsRun04_A.hi', 'OverloadedLabelsRun04_A.o']),
+ multimod_compile_and_run, ['overloadedlabelsrun04', ''])
diff --git a/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun01.hs b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun01.hs
new file mode 100644
index 0000000000..45c7854e64
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun01.hs
@@ -0,0 +1,29 @@
+-- Basic tests of overloaded labels
+
+{-# LANGUAGE OverloadedLabels
+ , DataKinds
+ , FlexibleContexts
+ , FlexibleInstances
+ , MultiParamTypeClasses
+ , NoMonomorphismRestriction
+ #-}
+
+import GHC.OverloadedLabels
+
+instance IsLabel "true" Bool where
+ fromLabel _ = True
+
+instance IsLabel "false" Bool where
+ fromLabel _ = False
+
+a :: IsLabel "true" t => t
+a = #true
+
+b = #false
+
+c :: Bool
+c = #true
+
+main = do print (a :: Bool)
+ print (b :: Bool)
+ print c
diff --git a/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun01.stdout b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun01.stdout
new file mode 100644
index 0000000000..4644fbc1ec
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun01.stdout
@@ -0,0 +1,3 @@
+True
+False
+True
diff --git a/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun02.hs b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun02.hs
new file mode 100644
index 0000000000..eea8f36d40
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun02.hs
@@ -0,0 +1,61 @@
+-- Using overloaded labels to provide nice syntactic sugar for a
+-- term representation using de Bruijn indices
+
+{-# LANGUAGE OverloadedLabels
+ , DataKinds
+ , FlexibleContexts
+ , FlexibleInstances
+ , GADTs
+ , KindSignatures
+ , MultiParamTypeClasses
+ , NoMonomorphismRestriction
+ , OverlappingInstances
+ , ScopedTypeVariables
+ , StandaloneDeriving
+ , TypeOperators
+ #-}
+
+import GHC.OverloadedLabels
+import Data.Proxy ( Proxy(..) )
+import GHC.TypeLits ( Symbol )
+
+instance x ~ y => IsLabel x (Proxy y) where
+ fromLabel _ = Proxy
+
+data Elem (x :: Symbol) g where
+ Top :: Elem x (x ': g)
+ Pop :: Elem x g -> Elem x (y ': g)
+deriving instance Show (Elem x g)
+
+
+class IsElem x g where
+ which :: Elem x g
+
+instance IsElem x (x ': g) where
+ which = Top
+
+instance IsElem x g => IsElem x (y ': g) where
+ which = Pop which
+
+
+data Tm g where
+ Var :: Elem x g -> Tm g
+ App :: Tm g -> Tm g -> Tm g
+ Lam :: Tm (x ': g) -> Tm g
+deriving instance Show (Tm g)
+
+instance IsElem x g => IsLabel x (Tm g) where
+ fromLabel _ = Var (which :: Elem x g)
+
+lam :: Proxy x -> Tm (x ': g) -> Tm g
+lam _ = Lam
+
+s = lam #x #x
+t = lam #x (lam #y (#x `App` #y))
+
+u :: IsElem "z" g => Tm g
+u = #z `App` #z
+
+main = do print s
+ print t
+ print (u :: Tm '["z"])
diff --git a/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun02.stdout b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun02.stdout
new file mode 100644
index 0000000000..ff2a4e75f0
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun02.stdout
@@ -0,0 +1,3 @@
+Lam (Var Top)
+Lam (Lam (App (Var (Pop Top)) (Var Top)))
+App (Var Top) (Var Top)
diff --git a/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun03.hs b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun03.hs
new file mode 100644
index 0000000000..a854d7ae07
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun03.hs
@@ -0,0 +1,21 @@
+-- Using overloaded labels as strings, slightly pointlessly
+
+{-# LANGUAGE OverloadedLabels
+ , DataKinds
+ , FlexibleContexts
+ , FlexibleInstances
+ , MultiParamTypeClasses
+ , ScopedTypeVariables
+ , TypeFamilies
+ , TypeSynonymInstances
+ #-}
+
+import GHC.OverloadedLabels
+import Data.Proxy ( Proxy(..) )
+import GHC.TypeLits ( KnownSymbol, symbolVal )
+
+instance (KnownSymbol x, c ~ Char) => IsLabel x [c] where
+ fromLabel _ = symbolVal (Proxy :: Proxy x)
+
+main = do putStrLn #x
+ print $ #x ++ #y
diff --git a/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun03.stdout b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun03.stdout
new file mode 100644
index 0000000000..599697946c
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun03.stdout
@@ -0,0 +1,2 @@
+x
+"xy"
diff --git a/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun04.hs b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun04.hs
new file mode 100644
index 0000000000..8794a87b61
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun04.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE OverloadedLabels, TemplateHaskell #-}
+
+import OverloadedLabelsRun04_A
+
+-- Who knew that there were so many ways that a line could start with
+-- a # sign in Haskell? None of these are overloaded labels:
+#line 7 "overloadedlabelsrun04.hs"
+# 8 "overloadedlabelsrun04.hs"
+#!notashellscript
+#pragma foo
+
+-- But this one is:
+#foo
diff --git a/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun04.stdout b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun04.stdout
new file mode 100644
index 0000000000..7326d96039
--- /dev/null
+++ b/testsuite/tests/overloadedrecflds/should_run/overloadedlabelsrun04.stdout
@@ -0,0 +1 @@
+Ok