-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathInlines.scala
583 lines (522 loc) · 26.7 KB
/
Inlines.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
package dotty.tools
package dotc
package inlines
import ast.*, core.*
import Flags.*, Symbols.*, Types.*, Decorators.*, Constants.*, Contexts.*
import StdNames.{tpnme, nme}
import NameOps.*
import typer.*
import NameKinds.BodyRetainerName
import SymDenotations.SymDenotation
import config.Printers.inlining
import ErrorReporting.errorTree
import dotty.tools.dotc.util.{SourceFile, SourcePosition, SrcPos}
import dotty.tools.dotc.transform.*
import dotty.tools.dotc.transform.MegaPhase
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
import parsing.Parsers.Parser
import transform.{PostTyper, Inlining, CrossVersionChecks}
import staging.StagingLevel
import collection.mutable
import reporting.{NotConstant, trace}
import util.Spans.Span
import dotty.tools.dotc.core.Periods.PhaseId
/** Support for querying inlineable methods and for inlining calls to such methods */
object Inlines:
import tpd.*
/** An exception signalling that an inline info cannot be computed due to a
* cyclic reference. i14772.scala shows a case where this happens.
*/
private[dotc] class MissingInlineInfo extends Exception
/** `sym` is an inline method with a known body to inline.
*/
def hasBodyToInline(sym: SymDenotation)(using Context): Boolean =
sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot)
/** The body to inline for method `sym`, or `EmptyTree` if none exists.
* @pre hasBodyToInline(sym)
*/
def bodyToInline(sym: SymDenotation)(using Context): Tree =
if hasBodyToInline(sym) then
sym.getAnnotation(defn.BodyAnnot).get.tree
else
EmptyTree
/** Are we in an inline method body? */
def inInlineMethod(using Context): Boolean =
ctx.owner.ownersIterator.exists(_.isInlineMethod)
/** Can a call to method `meth` be inlined? */
def isInlineable(meth: Symbol)(using Context): Boolean =
meth.is(Inline) && meth.hasAnnotation(defn.BodyAnnot) && !inInlineMethod
/** Should call be inlined in this context? */
def needsInlining(tree: Tree)(using Context): Boolean = tree match {
case Block(_, expr) => needsInlining(expr)
case _ =>
def isUnapplyExpressionWithDummy: Boolean =
// The first step of typing an `unapply` consists in typing the call
// with a dummy argument (see Applications.typedUnApply). We delay the
// inlining of this call.
def rec(tree: Tree): Boolean = tree match
case Apply(_, ProtoTypes.dummyTreeOfType(_) :: Nil) => true
case Apply(fn, _) => rec(fn)
case _ => false
tree.symbol.name.isUnapplyName && rec(tree)
isInlineable(tree.symbol)
&& !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly]
&& StagingLevel.level == 0
&& (
ctx.phase == Phases.inliningPhase
|| (ctx.phase == Phases.typerPhase && needsTransparentInlining(tree))
)
&& !ctx.typer.hasInliningErrors
&& !ctx.base.stopInlining
&& !ctx.mode.is(Mode.NoInline)
&& !isUnapplyExpressionWithDummy
}
private def needsTransparentInlining(tree: Tree)(using Context): Boolean =
tree.symbol.is(Transparent)
|| ctx.mode.is(Mode.ForceInline)
/** Try to inline a call to an inline method. Fail with error if the maximal
* inline depth is exceeded.
*
* @param tree The call to inline
* @param pt The expected type of the call.
* @return An `Inlined` node that refers to the original call and the inlined bindings
* and body that replace it.
*/
def inlineCall(tree: Tree)(using Context): Tree =
if tree.symbol.denot != SymDenotations.NoDenotation
&& tree.symbol.effectiveOwner == defn.CompiletimeTestingPackage.moduleClass
then
if (tree.symbol == defn.CompiletimeTesting_typeChecks) return Intrinsics.typeChecks(tree)
if (tree.symbol == defn.CompiletimeTesting_typeCheckErrors) return Intrinsics.typeCheckErrors(tree)
if ctx.isAfterTyper then
// During typer we wait with cross version checks until PostTyper, in order
// not to provoke cyclic references. See i16116 for a test case.
CrossVersionChecks.checkRef(tree.symbol, tree.srcPos)
if tree.symbol.isConstructor then return tree // error already reported for the inline constructor definition
/** Set the position of all trees logically contained in the expansion of
* inlined call `call` to the position of `call`. This transform is necessary
* when lifting bindings from the expansion to the outside of the call.
*/
def liftFromInlined(call: Tree) = new TreeMap:
override def transform(t: Tree)(using Context) =
if call.span.exists then
t match
case t @ Inlined(_, Nil, expr) if t.inlinedFromOuterScope => expr
case _ if t.isEmpty => t
case _ => super.transform(t.withSpan(call.span))
else t
end liftFromInlined
val bindings = new mutable.ListBuffer[Tree]
/** Lift bindings around inline call or in its function part to
* the `bindings` buffer. This is done as an optimization to keep
* inline call expansions smaller.
*/
def liftBindings(tree: Tree, liftPos: Tree => Tree): Tree = tree match {
case Block(stats, expr) =>
bindings ++= stats.map(liftPos)
liftBindings(expr, liftPos)
case tree @ Inlined(call, stats, expr) =>
bindings ++= stats.map(liftPos)
val lifter = liftFromInlined(call)
cpy.Inlined(tree)(call, Nil, liftBindings(expr, liftFromInlined(call).transform(_)))
case Apply(fn, args) =>
cpy.Apply(tree)(liftBindings(fn, liftPos), args)
case TypeApply(fn, args) =>
fn.tpe.widenTermRefExpr match
case tp: PolyType =>
val targBounds = tp.instantiateParamInfos(args.map(_.tpe))
for case (arg, bounds: TypeBounds) <- args.zip(targBounds) if !bounds.contains(arg.tpe) do
val boundsStr =
if bounds == TypeBounds.empty then " <: Any. Note that this type is higher-kinded."
else bounds.show
report.error(em"${arg.tpe} does not conform to bound$boundsStr", arg)
cpy.TypeApply(tree)(liftBindings(fn, liftPos), args)
case Select(qual, name) =>
cpy.Select(tree)(liftBindings(qual, liftPos), name)
case _ =>
tree
}
// assertAllPositioned(tree) // debug
val tree1 = liftBindings(tree, identity)
val tree2 =
if bindings.nonEmpty then
cpy.Block(tree)(bindings.toList, inlineCall(tree1))
else if enclosingInlineds.length < ctx.settings.XmaxInlines.value && !reachedInlinedTreesLimit then
val body =
try bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors
catch case _: MissingInlineInfo =>
throw CyclicReference(ctx.owner)
new InlineCall(tree).expand(body)
else
ctx.base.stopInlining = true
val (reason, setting) =
if reachedInlinedTreesLimit then ("inlined trees", ctx.settings.XmaxInlinedTrees)
else ("successive inlines", ctx.settings.XmaxInlines)
errorTree(
tree,
em"""|Maximal number of $reason (${setting.value}) exceeded,
|Maybe this is caused by a recursive inline method?
|You can use ${setting.name} to change the limit.""",
(tree :: enclosingInlineds).last.srcPos
)
if ctx.base.stopInlining && enclosingInlineds.isEmpty then
ctx.base.stopInlining = false
// we have completely backed out of the call that overflowed;
// reset so that further inline calls can be expanded
tree2
end inlineCall
/** Try to inline a pattern with an inline unapply method. Fail with error if the maximal
* inline depth is exceeded.
*
* @param fun The function of an Unapply node
* @return An `Unapply` with a `fun` containing the inlined call to the unapply
*/
def inlinedUnapplyFun(fun: tpd.Tree)(using Context): Tree =
// We cannot inline the unapply directly, since the pattern matcher relies on unapply applications
// as signposts what to do. On the other hand, we can do the inlining only in typer, not afterwards.
// So the trick is to create a "wrapper" unapply in an anonymous class that has the inlined unapply
// as its right hand side. The call to the wrapper unapply serves as the signpost for pattern matching.
// After pattern matching, the anonymous class is removed in phase InlinePatterns with a beta reduction step.
//
// An inline unapply `P.unapply` in a pattern `P[...](using ...)(x1,x2,...)(using t1: T1, t2: T2, ...)` is transformed into
// `{ class $anon { def unapply(s: S)(using t1: T1, t2: T2, ...): R = P.unapply[...](using ...)(s)(using t1, t2, ...) }; new $anon }.unapply(using y1,y2,...)`
// and the call `P.unapply[...](using ...)(x1, x2, ...)(using t1, t2, ...)` is inlined.
// This serves as a placeholder for the inlined body until the `patternMatcher` phase. After pattern matcher
// transforms the patterns into terms, the `inlinePatterns` phase removes this anonymous class by β-reducing
// the call to the `unapply`.
val sym = fun.symbol
val newUnapply = AnonClass(ctx.owner, List(defn.ObjectType), sym.coord) { cls =>
// `fun` is a partially applied method that contains all type applications of the method.
// The methodic type `fun.tpe.widen` is the type of the function starting from the scrutinee argument
// and its type parameters are instantiated.
val unapplyInfo = fun.tpe.widen
val unapplySym = newSymbol(cls, sym.name.toTermName, Synthetic | Method, unapplyInfo, coord = sym.coord).entered
val unapply = DefDef(unapplySym.asTerm, argss => fun.appliedToArgss(argss).withSpan(fun.span))
if sym.is(Transparent) then
// Inline the body and refine the type of the unapply method
val inlinedBody = inlineCall(unapply.rhs)(using ctx.withOwner(unapplySym))
val refinedResultType = inlinedBody.tpe.widen
def refinedResult(info: Type): Type = info match
case info: LambdaType => info.newLikeThis(info.paramNames, info.paramInfos, refinedResult(info.resultType))
case _ => refinedResultType
unapplySym.info = refinedResult(unapplyInfo)
List(cpy.DefDef(unapply)(tpt = TypeTree(refinedResultType), rhs = inlinedBody))
else
List(unapply)
}
newUnapply.select(sym.name).withSpan(fun.span)
end inlinedUnapplyFun
/** For a retained inline method, another method that keeps track of
* the body that is kept at runtime. For instance, an inline method
*
* inline override def f(x: T) = b
*
* is complemented by the body retainer method
*
* private def f$retainedBody(x: T) = f(x)
*
* where the call `f(x)` is inline-expanded. This body is then transferred
* back to `f` at erasure, using method addRetainedInlineBodies.
*/
def bodyRetainer(mdef: DefDef)(using Context): DefDef =
val meth = mdef.symbol.asTerm
val retainer = meth.copy(
name = BodyRetainerName(meth.name),
flags = (meth.flags &~ (Inline | Macro | Override | AbsOverride)) | Private,
coord = mdef.rhs.span.startPos).asTerm.entered
retainer.deriveTargetNameAnnotation(meth, name => BodyRetainerName(name.asTermName))
DefDef(retainer, prefss =>
inlineCall(
ref(meth).appliedToArgss(prefss).withSpan(mdef.rhs.span.startPos))(
using ctx.withOwner(retainer)))
.showing(i"retainer for $meth: $result", inlining)
/** Replace `Inlined` node by a block that contains its bindings and expansion */
def dropInlined(inlined: Inlined)(using Context): Tree =
val tree1 =
if inlined.bindings.isEmpty then inlined.expansion
else cpy.Block(inlined)(inlined.bindings, inlined.expansion)
// Reposition in the outer most inlined call
if (enclosingInlineds.nonEmpty) tree1 else reposition(tree1, inlined.span)
def reposition(tree: Tree, callSpan: Span)(using Context): Tree =
// Reference test tests/run/i4947b
val curSource = ctx.compilationUnit.source
// Tree copier that changes the source of all trees to `curSource`
val cpyWithNewSource = new TypedTreeCopier {
override protected def sourceFile(tree: tpd.Tree): SourceFile = curSource
override protected val untpdCpy: untpd.UntypedTreeCopier = new untpd.UntypedTreeCopier {
override protected def sourceFile(tree: untpd.Tree): SourceFile = curSource
}
}
/** Removes all Inlined trees, replacing them with blocks.
* Repositions all trees directly inside an inlined expansion of a non empty call to the position of the call.
* Any tree directly inside an empty call (inlined in the inlined code) retains their position.
*
* Until we implement JSR-45, we cannot represent in output positions in other source files.
* So, reposition inlined code from other files with the call position.
*/
class Reposition extends TreeMap(cpyWithNewSource) {
override def transform(tree: Tree)(using Context): Tree = {
def fixSpan[T <: untpd.Tree](copied: T): T =
copied.withSpan(if tree.source == curSource then tree.span else callSpan)
def finalize(copied: untpd.Tree) =
fixSpan(copied).withAttachmentsFrom(tree).withTypeUnchecked(tree.tpe)
inContext(ctx.withSource(curSource)) {
tree match
case tree: Ident => finalize(untpd.Ident(tree.name)(curSource))
case tree: Literal => finalize(untpd.Literal(tree.const)(curSource))
case tree: This => finalize(untpd.This(tree.qual)(curSource))
case tree: JavaSeqLiteral => finalize(untpd.JavaSeqLiteral(transform(tree.elems), transform(tree.elemtpt))(curSource))
case tree: SeqLiteral => finalize(untpd.SeqLiteral(transform(tree.elems), transform(tree.elemtpt))(curSource))
case tree: Bind => finalize(untpd.Bind(tree.name, transform(tree.body))(curSource))
case tree: TypeTree => finalize(tpd.TypeTree(tree.tpe))
case tree: DefTree => super.transform(tree).setDefTree
case EmptyTree => tree
case _ => fixSpan(super.transform(tree))
}
}
}
(new Reposition).transform(tree)
end reposition
/** Leave only a call trace consisting of
* - a reference to the top-level class from which the call was inlined,
* - the call's position
* in the call field of an Inlined node.
* The trace has enough info to completely reconstruct positions.
* Note: For macros it returns a Select and for other inline methods it returns an Ident (this distinction is only temporary to be able to run YCheckPositions)
*/
def inlineCallTrace(callSym: Symbol, pos: SourcePosition)(using Context): Tree = {
assert(ctx.source == pos.source)
val topLevelCls = callSym.topLevelClass
if (callSym.is(Macro)) ref(topLevelCls.owner).select(topLevelCls.name)(using ctx.withOwner(topLevelCls.owner)).withSpan(pos.span)
else Ident(topLevelCls.typeRef).withSpan(pos.span)
}
private object Intrinsics:
import dotty.tools.dotc.reporting.Diagnostic.Error
private enum ErrorKind:
case Parser, Typer
private def compileForErrors(tree: Tree)(using Context): List[(ErrorKind, Error)] =
assert(tree.symbol == defn.CompiletimeTesting_typeChecks || tree.symbol == defn.CompiletimeTesting_typeCheckErrors)
def stripTyped(t: Tree): Tree = t match {
case Typed(t2, _) => stripTyped(t2)
case Block(Nil, t2) => stripTyped(t2)
case Inlined(_, Nil, t2) => stripTyped(t2)
case _ => t
}
val Apply(_, codeArg :: Nil) = tree: @unchecked
val codeArg1 = stripTyped(codeArg.underlying)
val underlyingCodeArg =
if Inlines.isInlineable(codeArg1.symbol) then stripTyped(Inlines.inlineCall(codeArg1))
else codeArg1
// We should not be rewriting tested strings
val noRewriteSettings = ctx.settings.rewrite.updateIn(ctx.settingsState.reinitializedCopy(), None)
class MegaPhaseWithCustomPhaseId(miniPhases: Array[MiniPhase], startId: PhaseId, endId: PhaseId)
extends MegaPhase(miniPhases) {
override def start: Int = startId
override def end: Int = endId
}
// Let's reconstruct necessary transform MegaPhases, without anything
// that could cause problems here (like `CrossVersionChecks`).
// The individiual lists here should line up with Compiler.scala, i.e
// separate chunks there should also be kept separate here.
// For now we create a single MegaPhase, since there does not seem to
// be any important checks later (e.g. ForwardDepChecks could be applicable here,
// but the equivalent is also not run in the scala 2's `ctx.typechecks`,
// so let's leave it out for now).
lazy val reconstructedTransformPhases =
val transformPhases: List[List[(Class[?], () => MiniPhase)]] = List(
List(
(classOf[InlineVals], () => new InlineVals),
(classOf[ElimRepeated], () => new ElimRepeated),
(classOf[RefChecks], () => new RefChecks),
),
)
transformPhases.flatMap( (megaPhaseList: List[(Class[?], () => MiniPhase)]) =>
val (newMegaPhasePhases, phaseIds) =
megaPhaseList.flatMap {
case (filteredPhaseClass, miniphaseConstructor) =>
ctx.base.phases
.find(phase => filteredPhaseClass.isInstance(phase))
.map(phase => (miniphaseConstructor(), phase.id))
}
.unzip
if newMegaPhasePhases.isEmpty then None
else Some(MegaPhaseWithCustomPhaseId(newMegaPhasePhases.toArray, phaseIds.head, phaseIds.last))
)
ConstFold(underlyingCodeArg).tpe.widenTermRefExpr match {
case ConstantType(Constant(code: String)) =>
val unitName = "tasty-reflect"
val source2 = SourceFile.virtual(unitName, code)
// We need a dummy owner, as the actual one does not have a computed denotation yet,
// but might be inspected in a transform phase, leading to cyclic errors
val dummyOwner = newSymbol(ctx.owner, "$dummySymbol$".toTermName, Private, defn.AnyType, NoSymbol)
val newContext =
ctx.fresh
.setSettings(noRewriteSettings)
.setNewTyperState()
.setTyper(new Typer(ctx.nestingLevel + 1))
.setSource(source2)
.withOwner(dummyOwner)
inContext(newContext) {
val tree2 = new Parser(source2).block()
if ctx.reporter.allErrors.nonEmpty then
ctx.reporter.allErrors.map((ErrorKind.Parser, _))
else
val tree3 = ctx.typer.typed(tree2)
ctx.base.postTyperPhase match
case postTyper: PostTyper if ctx.reporter.allErrors.isEmpty =>
val tree4 = atPhase(postTyper) { postTyper.newTransformer.transform(tree3) }
ctx.base.setRootTreePhase match
case setRootTree =>
val tree5 =
val compilationUnit = CompilationUnit(unitName, code)
compilationUnit.tpdTree = tree4
compilationUnit.untpdTree = tree2
var units = List(compilationUnit)
atPhase(setRootTree)(setRootTree.runOn(units).head.tpdTree)
ctx.base.inliningPhase match
case inlining: Inlining if ctx.reporter.allErrors.isEmpty =>
val tree6 = atPhase(inlining) { inlining.newTransformer.transform(tree5) }
if ctx.reporter.allErrors.isEmpty && reconstructedTransformPhases.nonEmpty then
var transformTree = tree6
for phase <- reconstructedTransformPhases do
if ctx.reporter.allErrors.isEmpty then
transformTree = atPhase(phase.end + 1)(phase.transformUnit(transformTree))
case _ =>
case _ =>
ctx.reporter.allErrors.map((ErrorKind.Typer, _))
}
case t =>
report.error(em"argument to compileError must be a statically known String but was: $codeArg", codeArg1.srcPos)
Nil
}
private def packError(kind: ErrorKind, error: Error)(using Context): Tree =
def lit(x: Any) = Literal(Constant(x))
val constructor: Tree = ref(defn.CompiletimeTesting_Error_apply)
val parserErrorKind: Tree = ref(defn.CompiletimeTesting_ErrorKind_Parser)
val typerErrorKind: Tree = ref(defn.CompiletimeTesting_ErrorKind_Typer)
constructor.appliedTo(
lit(error.message),
lit(error.pos.lineContent.reverse.dropWhile("\n ".contains).reverse),
lit(error.pos.column),
if kind == ErrorKind.Parser then parserErrorKind else typerErrorKind)
private def packErrors(errors: List[(ErrorKind, Error)], pos: SrcPos)(using Context): Tree =
val individualErrors: List[Tree] = errors.map(packError)
val errorTpt = ref(defn.CompiletimeTesting_ErrorClass).withSpan(pos.span)
mkList(individualErrors, errorTpt)
/** Expand call to scala.compiletime.testing.typeChecks */
def typeChecks(tree: Tree)(using Context): Tree =
val errors = compileForErrors(tree)
Literal(Constant(errors.isEmpty)).withSpan(tree.span)
/** Expand call to scala.compiletime.testing.typeCheckErrors */
def typeCheckErrors(tree: Tree)(using Context): Tree =
val errors = compileForErrors(tree)
packErrors(errors, tree)
/** Expand call to scala.compiletime.codeOf */
def codeOf(arg: Tree, pos: SrcPos)(using Context): Tree =
Literal(Constant(arg.show(using ctx.withoutColors))).withSpan(pos.span)
end Intrinsics
/** Produces an inlined version of `call` via its `inlined` method.
*
* @param call the original call to an inlineable method
* @param rhsToInline the body of the inlineable method that replaces the call.
*/
private class InlineCall(call: tpd.Tree)(using Context) extends Inliner(call):
import tpd.*
import Inlines.*
/** The Inlined node representing the inlined call */
def expand(rhsToInline: Tree): Tree =
// Special handling of `requireConst` and `codeOf`
callValueArgss match
case (arg :: Nil) :: Nil =>
if inlinedMethod == defn.Compiletime_requireConst then
arg match
case ConstantValue(_) | Inlined(_, Nil, Typed(ConstantValue(_), _)) => // ok
case _ => report.error(em"expected a constant value but found: $arg", arg.srcPos)
return unitLiteral.withSpan(call.span)
else if inlinedMethod == defn.Compiletime_codeOf then
return Intrinsics.codeOf(arg, call.srcPos)
case _ =>
// Special handling of `constValue[T]`, `constValueOpt[T]`, `constValueTuple[T]`, `summonInline[T]` and `summonAll[T]`
if callTypeArgs.length == 1 then
def constValueOrError(tpe: Type): Tree =
val constVal = tryConstValue(tpe)
if constVal.isEmpty then
val msg = NotConstant("cannot take constValue", tpe)
report.error(msg, callTypeArgs.head.srcPos)
ref(defn.Predef_undefined).withSpan(callTypeArgs.head.span).withType(ErrorType(msg))
else
constVal
def searchImplicitOrError(tpe: Type): Tree =
val evTyper = new Typer(ctx.nestingLevel + 1)
val evCtx = ctx.fresh.setTyper(evTyper)
inContext(evCtx) {
val evidence = evTyper.inferImplicitArg(tpe, callTypeArgs.head.span)
evidence.tpe match
case fail: Implicits.LateMismatchedImplicit =>
val addendum = fail.errors match
case Nil => ""
case errs => errs.map(_.msg).mkString("a search with errors:\n ", "\n ", "")
errorTree(call, evTyper.missingArgMsg(evidence, tpe, addendum))
case fail: Implicits.SearchFailureType =>
errorTree(call, evTyper.missingArgMsg(evidence, tpe, where = ""))
case _ =>
evidence
}
def unrollTupleTypes(tpe: Type): Option[List[Type]] = tpe.dealias match
case AppliedType(tycon, args) if defn.isTupleClass(tycon.typeSymbol) =>
Some(args)
case AppliedType(tycon, head :: tail :: Nil) if tycon.isRef(defn.PairClass) =>
unrollTupleTypes(tail).map(head :: _)
case tpe: TermRef if tpe.symbol == defn.EmptyTupleModule =>
Some(Nil)
case tpe: AppliedType if tpe.isMatchAlias =>
unrollTupleTypes(tpe.tryNormalize)
case _ =>
None
if (inlinedMethod == defn.Compiletime_constValue) {
return constValueOrError(callTypeArgs.head.tpe)
}
else if (inlinedMethod == defn.Compiletime_constValueOpt) {
val constVal = tryConstValue(callTypeArgs.head.tpe)
return (
if (constVal.isEmpty) ref(defn.NoneModule.termRef)
else New(defn.SomeClass.typeRef.appliedTo(constVal.tpe), constVal :: Nil)
)
}
else if (inlinedMethod == defn.Compiletime_constValueTuple) {
unrollTupleTypes(callTypeArgs.head.tpe) match
case Some(types) =>
val constants = types.map(constValueOrError)
return Typed(tpd.tupleTree(constants), TypeTree(callTypeArgs.head.tpe)).withSpan(call.span)
case _ =>
return errorTree(call, em"Tuple element types must be known at compile time")
}
else if (inlinedMethod == defn.Compiletime_summonInline) {
return searchImplicitOrError(callTypeArgs.head.tpe)
}
else if (inlinedMethod == defn.Compiletime_summonAll) {
unrollTupleTypes(callTypeArgs.head.tpe) match
case Some(types) =>
val implicits = types.map(searchImplicitOrError)
return Typed(tpd.tupleTree(implicits), TypeTree(callTypeArgs.head.tpe)).withSpan(call.span)
case _ =>
return errorTree(call, em"Tuple element types must be known at compile time")
}
end if
val (bindings, expansion) = super.inlined(rhsToInline)
// Take care that only argument bindings go into `bindings`, since positions are
// different for bindings from arguments and bindings from body.
val inlined = tpd.Inlined(call, bindings, expansion)
if !hasOpaqueProxies then inlined
else
val target =
if inlinedMethod.is(Transparent) then call.tpe & inlined.tpe
else call.tpe
inlined.ensureConforms(target)
// Make sure that the sealing with the declared type
// is type correct. Without it we might get problems since the
// expression's type is the opaque alias but the call's type is
// the opaque type itself. An example is in pos/opaque-inline1.scala.
end expand
end InlineCall
end Inlines