Skip to content

Check inline expansion for exclusion #23019

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 37 additions & 30 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import util.common.*
import util.{Property, SimpleIdentityMap, SrcPos}
import Applications.{tupleComponentTypes, wrapDefs, defaultArgument}

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

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

def tryExtension(using Context): Tree =
val altImports = new mutable.ListBuffer[TermRef]()
val altImports = ListBuffer.empty[TermRef]
findRef(tree.name, WildcardType, ExtensionMethod, EmptyFlags, qual.srcPos, altImports) match
case ref: TermRef =>
def tryExtMethod(ref: TermRef)(using Context) =
Expand All @@ -4061,7 +4062,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
tryExtMethod(ref)
else
// Try all possible imports and collect successes and failures
val successes, failures = new mutable.ListBuffer[(Tree, TyperState)]
val successes, failures = ListBuffer.empty[(Tree, TyperState)]
for alt <- ref :: altImports.toList do
val nestedCtx = ctx.fresh.setNewTyperState()
val app = tryExtMethod(alt)(using nestedCtx)
Expand Down Expand Up @@ -4719,22 +4720,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
return readapt(tree.cast(captured))

// drop type if prototype is Unit
if (pt isRef defn.UnitClass) {
if pt.isRef(defn.UnitClass) then
// local adaptation makes sure every adapted tree conforms to its pt
// so will take the code path that decides on inlining
val tree1 = adapt(tree, WildcardType, locked)
checkStatementPurity(tree1)(tree, ctx.owner, isUnitExpr = true)

if ctx.settings.Whas.valueDiscard
&& !ctx.isAfterTyper
&& !tree.isInstanceOf[Inlined]
&& !isThisTypeResult(tree)
&& !isAscribedToUnit(tree)
then
report.warning(ValueDiscarding(tree.tpe), tree.srcPos)

checkValueDiscard(tree)
return tpd.Block(tree1 :: Nil, unitLiteral)
}

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

private def checkStatementPurity(tree: tpd.Tree)(original: untpd.Tree, exprOwner: Symbol, isUnitExpr: Boolean = false)(using Context): Unit =
private def checkStatementPurity(tree: tpd.Tree)(original: untpd.Tree, exprOwner: Symbol, isUnitExpr: Boolean)(using Context): Unit =
inline def isPureNotInlinedUnit = tree match
case Inlined(_, Nil, Literal(k)) if k.tag == UnitTag => false // assert(2 + 2 == 4)
case tree => isPureExpr(tree)
if !tree.tpe.isErroneous
&& !ctx.isAfterTyper
&& !tree.isInstanceOf[Inlined]
&& isPureExpr(tree)
&& isPureNotInlinedUnit
&& !isSelfOrSuperConstrCall(tree)
then tree match
case closureDef(meth)
Expand All @@ -5010,13 +5004,26 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
// sometimes we do not have the original anymore and use the transformed tree instead.
// But taken together, the two criteria are quite accurate.
missingArgs(tree, tree.tpe.widen)
case _ if tree.hasAttachment(AscribedToUnit) =>
// The tree was ascribed to `Unit` explicitly to silence the warning.
()
case _ if isUnitExpr =>
report.warning(PureUnitExpression(original, tree.tpe), original.srcPos)
case _ =>
report.warning(PureExpressionInStatementPosition(original, exprOwner), original.srcPos)
case tree =>
val warnable = tree match
case inlined: Inlined => inlined.expansion
case tree => tree
// Check if the tree was ascribed to `Unit` explicitly to silence the warning.
if !isThisTypeResult(warnable) && !isAscribedToUnit(warnable) then
val msg =
if isUnitExpr then
PureUnitExpression(original, warnable.tpe)
else
PureExpressionInStatementPosition(original, exprOwner)
report.warning(msg, original.srcPos)

private def checkValueDiscard(tree: tpd.Tree)(using Context): Unit =
if ctx.settings.Whas.valueDiscard && !ctx.isAfterTyper then
val warnable = tree match
case inlined: Inlined => inlined.expansion
case tree => tree
if !isThisTypeResult(warnable) && !isAscribedToUnit(warnable) then
report.warning(ValueDiscarding(warnable.tpe), tree.srcPos)

/** Types the body Scala 2 macro declaration `def f = macro <body>` */
protected def typedScala2MacroBody(call: untpd.Tree)(using Context): Tree =
Expand Down
19 changes: 19 additions & 0 deletions tests/warn/i23018.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//> using options -Wvalue-discard

transparent inline def toto: Any = 1
transparent inline def uhoh = 42: Unit // nowarn
def tata: Unit = toto // warn pure Discard
def hmm: Unit = uhoh
def literally: Unit = 42 // warn pure Discard
def funnily = 42: Unit // nowarn
def impure = ("*" * 42).length
def impurely: Unit = impure // warn impure discard

def i: Int = ???
def parenthetically: Int =
() // warn pure
i
transparent inline def reduced = ()
def reductively: Int =
reduced // no warn
i
Loading