sheaf pushed to branch wip/T26878 at Glasgow Haskell Compiler / GHC
Commits:
-
bf85c633
by sheaf at 2026-04-01T18:11:24+02:00
9 changed files:
- compiler/GHC/Core/Utils.hs
- compiler/GHC/Types/Tickish.hs
- libraries/ghc-heap/tests/tso_and_stack_closures.hs
- + testsuite/tests/profiling/should_compile/T27121.hs
- + testsuite/tests/profiling/should_compile/T27121_aux.hs
- testsuite/tests/profiling/should_compile/all.T
- + testsuite/tests/simplCore/should_compile/T26941.hs
- + testsuite/tests/simplCore/should_compile/T26941_aux.hs
- testsuite/tests/simplCore/should_compile/all.T
Changes:
| ... | ... | @@ -303,101 +303,262 @@ mkCast expr co |
| 303 | 303 | * *
|
| 304 | 304 | ********************************************************************* -}
|
| 305 | 305 | |
| 306 | --- | Wraps the given expression in the source annotation, dropping the
|
|
| 307 | --- annotation if possible.
|
|
| 306 | +-- | Wraps the given expression in a Tick, floating the tick as far into
|
|
| 307 | +-- the AST as possible in order to try to satisfy the tick's desired placement
|
|
| 308 | +-- properties (as per Note [Tickish placement] in GHC.Types.Tickish).
|
|
| 309 | +--
|
|
| 310 | +-- Prefer using 'mkTick' over explicit use of the 'Tick' constructor.
|
|
| 311 | +--
|
|
| 312 | +-- Also performs small on-the-fly optimisations:
|
|
| 313 | +--
|
|
| 314 | +-- * Eliminate unnecessary ticks by either absorbing them into existing ones
|
|
| 315 | +-- or dropping them if that is valid (e.g. dropping profiling ticks around
|
|
| 316 | +-- types, coercions and literals).
|
|
| 317 | +-- * Split profiling ticks into counting/scoping parts so that the two parts
|
|
| 318 | +-- can be placed independently into the AST.
|
|
| 308 | 319 | mkTick :: CoreTickish -> CoreExpr -> CoreExpr
|
| 309 | -mkTick t orig_expr = mkTick' id orig_expr
|
|
| 320 | +mkTick t orig_expr = mkTick' orig_expr
|
|
| 310 | 321 | where
|
| 311 | 322 | -- Some ticks (cost-centres) can be split in two, with the
|
| 312 | 323 | -- non-counting part having laxer placement properties.
|
| 313 | - canSplit = tickishCanSplit t && tickishPlace (mkNoCount t) /= tickishPlace t
|
|
| 324 | + -- See Note [Scoping ticks and counting ticks] in GHC.Types.Tickish.
|
|
| 325 | + can_split = tickishCanSplit t
|
|
| 314 | 326 | |
| 315 | - -- mkTick' handles floating of ticks *into* the expression.
|
|
| 316 | - mkTick' :: (CoreExpr -> CoreExpr) -- Apply before adding tick (float with)
|
|
| 317 | - -- Always a composition of (Tick t) wrappers
|
|
| 318 | - -> CoreExpr -- Current expression
|
|
| 319 | - -> CoreExpr
|
|
| 320 | - -- So in the call (mkTick' rest e), the expression
|
|
| 321 | - -- (rest e)
|
|
| 322 | - -- has the same type as e
|
|
| 323 | - -- Returns an expression equivalent to (Tick t (rest e))
|
|
| 324 | - mkTick' rest expr = case expr of
|
|
| 325 | - -- Float ticks into unsafe coerce the same way we would do with a cast.
|
|
| 326 | - Case scrut bndr ty alts@[Alt ac abs _rhs]
|
|
| 327 | - | Just rhs <- isUnsafeEqualityCase scrut bndr alts
|
|
| 328 | - -> Case scrut bndr ty [Alt ac abs (mkTick' rest rhs)]
|
|
| 329 | - |
|
| 330 | - -- Cost centre ticks should never be reordered relative to each
|
|
| 331 | - -- other. Therefore we can stop whenever two collide.
|
|
| 327 | + -- mkTick' handles floating of tick `t` *into* the expression.
|
|
| 328 | + mkTick' :: CoreExpr -> CoreExpr
|
|
| 329 | + mkTick' expr = case expr of
|
|
| 332 | 330 | Tick t2 e
|
| 333 | - | ProfNote{} <- t2, ProfNote{} <- t -> Tick t $ rest expr
|
|
| 334 | - |
|
| 335 | - -- Otherwise we assume that ticks of different placements float
|
|
| 336 | - -- through each other.
|
|
| 337 | - | tickishPlace t2 /= tickishPlace t -> Tick t2 $ mkTick' rest e
|
|
| 338 | - |
|
| 339 | - -- For annotations this is where we make sure to not introduce
|
|
| 340 | - -- redundant ticks.
|
|
| 341 | - | tickishContains t t2 -> mkTick' rest e -- Drop t2
|
|
| 342 | - | tickishContains t2 t -> rest e -- Drop t
|
|
| 343 | - | otherwise -> mkTick' (rest . Tick t2) e
|
|
| 344 | - |
|
| 345 | - -- Ticks don't care about types, so we just float all ticks
|
|
| 346 | - -- through them. Note that it's not enough to check for these
|
|
| 347 | - -- cases top-level. While mkTick will never produce Core with type
|
|
| 348 | - -- expressions below ticks, such constructs can be the result of
|
|
| 349 | - -- unfoldings. We therefore make an effort to put everything into
|
|
| 350 | - -- the right place no matter what we start with.
|
|
| 351 | - Cast e co -> mkCast (mkTick' rest e) co
|
|
| 352 | - Coercion co -> Tick t $ rest (Coercion co)
|
|
| 331 | + |
|
| 332 | + -- Common up ticks when possible, including profiling ticks that
|
|
| 333 | + -- share a cost centre and source notes that subsume one another.
|
|
| 334 | + | Just t' <- combineTickish_maybe t t2
|
|
| 335 | + -> mkTick t' e
|
|
| 336 | + |
|
| 337 | + -- Profiling ticks for different cost centres should never be reordered
|
|
| 338 | + -- relative to each other. Therefore, we stop whenever two collide.
|
|
| 339 | + | ProfNote {} <- t
|
|
| 340 | + , ProfNote {} <- t2
|
|
| 341 | + -> Tick t expr
|
|
| 342 | + |
|
| 343 | + -- Ticks of different placements float through each other, so that each
|
|
| 344 | + -- tick can be floated into its expected position in the AST.
|
|
| 345 | + -- See Note [Tickish placement] in GHC.Types.Tickish.
|
|
| 346 | + | tickishPlace t2 /= tickishPlace t
|
|
| 347 | + -> Tick t2 $ mkTick' e
|
|
| 353 | 348 | |
| 354 | 349 | Lam x e
|
| 355 | 350 | -- Always float through type lambdas. Even for non-type lambdas,
|
| 356 | 351 | -- floating is allowed for all but the most strict placement rule.
|
| 357 | 352 | | not (isRuntimeVar x) || tickishPlace t /= PlaceRuntime
|
| 358 | - -> Lam x $ mkTick' rest e
|
|
| 353 | + -> Lam x $ mkTick' e
|
|
| 359 | 354 | |
| 360 | - -- If it is both counting and scoped, we split the tick into its
|
|
| 361 | - -- two components, often allowing us to keep the counting tick on
|
|
| 362 | - -- the outside of the lambda and push the scoped tick inside.
|
|
| 363 | - -- The point of this is that the counting tick can probably be
|
|
| 364 | - -- floated, and the lambda may then be in a position to be
|
|
| 365 | - -- beta-reduced.
|
|
| 366 | - | canSplit
|
|
| 367 | - -> Tick (mkNoScope t) $ rest $ Lam x $ mkTick (mkNoCount t) e
|
|
| 355 | + -- Push SCCs into lambdas.
|
|
| 356 | + -- See (PSCC2) in Note [Pushing SCCs inwards].
|
|
| 357 | + | can_split
|
|
| 358 | + -> Tick (mkNoScope t) $ Lam x $ mkTick (mkNoCount t) e
|
|
| 368 | 359 | |
| 369 | 360 | App f arg
|
| 370 | - -- Always float through type applications.
|
|
| 361 | + -- All ticks float inwards through non-runtime arguments, as per
|
|
| 362 | + -- Note [Tickish placement] in GHC.Types.Tickish.
|
|
| 371 | 363 | | not (isRuntimeArg arg)
|
| 372 | - -> App (mkTick' rest f) arg
|
|
| 364 | + -> App (mkTick' f) arg
|
|
| 373 | 365 | |
| 374 | - -- We can also float through constructor applications, placement
|
|
| 375 | - -- permitting. Again we can split.
|
|
| 376 | - | isSaturatedConApp expr && (tickishPlace t==PlaceCostCentre || canSplit)
|
|
| 366 | + -- Push SCCs into saturated constructor applications.
|
|
| 367 | + -- See (PSCC3) in Note [Pushing SCCs inwards].
|
|
| 368 | + | isSaturatedConApp expr
|
|
| 369 | + , tickishPlace t == PlaceCostCentre || can_split
|
|
| 377 | 370 | -> if tickishPlace t == PlaceCostCentre
|
| 378 | - then rest $ tickHNFArgs t expr
|
|
| 379 | - else Tick (mkNoScope t) $ rest $ tickHNFArgs (mkNoCount t) expr
|
|
| 371 | + then tickHNFArgs t expr
|
|
| 372 | + else Tick (mkNoScope t) $ tickHNFArgs (mkNoCount t) expr
|
|
| 373 | + |
|
| 374 | + -- See Note [No ticks around types or coercions]
|
|
| 375 | + e@(Coercion {}) -> e
|
|
| 376 | + e@(Type {}) -> e
|
|
| 377 | + -- Don't wrap static data in a tick which compiles to code,
|
|
| 378 | + -- as the code will never be run.
|
|
| 379 | + e@(Lit {}) | tickishIsCode t -> e
|
|
| 380 | + |
|
| 381 | + -- All ticks can be floated through casts, as per Note [Tickish placement].
|
|
| 382 | + Cast e co -> mkCast (mkTick' e) co
|
|
| 383 | + |
|
| 384 | + -- Treat 'unsafeCoerce' as if it was a cast: float all ticks inwards.
|
|
| 385 | + -- See Note [Push ticks into unsafeCoerce]
|
|
| 386 | + Case scrut bndr ty alts@[Alt ac abs _rhs]
|
|
| 387 | + | Just rhs <- isUnsafeEqualityCase scrut bndr alts
|
|
| 388 | + -> Case scrut bndr ty [Alt ac abs (mkTick' rhs)]
|
|
| 380 | 389 | |
| 381 | 390 | Var x
|
| 382 | - | notFunction && tickishPlace t == PlaceCostCentre
|
|
| 383 | - -> rest expr -- Drop t
|
|
| 384 | - | notFunction && canSplit
|
|
| 385 | - -> Tick (mkNoScope t) $ rest expr
|
|
| 386 | - where
|
|
| 387 | - -- SCCs can be eliminated on variables provided the variable
|
|
| 388 | - -- is not a function. In these cases the SCC makes no difference:
|
|
| 389 | - -- the cost of evaluating the variable will be attributed to its
|
|
| 390 | - -- definition site. When the variable refers to a function, however,
|
|
| 391 | - -- an SCC annotation on the variable affects the cost-centre stack
|
|
| 392 | - -- when the function is called, so we must retain those.
|
|
| 393 | - notFunction = not (isFunTy (idType x))
|
|
| 394 | - |
|
| 395 | - Lit{}
|
|
| 391 | + -- Don't drop any ticks around a function
|
|
| 392 | + | isFunTy (idType x)
|
|
| 393 | + -> Tick t expr
|
|
| 394 | + -- Drop SCCs around non-function variables.
|
|
| 395 | + -- See (PSCC1) in Note [Pushing SCCs inwards].
|
|
| 396 | 396 | | tickishPlace t == PlaceCostCentre
|
| 397 | - -> rest expr -- Drop t
|
|
| 397 | + -> -- Drop pure SCC ticks: scc<foo> (x :: Int) ==> x
|
|
| 398 | + expr
|
|
| 399 | + | can_split
|
|
| 400 | + -> -- Drop the scoping part of the tick, but keep the counting part.
|
|
| 401 | + Tick (mkNoScope t) expr
|
|
| 402 | + |
|
| 403 | + -- Catch-all: annotate where we stand.
|
|
| 404 | + -- In particular (but not only): Let, most Cases.
|
|
| 405 | + _other -> Tick t expr
|
|
| 406 | + |
|
| 407 | +{- Note [Pushing SCCs inwards]
|
|
| 408 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 409 | +Amongst all ticks, SCCs have the laxest placement properties (PlaceCostCentre,
|
|
| 410 | +as described in Note [Tickish placement] GHC.Types.Tickish):
|
|
| 411 | + |
|
| 412 | + (PSCC1) SCCs around non-function variables can be eliminated.
|
|
| 413 | + The cost of evaluating the variable will be attributed to its definition
|
|
| 414 | + site, so the SCC makes no difference. Example:
|
|
| 415 | + |
|
| 416 | + scc<foo> (x :: Int) ==> x
|
|
| 417 | + |
|
| 418 | + NB: this is only valid when the variable is not a function. For example, in:
|
|
| 419 | + |
|
| 420 | + scc<foo> (f :: Int -> Int)
|
|
| 421 | + |
|
| 422 | + we must retain the cost centre annotation, as it affects the cost-centre
|
|
| 423 | + pointer when the function is called. Discarding the SCC in this case would
|
|
| 424 | + defeat the profiling mechanism entirely!
|
|
| 425 | + |
|
| 426 | + (PSCC2) SCCs can be pushed into lambdas.
|
|
| 427 | + |
|
| 428 | + scc<foo> (\x -> e) ==> \x -> scc<foo> e
|
|
| 429 | + |
|
| 430 | + (PSCC3) We can push SCCs into (saturated) constructor applications.
|
|
| 431 | + For example, for an arity 2 data constructor 'D':
|
|
| 432 | + |
|
| 433 | + scc<foo> (D e1 e2) ==> D (scc<foo> e1) (scc<foo> e2)
|
|
| 434 | + |
|
| 435 | +Now, two kinds of ticks contain SCCs:
|
|
| 436 | + |
|
| 437 | + - bare SCCs (i.e. ProfNote with profNoteCounts = False, profNoteScopes = True)
|
|
| 438 | + - profiling ticks that both count and scope
|
|
| 439 | + |
|
| 440 | +The above explanation deals with bare SCCs. When handling profiling ticks that
|
|
| 441 | +both count and scope, we can split tick into two, so that the scoping part can
|
|
| 442 | +be pushed inwards (or even discarded). Specifically, we perform the following
|
|
| 443 | +transformations:
|
|
| 444 | + |
|
| 445 | + (PSCC1) Drop the SCC around non-function variables, keeping only the counting
|
|
| 446 | + part:
|
|
| 447 | + |
|
| 448 | + scctick<foo> (x :: Int) ==> tick<foo> x
|
|
| 449 | + |
|
| 450 | + (PSCC2) Push the SCC inside lambdas:
|
|
| 451 | + |
|
| 452 | + scctick<foo> (\x. e) ==> tick<foo> (\x. scc<foo> e)
|
|
| 453 | + |
|
| 454 | + NB: we must keep the counting part outside the lambda, in order to preserve
|
|
| 455 | + tick counter tallies – it would not be sound to push the counting part inside.
|
|
| 398 | 456 | |
| 399 | - -- Catch-all: Annotate where we stand
|
|
| 400 | - _any -> Tick t $ rest expr
|
|
| 457 | + (PSCC3) Push the SCC inside saturated contructor applications.
|
|
| 458 | + |
|
| 459 | + scctick<foo> (D e1 e2) ==> tick<foo> (D (scc<foo> e1) (scc<foo> e2))
|
|
| 460 | + |
|
| 461 | +The benefit of these transformation is that the counting part, tick<foo>, can
|
|
| 462 | +likely be floated out of the way, which may expose additional optimisation
|
|
| 463 | +opportunities. For example, for (PSCC2):
|
|
| 464 | + |
|
| 465 | + (scctick<foo> (\x. e)) arg
|
|
| 466 | + |
|
| 467 | + ==>{PSCC2}
|
|
| 468 | + |
|
| 469 | + (tick<foo> (\x. scc<foo> e)) arg
|
|
| 470 | + |
|
| 471 | + ==>{GHC.Core.Opt.FloatOut.floatExpr, because 'tick<foo>' has no scope}
|
|
| 472 | + |
|
| 473 | + tick<foo> ((\x. scc<foo> e) arg)
|
|
| 474 | + |
|
| 475 | + ==>{beta reduction}
|
|
| 476 | + |
|
| 477 | + tick<foo> (let x = arg in scc<foo> e)
|
|
| 478 | + |
|
| 479 | +For (PSCC3):
|
|
| 480 | + |
|
| 481 | + case (scctick<foo> (Just x)) of { Nothing -> 0; Just y -> y + 1 }
|
|
| 482 | + |
|
| 483 | + ==>{PSCC3}
|
|
| 484 | + |
|
| 485 | + case (tick<foo> (Just (scc<foo> x))) of { Nothing -> 0; Just y -> y + 1 }
|
|
| 486 | + |
|
| 487 | + ==>{GHC.Core.Opt.FloatOut.floatExpr, because 'tick<foo>' has no scope}
|
|
| 488 | + |
|
| 489 | + tick<foo> (case Just (scc<foo> x) of { Nothing -> 0; Just y -> y + 1 })
|
|
| 490 | + |
|
| 491 | + ==>{case of known constructor}
|
|
| 492 | + |
|
| 493 | + tick<foo> (let y = scc<foo> x in y + 1)
|
|
| 494 | + |
|
| 495 | +Note [Push ticks into unsafeCoerce]
|
|
| 496 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 497 | +In #25212, we had a program of the form:
|
|
| 498 | + |
|
| 499 | + data Box = Box Any
|
|
| 500 | + asBox :: a -> Box
|
|
| 501 | + asBox x = {-# SCC asBox #-} Box (unsafeCoerce x)
|
|
| 502 | + |
|
| 503 | +As per Note [Implementing unsafeCoerce] in GHC.Internal.Unsafe.Coerce, the call
|
|
| 504 | +to `unsafeCoerce` turns into
|
|
| 505 | + |
|
| 506 | + case unsafeEqualityProof @Type @a @Any of
|
|
| 507 | + UnsafeRefl (co :: a ~# Any) -> x |> Sub co
|
|
| 508 | + |
|
| 509 | +The worker for 'asBox' is then of the form:
|
|
| 510 | + |
|
| 511 | + $wasBox = \@a (x :: a) ->
|
|
| 512 | + (# case unsafeEqualityProof @Type @a @Any of
|
|
| 513 | + UnsafeRefl (co :: a ~# Any) -> x |> Sub co
|
|
| 514 | + #)
|
|
| 515 | + |
|
| 516 | +When inserting the SCC, we push it into the constructor as per (PSCC3) in
|
|
| 517 | +Note [Pushing SCCs inwards], so we get:
|
|
| 518 | + |
|
| 519 | + $wasBox = \@a (x :: a) ->
|
|
| 520 | + tick<asBox>
|
|
| 521 | + (# scc<asBox>
|
|
| 522 | + case unsafeEqualityProof @Type @a @Any of
|
|
| 523 | + UnsafeRefl (co :: a ~# Any) -> x |> Sub co
|
|
| 524 | + #)
|
|
| 525 | + |
|
| 526 | +Now, if we don't push the SCC tick into the case statement, Core Prep will
|
|
| 527 | +see an expression like 'MkSolo# (scc<asBox> ...)', which it will ANFise to
|
|
| 528 | +'let x = scc<asBox> ... in MkSolo# x', creating an unwanted thunk in the process.
|
|
| 529 | + |
|
| 530 | +So the strategy is to treat this 'unsafeEqualityProof' case statement as if it
|
|
| 531 | +was a cast. We thus push the SCC into the RHS of the pattern match:
|
|
| 532 | + |
|
| 533 | + $wasBox = \@a (x :: a) ->
|
|
| 534 | + tick<asBox>
|
|
| 535 | + (# case unsafeEqualityProof @Type @a @Any of
|
|
| 536 | + UnsafeRefl (co :: a ~# Any) -> scc<asBox> x |> Sub co
|
|
| 537 | + #)
|
|
| 538 | + |
|
| 539 | +Then the SCC completely evaporates, as per (PSCC1) in Note [Pushing SCCs inwards].
|
|
| 540 | + |
|
| 541 | +Note [No ticks around types or coercions]
|
|
| 542 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 543 | +It doesn't make much sense to put a tick around a type or a coercion, as both
|
|
| 544 | +types and coercions are erased in the end.
|
|
| 545 | + |
|
| 546 | +In fact, it is quite dangerous to add a tick around types or coercions, because
|
|
| 547 | +the optimiser does not robustly look through ticks:
|
|
| 548 | + |
|
| 549 | + - 'GHC.Core.SimpleOpt.simple_bind_pair' does not look through ticks when
|
|
| 550 | + looking at the RHS to decide whether it is a Type or Coercion,
|
|
| 551 | + - 'GHC.Core.Opt.Simplify.Iteration.completeBind' does not look through ticks
|
|
| 552 | + when looking at the RHS of an CoVar binding.
|
|
| 553 | + |
|
| 554 | +This means it is vital to drop ticks around types/coercions:
|
|
| 555 | + |
|
| 556 | + - (#26941) Core Lint rejects bindings of the form "let co = tick ..."
|
|
| 557 | + in which the LHS is a CoVar and the RHS is a ticked Coercion.
|
|
| 558 | + - (#27121) The simplifier mis-handles ticked coercion bindings, which can
|
|
| 559 | + result in 'lookupIdSubst' panics (due to failing to extend the substitution
|
|
| 560 | + with a coercion).
|
|
| 561 | +-}
|
|
| 401 | 562 | |
| 402 | 563 | mkTicks :: [CoreTickish] -> CoreExpr -> CoreExpr
|
| 403 | 564 | mkTicks ticks expr = foldr mkTick expr ticks
|
| ... | ... | @@ -2573,8 +2734,8 @@ exprIsTickedString = isJust . exprIsTickedString_maybe |
| 2573 | 2734 | exprIsTickedString_maybe :: CoreExpr -> Maybe ByteString
|
| 2574 | 2735 | exprIsTickedString_maybe (Lit (LitString bs)) = Just bs
|
| 2575 | 2736 | exprIsTickedString_maybe (Tick t e)
|
| 2576 | - -- we don't tick literals with CostCentre ticks, compare to mkTick
|
|
| 2577 | - | tickishPlace t == PlaceCostCentre = Nothing
|
|
| 2737 | + -- Shortcut: ticks with code never wrap literals (compare with 'mkTick')
|
|
| 2738 | + | tickishIsCode t = Nothing
|
|
| 2578 | 2739 | | otherwise = exprIsTickedString_maybe e
|
| 2579 | 2740 | exprIsTickedString_maybe _ = Nothing
|
| 2580 | 2741 |
| ... | ... | @@ -17,6 +17,7 @@ module GHC.Types.Tickish ( |
| 17 | 17 | TickishPlacement(..),
|
| 18 | 18 | tickishPlace,
|
| 19 | 19 | tickishContains,
|
| 20 | + combineTickish_maybe,
|
|
| 20 | 21 | |
| 21 | 22 | -- * Breakpoint tick identifiers
|
| 22 | 23 | BreakpointId(..), BreakTickIndex
|
| ... | ... | @@ -261,8 +262,12 @@ Ticks have two independent attributes: |
| 261 | 262 | |
| 262 | 263 | See Note [Scoped ticks]
|
| 263 | 264 | |
| 265 | +Note that profiling notes which both count and scope can be split into two
|
|
| 266 | +separate ticks, one that counts and doesn't scope and one that scopes and doesn't
|
|
| 267 | +count; see 'tickishCanSplit', 'mkNoCount' and 'mkNoScope'.
|
|
| 268 | + |
|
| 264 | 269 | Note [Counting ticks]
|
| 265 | -~~~~~~~~~~~~~~~~~~~~
|
|
| 270 | +~~~~~~~~~~~~~~~~~~~~~
|
|
| 266 | 271 | The following ticks count:
|
| 267 | 272 | - ProfNote ticks with profNoteCounts = True
|
| 268 | 273 | - HPC ticks
|
| ... | ... | @@ -290,7 +295,7 @@ sharing, so in practice the actual number of ticks may vary, except |
| 290 | 295 | that we never change the value from zero to non-zero or vice-versa.
|
| 291 | 296 | |
| 292 | 297 | Note [Scoped ticks]
|
| 293 | -~~~~~~~~~~~~~~~~~~~~
|
|
| 298 | +~~~~~~~~~~~~~~~~~~~
|
|
| 294 | 299 | The following ticks are scoped:
|
| 295 | 300 | - ProfNote ticks with profNoteScope = True
|
| 296 | 301 | - Breakpoints
|
| ... | ... | @@ -375,6 +380,61 @@ Whether we are allowed to float in additional cost depends on the tick: |
| 375 | 380 | |
| 376 | 381 | While these transformations are legal, we want to make a best effort to
|
| 377 | 382 | only make use of them where it exposes transformation opportunities.
|
| 383 | + |
|
| 384 | +Note [Tickish placement]
|
|
| 385 | +~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 386 | +The placement behaviour of ticks (i.e. which nodes we want the tick to be placed
|
|
| 387 | +around in the AST) is governed by 'TickishPlacement'.
|
|
| 388 | +From most restrictive to least restrictive placement rules:
|
|
| 389 | + |
|
| 390 | + - PlaceRuntime: counting ticks.
|
|
| 391 | + |
|
| 392 | + Ticks with 'PlaceRuntime' placement want to be placed around run-time
|
|
| 393 | + expressions. They can be moved through pure compile-time constructs such as
|
|
| 394 | + other type arguments, casts, or type lambdas:
|
|
| 395 | + |
|
| 396 | + tick <t> (f @ty) ==> (tick <t> f) @ty
|
|
| 397 | + tick <t> (e |> co) ==> (tick <t> e) |> co
|
|
| 398 | + tick <t> (/\a. e) ==> /\a. tick <t> e
|
|
| 399 | + |
|
| 400 | + This is the most restrictive placement rule for ticks, as all tickishs have
|
|
| 401 | + in common that they want to track runtime behaviour.
|
|
| 402 | + |
|
| 403 | + Any tick that counts (see Note [Counting ticks]) has 'PlaceRuntime' placement.
|
|
| 404 | + |
|
| 405 | + - PlaceNonLam: source notes.
|
|
| 406 | + |
|
| 407 | + Like PlaceRuntime, but we can also float the tick through value lambdas:
|
|
| 408 | + |
|
| 409 | + tick <t> (\x. e) ==> \x. tick <t> e
|
|
| 410 | + |
|
| 411 | + This makes sense where there is little difference between annotating the
|
|
| 412 | + lambda and annotating the lambda's code.
|
|
| 413 | + |
|
| 414 | + - PlaceCostCentre: non-counting profiling ticks.
|
|
| 415 | + |
|
| 416 | + In addition to floating through lambdas, cost-centre style tickishs can be
|
|
| 417 | + pushed into (saturated) constructor applications, and can be eliminated when
|
|
| 418 | + placed around non-function variables:
|
|
| 419 | + |
|
| 420 | + tick <t> (C e1 e2) ==> C (tick <t> e1) (tick <t> e2)
|
|
| 421 | + |
|
| 422 | + tick <t> (x :: Int) ==> (x :: Int)
|
|
| 423 | + |
|
| 424 | + Neither the constructor application nor the variable 'x' are likely to have
|
|
| 425 | + any cost worth mentioning.
|
|
| 426 | + |
|
| 427 | +We generally try to push ticks inwards until they end up placed around a Core
|
|
| 428 | +expression that is appropriate for their placement rule, as described above.
|
|
| 429 | +This gives us the opportunity to eliminate the tick, either by combining it with
|
|
| 430 | +another tick (see 'combineTickish_maybe') or by dropping it altogether. For
|
|
| 431 | +example, a (non-counting) SCC around a non-function variable can be dropped, as
|
|
| 432 | +there is no cost to scope over.
|
|
| 433 | + |
|
| 434 | +After the tick has been placed by 'mkTick', the simplifier may later (during
|
|
| 435 | +simplification) decide to float it outwards (see e.g. GHC.Core.Opt.Simplify.Iteration.simplTick).
|
|
| 436 | +The story here is not fully worked out, as the simplifier calls 'mkTick', which
|
|
| 437 | +might push the tick inwards again.
|
|
| 378 | 438 | -}
|
| 379 | 439 | |
| 380 | 440 | -- | Returns @True@ for ticks that can be floated upwards easily even
|
| ... | ... | @@ -441,35 +501,19 @@ isProfTick _ = False |
| 441 | 501 | -- annotating for example using @mkTick@. If we find that we want to
|
| 442 | 502 | -- put a tickish on an expression ruled out here, we try to float it
|
| 443 | 503 | -- inwards until we find a suitable expression.
|
| 504 | +--
|
|
| 505 | +-- See Note [Tickish placement].
|
|
| 444 | 506 | data TickishPlacement =
|
| 445 | 507 | |
| 446 | - -- | Place ticks exactly on run-time expressions. We can still
|
|
| 447 | - -- move the tick through pure compile-time constructs such as
|
|
| 448 | - -- other ticks, casts or type lambdas. This is the most
|
|
| 449 | - -- restrictive placement rule for ticks, as all tickishs have in
|
|
| 450 | - -- common that they want to track runtime processes. The only
|
|
| 451 | - -- legal placement rule for counting ticks.
|
|
| 452 | - -- NB: We generally try to move these as close to the relevant
|
|
| 453 | - -- runtime expression as possible. This means they get pushed through
|
|
| 454 | - -- tyoe arguments. E.g. we create `(tick f) @Bool` instead of `tick (f @Bool)`.
|
|
| 508 | + -- | Place ticks exactly on run-time expressions, moving them through pure
|
|
| 509 | + -- compile-time constructs such as other ticks, casts or type lambdas.
|
|
| 455 | 510 | PlaceRuntime
|
| 456 | 511 | |
| 457 | - -- | As @PlaceRuntime@, but we float the tick through all
|
|
| 458 | - -- lambdas. This makes sense where there is little difference
|
|
| 459 | - -- between annotating the lambda and annotating the lambda's code.
|
|
| 512 | + -- | As @PlaceRuntime@, but also allow to float the tick through all lambdas.
|
|
| 460 | 513 | | PlaceNonLam
|
| 461 | 514 | |
| 462 | - -- | In addition to floating through lambdas, cost-centre style
|
|
| 463 | - -- tickishs can also be moved from constructors, non-function
|
|
| 464 | - -- variables and literals. For example:
|
|
| 465 | - --
|
|
| 466 | - -- let x = scc<...> C (scc<...> y) (scc<...> 3) in ...
|
|
| 467 | - --
|
|
| 468 | - -- Neither the constructor application, the variable or the
|
|
| 469 | - -- literal are likely to have any cost worth mentioning. And even
|
|
| 470 | - -- if y names a thunk, the call would not care about the
|
|
| 471 | - -- evaluation context. Therefore removing all annotations in the
|
|
| 472 | - -- above example is safe.
|
|
| 515 | + -- | As 'PlaceNonLam', but also float through constructors, non-function
|
|
| 516 | + -- variables and literals.
|
|
| 473 | 517 | | PlaceCostCentre
|
| 474 | 518 | |
| 475 | 519 | deriving (Eq,Show)
|
| ... | ... | @@ -477,7 +521,9 @@ data TickishPlacement = |
| 477 | 521 | instance Outputable TickishPlacement where
|
| 478 | 522 | ppr = text . show
|
| 479 | 523 | |
| 480 | --- | Placement behaviour we want for the ticks
|
|
| 524 | +-- | Placement behaviour we want for the ticks.
|
|
| 525 | +--
|
|
| 526 | +-- See Note [Tickish placement].
|
|
| 481 | 527 | tickishPlace :: GenTickish pass -> TickishPlacement
|
| 482 | 528 | tickishPlace n@ProfNote{}
|
| 483 | 529 | | profNoteCount n = PlaceRuntime
|
| ... | ... | @@ -486,6 +532,43 @@ tickishPlace HpcTick{} = PlaceRuntime |
| 486 | 532 | tickishPlace Breakpoint{} = PlaceRuntime
|
| 487 | 533 | tickishPlace SourceNote{} = PlaceNonLam
|
| 488 | 534 | |
| 535 | +-- | Merge two ticks into one, if that is possible.
|
|
| 536 | +--
|
|
| 537 | +-- Examples:
|
|
| 538 | +--
|
|
| 539 | +-- - combine two source note ticks if one contains the other,
|
|
| 540 | +-- - combine a non-counting profiling tick with a non-scoping profiling tick
|
|
| 541 | +-- for the same cost centre
|
|
| 542 | +-- - combine two equal breakpoint ticks or HPC ticks
|
|
| 543 | +combineTickish_maybe :: Eq (GenTickish pass)
|
|
| 544 | + => GenTickish pass -> GenTickish pass -> Maybe (GenTickish pass)
|
|
| 545 | +combineTickish_maybe
|
|
| 546 | + (ProfNote { profNoteCC = cc1, profNoteCount = cnt1, profNoteScope = scope1 })
|
|
| 547 | + (ProfNote { profNoteCC = cc2, profNoteCount = cnt2, profNoteScope = scope2 })
|
|
| 548 | + | cc1 == cc2
|
|
| 549 | + , not cnt1 || not cnt2
|
|
| 550 | + = Just $ ProfNote { profNoteCC = cc1
|
|
| 551 | + , profNoteCount = cnt1 || cnt2
|
|
| 552 | + , profNoteScope = scope1 || scope2
|
|
| 553 | + }
|
|
| 554 | +combineTickish_maybe t1@(SourceNote sp1 n1) t2@(SourceNote sp2 n2)
|
|
| 555 | + | n1 == n2
|
|
| 556 | + , sp1 `containsSpan` sp2
|
|
| 557 | + = Just t1
|
|
| 558 | + | n1 == n2
|
|
| 559 | + , sp2 `containsSpan` sp1
|
|
| 560 | + = Just t2
|
|
| 561 | + -- NB: it would be possible to use 'combineRealSrcSpans' instead,
|
|
| 562 | + -- but that has the risk of combining many source note ticks into a single
|
|
| 563 | + -- tick with a huge source span.
|
|
| 564 | +combineTickish_maybe t1@(HpcTick {}) t2@(HpcTick {})
|
|
| 565 | + | t1 == t2
|
|
| 566 | + = Just t1
|
|
| 567 | +combineTickish_maybe t1@(Breakpoint {}) t2@(Breakpoint {})
|
|
| 568 | + | t1 == t2
|
|
| 569 | + = Just t1
|
|
| 570 | +combineTickish_maybe _ _ = Nothing
|
|
| 571 | + |
|
| 489 | 572 | -- | Returns whether one tick "contains" the other one, therefore
|
| 490 | 573 | -- making the second tick redundant.
|
| 491 | 574 | tickishContains :: Eq (GenTickish pass)
|
| ... | ... | @@ -48,7 +48,9 @@ main = do |
| 48 | 48 | assertEqual (cc_module myCostCentre) "Main"
|
| 49 | 49 | assertEqual (cc_srcloc myCostCentre) (Just "tso_and_stack_closures.hs:24:48-80")
|
| 50 | 50 | assertEqual (cc_is_caf myCostCentre) False
|
| 51 | - Nothing -> error $ "MyCostCentre not found in:\n" ++ unlines (cc_label <$> linkedCostCentres costCentre)
|
|
| 51 | + Nothing -> error "MyCostCentre not found"
|
|
| 52 | + -- Don't print all of 'linkedCostCentres costCentre',
|
|
| 53 | + -- as that is ~20k lines of output.
|
|
| 52 | 54 | #endif
|
| 53 | 55 | |
| 54 | 56 | linkedCostCentres :: Maybe CostCentre -> [CostCentre]
|
| 1 | +module T27121 where
|
|
| 2 | + |
|
| 3 | +import T27121_aux
|
|
| 4 | + |
|
| 5 | +updateFileDiagnostics
|
|
| 6 | + :: LanguageContextEnv ()
|
|
| 7 | + -> IO ()
|
|
| 8 | +updateFileDiagnostics env = do
|
|
| 9 | + withTrace $ \ _tag ->
|
|
| 10 | + runLspT env $ do
|
|
| 11 | + sendNotification SMethod_TextDocumentPublishDiagnostics
|
|
| 12 | + PublishDiagnosticsParams |
| 1 | +{-# LANGUAGE DataKinds #-}
|
|
| 2 | +{-# LANGUAGE DerivingStrategies #-}
|
|
| 3 | +{-# LANGUAGE DuplicateRecordFields #-}
|
|
| 4 | +{-# LANGUAGE FunctionalDependencies #-}
|
|
| 5 | +{-# LANGUAGE LambdaCase #-}
|
|
| 6 | +{-# LANGUAGE RoleAnnotations #-}
|
|
| 7 | +{-# LANGUAGE TypeFamilies #-}
|
|
| 8 | + |
|
| 9 | +module T27121_aux
|
|
| 10 | + ( withTrace
|
|
| 11 | + , sendNotification
|
|
| 12 | + , LspT, runLspT
|
|
| 13 | + , SMethod(..)
|
|
| 14 | + , LanguageContextEnv
|
|
| 15 | + , PublishDiagnosticsParams(..)
|
|
| 16 | + )
|
|
| 17 | + where
|
|
| 18 | + |
|
| 19 | +-- base
|
|
| 20 | +import Control.Monad.IO.Class ( MonadIO, liftIO )
|
|
| 21 | +import Data.Kind ( Type )
|
|
| 22 | +import GHC.TypeLits ( Symbol )
|
|
| 23 | + |
|
| 24 | +--------------------------------------------------------------------------------
|
|
| 25 | + |
|
| 26 | +withTrace :: Monad m => ((String -> String -> m ()) -> m a) -> m a
|
|
| 27 | +withTrace act
|
|
| 28 | + | myUserTracingEnabled
|
|
| 29 | + = return undefined
|
|
| 30 | + | otherwise = act (\_ _ -> pure ())
|
|
| 31 | +{-# NOINLINE withTrace #-}
|
|
| 32 | + |
|
| 33 | +myUserTracingEnabled :: Bool
|
|
| 34 | +myUserTracingEnabled = False
|
|
| 35 | +{-# NOINLINE myUserTracingEnabled #-}
|
|
| 36 | + |
|
| 37 | +type Text = String
|
|
| 38 | + |
|
| 39 | +newtype LspT config a = LspT {unLspT :: LanguageContextEnv config -> IO a}
|
|
| 40 | + |
|
| 41 | +instance Functor (LspT config) where
|
|
| 42 | + fmap f (LspT g) = LspT (fmap f . g)
|
|
| 43 | + |
|
| 44 | +instance Applicative (LspT config) where
|
|
| 45 | + pure = LspT . const . pure
|
|
| 46 | + LspT f <*> LspT a = LspT $ \ env -> f env <*> a env
|
|
| 47 | +instance Monad (LspT config) where
|
|
| 48 | + LspT a >>= f = LspT $ \ env -> do
|
|
| 49 | + b <- a env
|
|
| 50 | + unLspT ( f b ) env
|
|
| 51 | +instance MonadIO (LspT config) where
|
|
| 52 | + liftIO = LspT . const . liftIO
|
|
| 53 | + |
|
| 54 | +type role LspT representational nominal
|
|
| 55 | + |
|
| 56 | +runLspT :: LanguageContextEnv config -> LspT config a -> IO a
|
|
| 57 | +runLspT env (LspT f) = f env
|
|
| 58 | +{-# INLINE runLspT #-}
|
|
| 59 | + |
|
| 60 | +data PublishDiagnosticsParams = PublishDiagnosticsParams
|
|
| 61 | + |
|
| 62 | +data LanguageContextEnv config =
|
|
| 63 | + LanguageContextEnv
|
|
| 64 | + { resSendMessage :: FromServerMessage -> IO () }
|
|
| 65 | + |
|
| 66 | + |
|
| 67 | +sendNotification ::
|
|
| 68 | + forall (m :: Method ServerToClient Notification) f config.
|
|
| 69 | + MonadLsp config f =>
|
|
| 70 | + SServerMethod m ->
|
|
| 71 | + MessageParams m ->
|
|
| 72 | + f ()
|
|
| 73 | +sendNotification m params =
|
|
| 74 | + let msg = TNotificationMessage { _method = m, _params = params }
|
|
| 75 | + in case splitServerMethod m of
|
|
| 76 | + IsServerNot -> sendToClient $ fromServerNot msg
|
|
| 77 | + |
|
| 78 | +type Method :: MessageDirection -> MessageKind -> Type
|
|
| 79 | +data Method f t where
|
|
| 80 | + Method_TextDocumentImplementation :: Method ClientToServer Request
|
|
| 81 | + Method_TextDocumentTypeDefinition :: Method ClientToServer Request
|
|
| 82 | + Method_WorkspaceWorkspaceFolders :: Method ServerToClient Request
|
|
| 83 | + Method_WorkspaceConfiguration :: Method ServerToClient Request
|
|
| 84 | + Method_TextDocumentDocumentColor :: Method ClientToServer Request
|
|
| 85 | + Method_TextDocumentColorPresentation :: Method ClientToServer Request
|
|
| 86 | + Method_TextDocumentFoldingRange :: Method ClientToServer Request
|
|
| 87 | + Method_TextDocumentDeclaration :: Method ClientToServer Request
|
|
| 88 | + Method_TextDocumentSelectionRange :: Method ClientToServer Request
|
|
| 89 | + Method_WindowWorkDoneProgressCreate :: Method ServerToClient Request
|
|
| 90 | + Method_TextDocumentPrepareCallHierarchy :: Method ClientToServer Request
|
|
| 91 | + Method_CallHierarchyIncomingCalls :: Method ClientToServer Request
|
|
| 92 | + Method_CallHierarchyOutgoingCalls :: Method ClientToServer Request
|
|
| 93 | + Method_TextDocumentSemanticTokensFull :: Method ClientToServer Request
|
|
| 94 | + Method_TextDocumentSemanticTokensFullDelta :: Method ClientToServer Request
|
|
| 95 | + Method_TextDocumentSemanticTokensRange :: Method ClientToServer Request
|
|
| 96 | + Method_WorkspaceSemanticTokensRefresh :: Method ServerToClient Request
|
|
| 97 | + Method_WindowShowDocument :: Method ServerToClient Request
|
|
| 98 | + Method_TextDocumentLinkedEditingRange :: Method ClientToServer Request
|
|
| 99 | + Method_WorkspaceWillCreateFiles :: Method ClientToServer Request
|
|
| 100 | + Method_WorkspaceWillRenameFiles :: Method ClientToServer Request
|
|
| 101 | + Method_WorkspaceWillDeleteFiles :: Method ClientToServer Request
|
|
| 102 | + Method_TextDocumentMoniker :: Method ClientToServer Request
|
|
| 103 | + Method_TextDocumentPrepareTypeHierarchy :: Method ClientToServer Request
|
|
| 104 | + Method_TypeHierarchySupertypes :: Method ClientToServer Request
|
|
| 105 | + Method_TypeHierarchySubtypes :: Method ClientToServer Request
|
|
| 106 | + Method_TextDocumentInlineValue :: Method ClientToServer Request
|
|
| 107 | + Method_WorkspaceInlineValueRefresh :: Method ServerToClient Request
|
|
| 108 | + Method_TextDocumentInlayHint :: Method ClientToServer Request
|
|
| 109 | + Method_InlayHintResolve :: Method ClientToServer Request
|
|
| 110 | + Method_WorkspaceInlayHintRefresh :: Method ServerToClient Request
|
|
| 111 | + Method_TextDocumentDiagnostic :: Method ClientToServer Request
|
|
| 112 | + Method_WorkspaceDiagnostic :: Method ClientToServer Request
|
|
| 113 | + Method_WorkspaceDiagnosticRefresh :: Method ServerToClient Request
|
|
| 114 | + Method_ClientRegisterCapability :: Method ServerToClient Request
|
|
| 115 | + Method_ClientUnregisterCapability :: Method ServerToClient Request
|
|
| 116 | + Method_Initialize :: Method ClientToServer Request
|
|
| 117 | + Method_Shutdown :: Method ClientToServer Request
|
|
| 118 | + Method_WindowShowMessageRequest :: Method ServerToClient Request
|
|
| 119 | + Method_TextDocumentWillSaveWaitUntil :: Method ClientToServer Request
|
|
| 120 | + Method_TextDocumentCompletion :: Method ClientToServer Request
|
|
| 121 | + Method_CompletionItemResolve :: Method ClientToServer Request
|
|
| 122 | + Method_TextDocumentHover :: Method ClientToServer Request
|
|
| 123 | + Method_TextDocumentSignatureHelp :: Method ClientToServer Request
|
|
| 124 | + Method_TextDocumentDefinition :: Method ClientToServer Request
|
|
| 125 | + Method_TextDocumentReferences :: Method ClientToServer Request
|
|
| 126 | + Method_TextDocumentDocumentHighlight :: Method ClientToServer Request
|
|
| 127 | + Method_TextDocumentDocumentSymbol :: Method ClientToServer Request
|
|
| 128 | + Method_TextDocumentCodeAction :: Method ClientToServer Request
|
|
| 129 | + Method_CodeActionResolve :: Method ClientToServer Request
|
|
| 130 | + Method_WorkspaceSymbol :: Method ClientToServer Request
|
|
| 131 | + Method_WorkspaceSymbolResolve :: Method ClientToServer Request
|
|
| 132 | + Method_TextDocumentCodeLens :: Method ClientToServer Request
|
|
| 133 | + Method_CodeLensResolve :: Method ClientToServer Request
|
|
| 134 | + Method_WorkspaceCodeLensRefresh :: Method ServerToClient Request
|
|
| 135 | + Method_TextDocumentDocumentLink :: Method ClientToServer Request
|
|
| 136 | + Method_DocumentLinkResolve :: Method ClientToServer Request
|
|
| 137 | + Method_TextDocumentFormatting :: Method ClientToServer Request
|
|
| 138 | + Method_TextDocumentRangeFormatting :: Method ClientToServer Request
|
|
| 139 | + Method_TextDocumentOnTypeFormatting :: Method ClientToServer Request
|
|
| 140 | + Method_TextDocumentRename :: Method ClientToServer Request
|
|
| 141 | + Method_TextDocumentPrepareRename :: Method ClientToServer Request
|
|
| 142 | + Method_WorkspaceExecuteCommand :: Method ClientToServer Request
|
|
| 143 | + Method_WorkspaceApplyEdit :: Method ServerToClient Request
|
|
| 144 | + Method_WorkspaceDidChangeWorkspaceFolders :: Method ClientToServer Notification
|
|
| 145 | + Method_WindowWorkDoneProgressCancel :: Method ClientToServer Notification
|
|
| 146 | + Method_WorkspaceDidCreateFiles :: Method ClientToServer Notification
|
|
| 147 | + Method_WorkspaceDidRenameFiles :: Method ClientToServer Notification
|
|
| 148 | + Method_WorkspaceDidDeleteFiles :: Method ClientToServer Notification
|
|
| 149 | + Method_NotebookDocumentDidOpen :: Method ClientToServer Notification
|
|
| 150 | + Method_NotebookDocumentDidChange :: Method ClientToServer Notification
|
|
| 151 | + Method_NotebookDocumentDidSave :: Method ClientToServer Notification
|
|
| 152 | + Method_NotebookDocumentDidClose :: Method ClientToServer Notification
|
|
| 153 | + Method_Initialized :: Method ClientToServer Notification
|
|
| 154 | + Method_Exit :: Method ClientToServer Notification
|
|
| 155 | + Method_WorkspaceDidChangeConfiguration :: Method ClientToServer Notification
|
|
| 156 | + Method_WindowShowMessage :: Method ServerToClient Notification
|
|
| 157 | + Method_WindowLogMessage :: Method ServerToClient Notification
|
|
| 158 | + Method_TelemetryEvent :: Method ServerToClient Notification
|
|
| 159 | + Method_TextDocumentDidOpen :: Method ClientToServer Notification
|
|
| 160 | + Method_TextDocumentDidChange :: Method ClientToServer Notification
|
|
| 161 | + Method_TextDocumentDidClose :: Method ClientToServer Notification
|
|
| 162 | + Method_TextDocumentDidSave :: Method ClientToServer Notification
|
|
| 163 | + Method_TextDocumentWillSave :: Method ClientToServer Notification
|
|
| 164 | + Method_WorkspaceDidChangeWatchedFiles :: Method ClientToServer Notification
|
|
| 165 | + Method_TextDocumentPublishDiagnostics :: Method ServerToClient Notification
|
|
| 166 | + Method_SetTrace :: Method ClientToServer Notification
|
|
| 167 | + Method_LogTrace :: Method ServerToClient Notification
|
|
| 168 | + Method_CancelRequest :: Method f Notification
|
|
| 169 | + Method_Progress :: Method f Notification
|
|
| 170 | + Method_CustomMethod :: Symbol -> Method f t
|
|
| 171 | + |
|
| 172 | +type SMethod :: forall f t . Method f t -> Type
|
|
| 173 | +data SMethod m where
|
|
| 174 | + SMethod_TextDocumentImplementation :: SMethod Method_TextDocumentImplementation
|
|
| 175 | + SMethod_TextDocumentTypeDefinition :: SMethod Method_TextDocumentTypeDefinition
|
|
| 176 | + SMethod_WorkspaceWorkspaceFolders :: SMethod Method_WorkspaceWorkspaceFolders
|
|
| 177 | + SMethod_WorkspaceConfiguration :: SMethod Method_WorkspaceConfiguration
|
|
| 178 | + SMethod_TextDocumentDocumentColor :: SMethod Method_TextDocumentDocumentColor
|
|
| 179 | + SMethod_TextDocumentColorPresentation :: SMethod Method_TextDocumentColorPresentation
|
|
| 180 | + SMethod_TextDocumentFoldingRange :: SMethod Method_TextDocumentFoldingRange
|
|
| 181 | + SMethod_TextDocumentDeclaration :: SMethod Method_TextDocumentDeclaration
|
|
| 182 | + SMethod_TextDocumentSelectionRange :: SMethod Method_TextDocumentSelectionRange
|
|
| 183 | + SMethod_WindowWorkDoneProgressCreate :: SMethod Method_WindowWorkDoneProgressCreate
|
|
| 184 | + SMethod_TextDocumentPrepareCallHierarchy :: SMethod Method_TextDocumentPrepareCallHierarchy
|
|
| 185 | + SMethod_CallHierarchyIncomingCalls :: SMethod Method_CallHierarchyIncomingCalls
|
|
| 186 | + SMethod_CallHierarchyOutgoingCalls :: SMethod Method_CallHierarchyOutgoingCalls
|
|
| 187 | + SMethod_TextDocumentSemanticTokensFull :: SMethod Method_TextDocumentSemanticTokensFull
|
|
| 188 | + SMethod_TextDocumentSemanticTokensFullDelta :: SMethod Method_TextDocumentSemanticTokensFullDelta
|
|
| 189 | + SMethod_TextDocumentSemanticTokensRange :: SMethod Method_TextDocumentSemanticTokensRange
|
|
| 190 | + SMethod_WorkspaceSemanticTokensRefresh :: SMethod Method_WorkspaceSemanticTokensRefresh
|
|
| 191 | + SMethod_WindowShowDocument :: SMethod Method_WindowShowDocument
|
|
| 192 | + SMethod_TextDocumentLinkedEditingRange :: SMethod Method_TextDocumentLinkedEditingRange
|
|
| 193 | + SMethod_WorkspaceWillCreateFiles :: SMethod Method_WorkspaceWillCreateFiles
|
|
| 194 | + SMethod_WorkspaceWillRenameFiles :: SMethod Method_WorkspaceWillRenameFiles
|
|
| 195 | + SMethod_WorkspaceWillDeleteFiles :: SMethod Method_WorkspaceWillDeleteFiles
|
|
| 196 | + SMethod_TextDocumentMoniker :: SMethod Method_TextDocumentMoniker
|
|
| 197 | + SMethod_TextDocumentPrepareTypeHierarchy :: SMethod Method_TextDocumentPrepareTypeHierarchy
|
|
| 198 | + SMethod_TypeHierarchySupertypes :: SMethod Method_TypeHierarchySupertypes
|
|
| 199 | + SMethod_TypeHierarchySubtypes :: SMethod Method_TypeHierarchySubtypes
|
|
| 200 | + SMethod_TextDocumentInlineValue :: SMethod Method_TextDocumentInlineValue
|
|
| 201 | + SMethod_WorkspaceInlineValueRefresh :: SMethod Method_WorkspaceInlineValueRefresh
|
|
| 202 | + SMethod_TextDocumentInlayHint :: SMethod Method_TextDocumentInlayHint
|
|
| 203 | + SMethod_InlayHintResolve :: SMethod Method_InlayHintResolve
|
|
| 204 | + SMethod_WorkspaceInlayHintRefresh :: SMethod Method_WorkspaceInlayHintRefresh
|
|
| 205 | + SMethod_TextDocumentDiagnostic :: SMethod Method_TextDocumentDiagnostic
|
|
| 206 | + SMethod_WorkspaceDiagnostic :: SMethod Method_WorkspaceDiagnostic
|
|
| 207 | + SMethod_WorkspaceDiagnosticRefresh :: SMethod Method_WorkspaceDiagnosticRefresh
|
|
| 208 | + SMethod_ClientRegisterCapability :: SMethod Method_ClientRegisterCapability
|
|
| 209 | + SMethod_ClientUnregisterCapability :: SMethod Method_ClientUnregisterCapability
|
|
| 210 | + SMethod_Initialize :: SMethod Method_Initialize
|
|
| 211 | + SMethod_Shutdown :: SMethod Method_Shutdown
|
|
| 212 | + SMethod_WindowShowMessageRequest :: SMethod Method_WindowShowMessageRequest
|
|
| 213 | + SMethod_TextDocumentWillSaveWaitUntil :: SMethod Method_TextDocumentWillSaveWaitUntil
|
|
| 214 | + SMethod_TextDocumentCompletion :: SMethod Method_TextDocumentCompletion
|
|
| 215 | + SMethod_CompletionItemResolve :: SMethod Method_CompletionItemResolve
|
|
| 216 | + SMethod_TextDocumentHover :: SMethod Method_TextDocumentHover
|
|
| 217 | + SMethod_TextDocumentSignatureHelp :: SMethod Method_TextDocumentSignatureHelp
|
|
| 218 | + SMethod_TextDocumentDefinition :: SMethod Method_TextDocumentDefinition
|
|
| 219 | + SMethod_TextDocumentReferences :: SMethod Method_TextDocumentReferences
|
|
| 220 | + SMethod_TextDocumentDocumentHighlight :: SMethod Method_TextDocumentDocumentHighlight
|
|
| 221 | + SMethod_TextDocumentDocumentSymbol :: SMethod Method_TextDocumentDocumentSymbol
|
|
| 222 | + SMethod_TextDocumentCodeAction :: SMethod Method_TextDocumentCodeAction
|
|
| 223 | + SMethod_CodeActionResolve :: SMethod Method_CodeActionResolve
|
|
| 224 | + SMethod_WorkspaceSymbol :: SMethod Method_WorkspaceSymbol
|
|
| 225 | + SMethod_WorkspaceSymbolResolve :: SMethod Method_WorkspaceSymbolResolve
|
|
| 226 | + SMethod_TextDocumentCodeLens :: SMethod Method_TextDocumentCodeLens
|
|
| 227 | + SMethod_CodeLensResolve :: SMethod Method_CodeLensResolve
|
|
| 228 | + SMethod_WorkspaceCodeLensRefresh :: SMethod Method_WorkspaceCodeLensRefresh
|
|
| 229 | + SMethod_TextDocumentDocumentLink :: SMethod Method_TextDocumentDocumentLink
|
|
| 230 | + SMethod_DocumentLinkResolve :: SMethod Method_DocumentLinkResolve
|
|
| 231 | + SMethod_TextDocumentFormatting :: SMethod Method_TextDocumentFormatting
|
|
| 232 | + SMethod_TextDocumentRangeFormatting :: SMethod Method_TextDocumentRangeFormatting
|
|
| 233 | + SMethod_TextDocumentOnTypeFormatting :: SMethod Method_TextDocumentOnTypeFormatting
|
|
| 234 | + SMethod_TextDocumentRename :: SMethod Method_TextDocumentRename
|
|
| 235 | + SMethod_TextDocumentPrepareRename :: SMethod Method_TextDocumentPrepareRename
|
|
| 236 | + SMethod_WorkspaceExecuteCommand :: SMethod Method_WorkspaceExecuteCommand
|
|
| 237 | + SMethod_WorkspaceApplyEdit :: SMethod Method_WorkspaceApplyEdit
|
|
| 238 | + SMethod_WorkspaceDidChangeWorkspaceFolders :: SMethod Method_WorkspaceDidChangeWorkspaceFolders
|
|
| 239 | + SMethod_WindowWorkDoneProgressCancel :: SMethod Method_WindowWorkDoneProgressCancel
|
|
| 240 | + SMethod_WorkspaceDidCreateFiles :: SMethod Method_WorkspaceDidCreateFiles
|
|
| 241 | + SMethod_WorkspaceDidRenameFiles :: SMethod Method_WorkspaceDidRenameFiles
|
|
| 242 | + SMethod_WorkspaceDidDeleteFiles :: SMethod Method_WorkspaceDidDeleteFiles
|
|
| 243 | + SMethod_NotebookDocumentDidOpen :: SMethod Method_NotebookDocumentDidOpen
|
|
| 244 | + SMethod_NotebookDocumentDidChange :: SMethod Method_NotebookDocumentDidChange
|
|
| 245 | + SMethod_NotebookDocumentDidSave :: SMethod Method_NotebookDocumentDidSave
|
|
| 246 | + SMethod_NotebookDocumentDidClose :: SMethod Method_NotebookDocumentDidClose
|
|
| 247 | + SMethod_Initialized :: SMethod Method_Initialized
|
|
| 248 | + SMethod_Exit :: SMethod Method_Exit
|
|
| 249 | + SMethod_WorkspaceDidChangeConfiguration :: SMethod Method_WorkspaceDidChangeConfiguration
|
|
| 250 | + SMethod_WindowShowMessage :: SMethod Method_WindowShowMessage
|
|
| 251 | + SMethod_WindowLogMessage :: SMethod Method_WindowLogMessage
|
|
| 252 | + SMethod_TelemetryEvent :: SMethod Method_TelemetryEvent
|
|
| 253 | + SMethod_TextDocumentDidOpen :: SMethod Method_TextDocumentDidOpen
|
|
| 254 | + SMethod_TextDocumentDidChange :: SMethod Method_TextDocumentDidChange
|
|
| 255 | + SMethod_TextDocumentDidClose :: SMethod Method_TextDocumentDidClose
|
|
| 256 | + SMethod_TextDocumentDidSave :: SMethod Method_TextDocumentDidSave
|
|
| 257 | + SMethod_TextDocumentWillSave :: SMethod Method_TextDocumentWillSave
|
|
| 258 | + SMethod_WorkspaceDidChangeWatchedFiles :: SMethod Method_WorkspaceDidChangeWatchedFiles
|
|
| 259 | + SMethod_TextDocumentPublishDiagnostics :: SMethod Method_TextDocumentPublishDiagnostics
|
|
| 260 | + SMethod_SetTrace :: SMethod Method_SetTrace
|
|
| 261 | + SMethod_LogTrace :: SMethod Method_LogTrace
|
|
| 262 | + SMethod_CancelRequest :: SMethod Method_CancelRequest
|
|
| 263 | + SMethod_Progress :: SMethod Method_Progress
|
|
| 264 | + |
|
| 265 | +type SServerMethod (m :: Method ServerToClient t) = SMethod m
|
|
| 266 | + |
|
| 267 | +data MessageDirection = ServerToClient | ClientToServer
|
|
| 268 | + |
|
| 269 | +data MessageKind = Notification | Request
|
|
| 270 | + |
|
| 271 | + |
|
| 272 | +type ServerNotOrReq :: forall t. Method ServerToClient t -> Type
|
|
| 273 | +data ServerNotOrReq m where
|
|
| 274 | + IsServerNot ::
|
|
| 275 | + ( TMessage m ~ TNotificationMessage m
|
|
| 276 | + ) =>
|
|
| 277 | + ServerNotOrReq (m :: Method ServerToClient Notification)
|
|
| 278 | + IsServerReq ::
|
|
| 279 | + forall (m :: Method ServerToClient Request).
|
|
| 280 | + ( TMessage m ~ TRequestMessage m
|
|
| 281 | + ) =>
|
|
| 282 | + ServerNotOrReq m
|
|
| 283 | + |
|
| 284 | +type TMessage :: forall f t. Method f t -> Type
|
|
| 285 | +type family TMessage m where
|
|
| 286 | + TMessage (Method_CustomMethod s :: Method f t) = ()
|
|
| 287 | + TMessage (m :: Method f Request) = TRequestMessage m
|
|
| 288 | + TMessage (m :: Method f Notification) = TNotificationMessage m
|
|
| 289 | + |
|
| 290 | + |
|
| 291 | +data TNotificationMessage (m :: Method f Notification) = TNotificationMessage
|
|
| 292 | + { _method :: SMethod m
|
|
| 293 | + , _params :: MessageParams m
|
|
| 294 | + }
|
|
| 295 | + |
|
| 296 | +data TRequestMessage (m :: Method f Request) = TRequestMessage
|
|
| 297 | + |
|
| 298 | +type MessageParams :: forall f t . Method f t -> Type
|
|
| 299 | +type family MessageParams (m :: Method f t) where
|
|
| 300 | + MessageParams Method_TextDocumentPublishDiagnostics = PublishDiagnosticsParams
|
|
| 301 | + |
|
| 302 | +class MonadIO m => MonadLsp config m | m -> config where
|
|
| 303 | + getLspEnv :: m (LanguageContextEnv config)
|
|
| 304 | + |
|
| 305 | +instance MonadLsp config (LspT config) where
|
|
| 306 | + {-# INLINE getLspEnv #-}
|
|
| 307 | + getLspEnv = LspT pure
|
|
| 308 | + |
|
| 309 | + |
|
| 310 | +{-# INLINE splitServerMethod #-}
|
|
| 311 | +splitServerMethod :: SServerMethod m -> ServerNotOrReq m
|
|
| 312 | +splitServerMethod = \case
|
|
| 313 | + SMethod_TextDocumentPublishDiagnostics -> IsServerNot
|
|
| 314 | + SMethod_WindowShowMessage -> IsServerNot
|
|
| 315 | + SMethod_WindowShowMessageRequest -> IsServerReq
|
|
| 316 | + SMethod_WindowShowDocument -> IsServerReq
|
|
| 317 | + SMethod_WindowLogMessage -> IsServerNot
|
|
| 318 | + SMethod_WindowWorkDoneProgressCreate -> IsServerReq
|
|
| 319 | + SMethod_Progress -> IsServerNot
|
|
| 320 | + SMethod_TelemetryEvent -> IsServerNot
|
|
| 321 | + SMethod_ClientRegisterCapability -> IsServerReq
|
|
| 322 | + SMethod_ClientUnregisterCapability -> IsServerReq
|
|
| 323 | + SMethod_WorkspaceWorkspaceFolders -> IsServerReq
|
|
| 324 | + SMethod_WorkspaceConfiguration -> IsServerReq
|
|
| 325 | + SMethod_WorkspaceApplyEdit -> IsServerReq
|
|
| 326 | + SMethod_LogTrace -> IsServerNot
|
|
| 327 | + SMethod_CancelRequest -> IsServerNot
|
|
| 328 | + SMethod_WorkspaceCodeLensRefresh -> IsServerReq
|
|
| 329 | + SMethod_WorkspaceSemanticTokensRefresh -> IsServerReq
|
|
| 330 | + SMethod_WorkspaceInlineValueRefresh -> IsServerReq
|
|
| 331 | + SMethod_WorkspaceInlayHintRefresh -> IsServerReq
|
|
| 332 | + SMethod_WorkspaceDiagnosticRefresh -> IsServerReq
|
|
| 333 | + |
|
| 334 | +fromServerNot ::
|
|
| 335 | + forall (m :: Method ServerToClient Notification).
|
|
| 336 | + TMessage m ~ TNotificationMessage m =>
|
|
| 337 | + TNotificationMessage m ->
|
|
| 338 | + FromServerMessage
|
|
| 339 | +fromServerNot m@TNotificationMessage{_method = meth} = FromServerMess meth m
|
|
| 340 | + |
|
| 341 | + |
|
| 342 | +data FromServerMessage' a where
|
|
| 343 | + FromServerMess :: forall t (m :: Method ServerToClient t) a. SMethod m -> TMessage m -> FromServerMessage' a
|
|
| 344 | + FromServerRsp :: forall (m :: Method ClientToServer Request) a. a m -> TResponseMessage m -> FromServerMessage' a
|
|
| 345 | + |
|
| 346 | +type FromServerMessage = FromServerMessage' SMethod
|
|
| 347 | + |
|
| 348 | +data TResponseMessage (m :: Method f Request) = TResponseMessage
|
|
| 349 | + |
|
| 350 | +sendToClient :: MonadLsp config m => FromServerMessage -> m ()
|
|
| 351 | +sendToClient msg = do
|
|
| 352 | + f <- resSendMessage <$> getLspEnv
|
|
| 353 | + liftIO $ f msg
|
|
| 354 | +{-# INLINE sendToClient #-} |
| ... | ... | @@ -21,3 +21,4 @@ test('T15108', [test_opts], compile, ['-O -prof -fprof-auto']) |
| 21 | 21 | test('T19894', [test_opts, extra_files(['T19894'])], multimod_compile, ['Main', '-v0 -O2 -prof -fprof-auto -iT19894'])
|
| 22 | 22 | test('T20938', [test_opts], compile, ['-O -prof'])
|
| 23 | 23 | test('T26056', [test_opts], compile, ['-O -prof'])
|
| 24 | +test('T27121', [test_opts], compile, ['-O -prof -fprof-auto']) |
| 1 | +{-# LANGUAGE DataKinds #-}
|
|
| 2 | +{-# LANGUAGE GADTs #-}
|
|
| 3 | +{-# LANGUAGE TypeOperators #-}
|
|
| 4 | + |
|
| 5 | +module T26941 where
|
|
| 6 | + |
|
| 7 | +import GHC.TypeLits
|
|
| 8 | + |
|
| 9 | +import T26941_aux ( SMayNat(SKnown), ListH, shxHead )
|
|
| 10 | + |
|
| 11 | +shsHead :: ListH (Just n : sh) Int -> SNat n
|
|
| 12 | +shsHead shx =
|
|
| 13 | + case shxHead shx of
|
|
| 14 | + SKnown SNat -> SNat |
| 1 | +{-# LANGUAGE DataKinds #-}
|
|
| 2 | +{-# LANGUAGE GADTs #-}
|
|
| 3 | +{-# LANGUAGE StandaloneKindSignatures #-}
|
|
| 4 | +{-# LANGUAGE TypeOperators #-}
|
|
| 5 | + |
|
| 6 | +module T26941_aux where
|
|
| 7 | + |
|
| 8 | +import Data.Kind
|
|
| 9 | +import GHC.TypeLits
|
|
| 10 | + |
|
| 11 | +shxHead :: ListH (n : sh) i -> SMayNat i n
|
|
| 12 | +shxHead list = {-# SCC "bad_scc" #-}
|
|
| 13 | + ( case list of (i `ConsKnown` _) -> SKnown i )
|
|
| 14 | + |
|
| 15 | +type ListH :: [Maybe Nat] -> Type -> Type
|
|
| 16 | +data ListH sh i where
|
|
| 17 | + ConsKnown :: SNat n -> ListH sh i -> ListH (Just n : sh) i
|
|
| 18 | + |
|
| 19 | +data SMayNat i n where
|
|
| 20 | + SKnown :: SNat n -> SMayNat i (Just n) |
| ... | ... | @@ -576,6 +576,8 @@ test('T26117', [grep_errmsg(r'==')], compile, ['-O -ddump-simpl -dsuppress-uniqu |
| 576 | 576 | test('T26349', normal, compile, ['-O -ddump-rules'])
|
| 577 | 577 | test('T26681', normal, compile, ['-O'])
|
| 578 | 578 | |
| 579 | +test('T26941', [extra_files(['T26941_aux.hs']), req_profiling], multimod_compile, ['T26941', '-v0 -O -prof'])
|
|
| 580 | + |
|
| 579 | 581 | # T26709: we expect three `case` expressions not four
|
| 580 | 582 | test('T26709', [grep_errmsg(r'case')],
|
| 581 | 583 | multimod_compile,
|