Skip to content
Merged
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
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/inlines/Inlines.scala
Original file line number Diff line number Diff line change
Expand Up @@ -623,12 +623,13 @@ object Inlines:
val withAdjustedThisTypes = if call.symbol.is(Macro) then fixThisTypeModuleClassReferences(unpacked) else unpacked
(call.tpe & withAdjustedThisTypes, withAdjustedThisTypes != unpacked)
else (call.tpe, false)
val resultType = target.widenIfUnstable
if forceCast then
// we need to force the cast for issues with ThisTypes, as ensureConforms will just
// check subtyping and then choose not to cast, leaving the previous, incorrect type
inlined.cast(target)
inlined.cast(resultType)
else
inlined.ensureConforms(target)
inlined.ensureConforms(resultType)
// 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
Expand Down
49 changes: 28 additions & 21 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1157,7 +1157,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 @@ -4017,7 +4017,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 @@ -4907,15 +4908,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
// 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)
end if

Expand Down Expand Up @@ -5183,11 +5176,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 =
if !tree.tpe.isErroneous
&& !ctx.isAfterTyper
&& !tree.isInstanceOf[Inlined]
&& isPureExpr(tree)
&& tree.match
case Inlined(_, Nil, Literal(k)) if k.tag == UnitTag => false // e.g., assert(2 + 2 == 4)
case tree => isPureExpr(tree)
&& !isSelfOrSuperConstrCall(tree)
then tree match
case closureDef(meth)
Expand All @@ -5201,13 +5196,25 @@ 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 =>
withDiscardWarnable(tree): warnable =>
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
withDiscardWarnable(tree): warnable =>
report.warning(ValueDiscarding(warnable.tpe), tree.srcPos)

// Check if the tree was ascribed to `Unit` explicitly to silence the warning.
private def withDiscardWarnable(tree: tpd.Tree)(op: (tpd.Tree) => Unit)(using Context): Unit =
val warnable = tree match
case inlined: Inlined => inlined.expansion
case tree => tree
if !isThisTypeResult(warnable) && !isAscribedToUnit(warnable) then
op(warnable)

/** Types the body Scala 2 macro declaration `def f = macro <body>` */
protected def typedScala2MacroBody(call: untpd.Tree)(using Context): Tree =
Expand Down
22 changes: 22 additions & 0 deletions tests/pos/i25091.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
transparent trait VersionComponent[T]:
protected def minimumValue: Int
inline def minimum: T = wrap(minimumValue)
inline def reset: T = minimum
inline def wrap(value: Int): T
inline def unwrap(value: T): Int

opaque type MajorVersion = Int
object MajorVersion extends VersionComponent[MajorVersion]:
protected inline def minimumValue: Int = 0
inline def wrap(value: Int): MajorVersion = value
inline def unwrap(mv: MajorVersion): Int = mv

case class Version(major: MajorVersion)
object Version:
trait Increment[F]:
extension (v: Version) def increment: Version

object Increment:
given Increment[MajorVersion]:
extension (v: Version)
inline def increment: Version = Version(MajorVersion.reset)
15 changes: 15 additions & 0 deletions tests/pos/i25091b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
object Test:
trait VersionComponent[T]:
inline def minimum: T = wrap(42)
inline def wrap(value: Int): T

opaque type MajorVersionType = Int
object MajorVersion extends VersionComponent[MajorVersionType]:
inline def wrap(value: Int): MajorVersionType = value
inline def reset: MajorVersionType = minimum

trait Increment[F]:
def increment: MajorVersionType

class MyIncrement extends Increment[MajorVersionType]:
inline def increment: MajorVersionType = MajorVersion.reset
32 changes: 32 additions & 0 deletions tests/warn/i23018.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-- [E190] Potential Issue Warning: tests/warn/i23018.scala:6:17 --------------------------------------------------------
6 |def tata: Unit = toto // warn pure Discard
| ^^^^
| Discarded non-Unit value of type Int. Add `: Unit` to discard silently.
|
| longer explanation available when compiling with `-explain`
-- [E175] Potential Issue Warning: tests/warn/i23018.scala:19:37 -------------------------------------------------------
19 |transparent inline def even: Unit = (42: C) // warn impure discard
| ^^^^^
| discarded non-Unit value of type C. Add `: Unit` to discard silently.
-- [E190] Potential Issue Warning: tests/warn/i23018.scala:21:22 -------------------------------------------------------
21 |def literally: Unit = 42 // warn pure Discard
| ^^
| Discarded non-Unit value of type Int. Add `: Unit` to discard silently.
|
| longer explanation available when compiling with `-explain`
-- [E175] Potential Issue Warning: tests/warn/i23018.scala:24:21 -------------------------------------------------------
24 |def impurely: Unit = impure // warn impure discard
| ^^^^^^
| discarded non-Unit value of type Int. Add `: Unit` to discard silently.
-- [E129] Potential Issue Warning: tests/warn/i23018.scala:28:2 --------------------------------------------------------
28 | () // warn pure expression (but not Discard)
| ^^
| A pure expression does nothing in statement position
|
| longer explanation available when compiling with `-explain`
-- [E190] Potential Issue Warning: tests/warn/i23018.scala:37:27 -------------------------------------------------------
37 |def embodied: Unit = bodily(toto) // warn pure Discard
| ^^^^^^^^^^^^
| Discarded non-Unit value of type Int. Add `: Unit` to discard silently.
|
| longer explanation available when compiling with `-explain`
39 changes: 39 additions & 0 deletions tests/warn/i23018.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//> using options -Wvalue-discard

import scala.language.experimental.genericNumberLiterals

transparent inline def toto: Any = 1
def tata: Unit = toto // warn pure Discard

transparent inline def uhoh = 42: Unit // nowarn
def hmm: Unit = uhoh

class C
object C:
val K = C()
class Converter extends scala.util.FromDigits[C]:
inline transparent def fromDigits(digits: String): C = C.K
given Converter()

transparent inline def odd = (42: C): Unit // nowarn
transparent inline def even: Unit = (42: C) // warn impure discard

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 expression (but not Discard)
i
transparent inline def reduced = ()
def reductively: Int =
reduced // no warn inlined literal unit, e.g., assert(2 + 2 == 4)
i

// three different expansions, but warning is early at typer, so elaboration doesn't matter
transparent inline def bodily[A](body: => A): A = body
def embodied: Unit = bodily(toto) // warn pure Discard
def disembodied: Unit = bodily(reduced) // nowarn
def um: Unit = bodily(uhoh) // nowarn
4 changes: 2 additions & 2 deletions tests/warn/i23250.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//> using options -Wunused:all -Werror
//> using options -Wunused:all

trait MonadError[F[_], E]
type MonadThrow[F[_]] = MonadError[F, Throwable]
Expand All @@ -10,7 +10,7 @@ trait WriteResult
trait MetaStreamsSyntax:
extension [F[_]](ms: MetaStreams[F])(using MonadThrow[F])
def setMaxAge(): F[WriteResult] =
summon[MonadThrow[F]]
summon[MonadThrow[F]] // warn pure expr
ms.use[WriteResult]

def setTruncateBefore(): F[WriteResult] =
Expand Down
Loading