Skip to content

Commit c489b70

Browse files
committed
Check inline expansion for exclusion
Preserve attachments of literal constant in `42: Unit`.
1 parent 4d7d383 commit c489b70

File tree

2 files changed

+56
-30
lines changed

2 files changed

+56
-30
lines changed

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import util.common.*
3535
import util.{Property, SimpleIdentityMap, SrcPos}
3636
import Applications.{tupleComponentTypes, wrapDefs, defaultArgument}
3737

38-
import collection.mutable
38+
import collection.mutable, mutable.ListBuffer
3939
import Implicits.*
4040
import util.Stats.record
4141
import config.Printers.{gadts, typr}
@@ -207,7 +207,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
207207
* a reference for `m` is searched. `null` in all other situations.
208208
*/
209209
def findRef(name: Name, pt: Type, required: FlagSet, excluded: FlagSet, pos: SrcPos,
210-
altImports: mutable.ListBuffer[TermRef] | Null = null)(using Context): Type = {
210+
altImports: ListBuffer[TermRef] | Null = null)(using Context): Type = {
211211
val refctx = ctx
212212
val noImports = ctx.mode.is(Mode.InPackageClauseName)
213213
def suppressErrors = excluded.is(ConstructorProxy)
@@ -1124,7 +1124,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
11241124
record("typedNumber")
11251125
val digits = tree.digits
11261126
val target = pt.dealias
1127-
def lit(value: Any) = Literal(Constant(value)).withSpan(tree.span)
1127+
def lit(value: Any) = Literal(Constant(value)).withSpan(tree.span).withAttachmentsFrom(tree)
11281128
try {
11291129
// Special case primitive numeric types
11301130
if (target.isRef(defn.IntClass) ||
@@ -1174,7 +1174,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
11741174
}
11751175
var app: untpd.Tree = untpd.Apply(fromDigits, firstArg :: otherArgs)
11761176
if (ctx.mode.is(Mode.Pattern)) app = untpd.Block(Nil, app)
1177-
return typed(app, pt)
1177+
return typed(app, pt).withAttachmentsFrom(tree)
11781178
case _ =>
11791179
}
11801180
// Otherwise convert to Int or Double according to digits format
@@ -3501,7 +3501,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
35013501
else {
35023502
val app = typedApply(desugar.binop(l, op, r).withAttachmentsFrom(tree), pt)
35033503
if op.name.isRightAssocOperatorName && !ctx.mode.is(Mode.QuotedExprPattern) then
3504-
val defs = new mutable.ListBuffer[Tree]
3504+
val defs = ListBuffer.empty[Tree]
35053505
def lift(app: Tree): Tree = (app: @unchecked) match
35063506
case Apply(fn, args) =>
35073507
if (app.tpe.isError) app
@@ -3801,7 +3801,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38013801
trees mapconserve (typed(_))
38023802

38033803
def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(using Context): (List[Tree], Context) = {
3804-
val buf = new mutable.ListBuffer[Tree]
3804+
val buf = ListBuffer.empty[Tree]
38053805
var enumContexts: SimpleIdentityMap[Symbol, Context] = SimpleIdentityMap.empty
38063806
val initialNotNullInfos = ctx.notNullInfos
38073807
// A map from `enum` symbols to the contexts enclosing their definitions
@@ -3845,7 +3845,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
38453845
traverse(xtree :: rest)
38463846
case stat :: rest =>
38473847
val stat1 = typed(stat)(using ctx.exprContext(stat, exprOwner))
3848-
if !Linter.warnOnInterestingResultInStatement(stat1) then checkStatementPurity(stat1)(stat, exprOwner)
3848+
if !Linter.warnOnInterestingResultInStatement(stat1) then
3849+
checkStatementPurity(stat1)(stat, exprOwner, isUnitExpr = false)
38493850
buf += stat1
38503851
traverse(rest)(using stat1.nullableContext)
38513852
case nil =>
@@ -4052,7 +4053,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
40524053
def selectionProto = SelectionProto(tree.name, mbrProto, compat, privateOK = inSelect, tree.nameSpan)
40534054

40544055
def tryExtension(using Context): Tree =
4055-
val altImports = new mutable.ListBuffer[TermRef]()
4056+
val altImports = ListBuffer.empty[TermRef]
40564057
findRef(tree.name, WildcardType, ExtensionMethod, EmptyFlags, qual.srcPos, altImports) match
40574058
case ref: TermRef =>
40584059
def tryExtMethod(ref: TermRef)(using Context) =
@@ -4061,7 +4062,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
40614062
tryExtMethod(ref)
40624063
else
40634064
// Try all possible imports and collect successes and failures
4064-
val successes, failures = new mutable.ListBuffer[(Tree, TyperState)]
4065+
val successes, failures = ListBuffer.empty[(Tree, TyperState)]
40654066
for alt <- ref :: altImports.toList do
40664067
val nestedCtx = ctx.fresh.setNewTyperState()
40674068
val app = tryExtMethod(alt)(using nestedCtx)
@@ -4719,22 +4720,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
47194720
return readapt(tree.cast(captured))
47204721

47214722
// drop type if prototype is Unit
4722-
if (pt isRef defn.UnitClass) {
4723+
if pt.isRef(defn.UnitClass) then
47234724
// local adaptation makes sure every adapted tree conforms to its pt
47244725
// so will take the code path that decides on inlining
47254726
val tree1 = adapt(tree, WildcardType, locked)
47264727
checkStatementPurity(tree1)(tree, ctx.owner, isUnitExpr = true)
4727-
4728-
if ctx.settings.Whas.valueDiscard
4729-
&& !ctx.isAfterTyper
4730-
&& !tree.isInstanceOf[Inlined]
4731-
&& !isThisTypeResult(tree)
4732-
&& !isAscribedToUnit(tree)
4733-
then
4734-
report.warning(ValueDiscarding(tree.tpe), tree.srcPos)
4735-
4728+
checkValueDiscard(tree)
47364729
return tpd.Block(tree1 :: Nil, unitLiteral)
4737-
}
47384730

47394731
// convert function literal to SAM closure
47404732
tree match {
@@ -4992,11 +4984,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
49924984
typedExpr(cmp, defn.BooleanType)
49934985
case _ =>
49944986

4995-
private def checkStatementPurity(tree: tpd.Tree)(original: untpd.Tree, exprOwner: Symbol, isUnitExpr: Boolean = false)(using Context): Unit =
4987+
private def checkStatementPurity(tree: tpd.Tree)(original: untpd.Tree, exprOwner: Symbol, isUnitExpr: Boolean)(using Context): Unit =
4988+
inline def isPureNotInlinedUnit = tree match
4989+
case Inlined(_, Nil, Literal(k)) if k.tag == UnitTag => false // assert(2 + 2 == 4)
4990+
case tree => isPureExpr(tree)
49964991
if !tree.tpe.isErroneous
49974992
&& !ctx.isAfterTyper
4998-
&& !tree.isInstanceOf[Inlined]
4999-
&& isPureExpr(tree)
4993+
&& isPureNotInlinedUnit
50004994
&& !isSelfOrSuperConstrCall(tree)
50014995
then tree match
50024996
case closureDef(meth)
@@ -5010,13 +5004,26 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
50105004
// sometimes we do not have the original anymore and use the transformed tree instead.
50115005
// But taken together, the two criteria are quite accurate.
50125006
missingArgs(tree, tree.tpe.widen)
5013-
case _ if tree.hasAttachment(AscribedToUnit) =>
5014-
// The tree was ascribed to `Unit` explicitly to silence the warning.
5015-
()
5016-
case _ if isUnitExpr =>
5017-
report.warning(PureUnitExpression(original, tree.tpe), original.srcPos)
5018-
case _ =>
5019-
report.warning(PureExpressionInStatementPosition(original, exprOwner), original.srcPos)
5007+
case tree =>
5008+
val warnable = tree match
5009+
case inlined: Inlined => inlined.expansion
5010+
case tree => tree
5011+
// Check if the tree was ascribed to `Unit` explicitly to silence the warning.
5012+
if !isThisTypeResult(warnable) && !isAscribedToUnit(warnable) then
5013+
val msg =
5014+
if isUnitExpr then
5015+
PureUnitExpression(original, warnable.tpe)
5016+
else
5017+
PureExpressionInStatementPosition(original, exprOwner)
5018+
report.warning(msg, original.srcPos)
5019+
5020+
private def checkValueDiscard(tree: tpd.Tree)(using Context): Unit =
5021+
if ctx.settings.Whas.valueDiscard && !ctx.isAfterTyper then
5022+
val warnable = tree match
5023+
case inlined: Inlined => inlined.expansion
5024+
case tree => tree
5025+
if !isThisTypeResult(warnable) && !isAscribedToUnit(warnable) then
5026+
report.warning(ValueDiscarding(warnable.tpe), tree.srcPos)
50205027

50215028
/** Types the body Scala 2 macro declaration `def f = macro <body>` */
50225029
protected def typedScala2MacroBody(call: untpd.Tree)(using Context): Tree =

tests/warn/i23018.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//> using options -Wvalue-discard
2+
3+
transparent inline def toto: Any = 1
4+
transparent inline def uhoh = 42: Unit // nowarn
5+
def tata: Unit = toto // warn pure Discard
6+
def hmm: Unit = uhoh
7+
def literally: Unit = 42 // warn pure Discard
8+
def funnily = 42: Unit // nowarn
9+
def impure = ("*" * 42).length
10+
def impurely: Unit = impure // warn impure discard
11+
12+
def i: Int = ???
13+
def parenthetically: Int =
14+
() // warn pure
15+
i
16+
transparent inline def reduced = ()
17+
def reductively: Int =
18+
reduced // no warn
19+
i

0 commit comments

Comments
 (0)