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
44 changes: 43 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package typer

import transform._
import core._
import Symbols._, Types._, Contexts._, Flags._, Names._, NameOps._
import Symbols._, Types._, Contexts._, Flags._, Names._, NameOps._, NameKinds._
import StdNames._, Denotations._, SymUtils._, Phases._, SymDenotations._
import NameKinds.DefaultGetterName
import Annotations._
Expand Down Expand Up @@ -1047,6 +1047,47 @@ object RefChecks {
report.error(i"private $sym cannot override ${other.showLocated}", sym.srcPos)
end checkNoPrivateOverrides

/** Check that unary method definition do not receive parameters.
* They can only receive inferred parameters such as type parameters and implicit parameters.
*/
def checkUnaryMethods(sym: Symbol)(using Context): Unit =
/** Check that the only term parameters are contextual or implicit */
def checkParameters(tpe: Type): Unit =
tpe match
case tpe: MethodType =>
if tpe.isImplicitMethod || tpe.isContextualMethod then
checkParameters(tpe.resType)
else
val what =
if tpe.paramNames.isEmpty then "empty parameter list.\n\nPossible fix: remove the `()` arguments."
else "parameters"
report.warning(s"Unary method cannot take $what", sym.sourcePos)
case tpe: PolyType =>
checkParameters(tpe.resType)
case _ =>
// ok

/** Skip leading type and contextual parameters, then skip the
* self parameter, and finally check the parameter
*/
def checkExtensionParameters(tpe: Type): Unit =
tpe match
case tpe: MethodType =>
assert(tpe.paramNames.length == 1)
if tpe.isContextualMethod then checkExtensionParameters(tpe.resType)
else checkParameters(tpe.resType)
case tpe: PolyType =>
checkExtensionParameters(tpe.resType)

if sym.name.startsWith(nme.UNARY_PREFIX.toString) then
if sym.is(Extension) || sym.name.is(ExtMethName) then
// if is method from `extension` or value class
checkExtensionParameters(sym.info)
else
checkParameters(sym.info)

end checkUnaryMethods

type LevelAndIndex = immutable.Map[Symbol, (LevelInfo, Int)]

class OptLevelInfo {
Expand Down Expand Up @@ -1254,6 +1295,7 @@ class RefChecks extends MiniPhase { thisPhase =>
checkExperimentalAnnots(tree.symbol)
checkExperimentalSignature(tree.symbol, tree)
checkImplicitNotFoundAnnotation.defDef(tree.symbol.denot)
checkUnaryMethods(tree.symbol)
tree
}

Expand Down
41 changes: 41 additions & 0 deletions tests/neg-custom-args/fatal-warnings/i9241.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class Foo {
def unary_~() : Foo = this // error
def unary_-(using Int)(): Foo = this // error
def unary_+()(implicit i: Int): Foo = this // error
def unary_![T](): Foo = this // error
}

class Bar {
def unary_~ : Bar = this
def unary_-(using Int): Bar = this
def unary_+(implicit i: Int): Bar = this
def unary_![T]: Bar = this
}

final class Baz private (val x: Int) extends AnyVal {
def unary_- : Baz = ???
def unary_+[T] : Baz = ???
def unary_!() : Baz = ??? // error
def unary_~(using Int) : Baz = ???
}

extension (x: Int)
def unary_- : Int = ???
def unary_+[T] : Int = ???
def unary_!() : Int = ??? // error
def unary_~(using Int) : Int = ???
end extension

extension [T](x: Short)
def unary_- : Int = ???
def unary_+[U] : Int = ???
def unary_!() : Int = ??? // error
def unary_~(using Int) : Int = ???
end extension

extension (using Int)(x: Byte)
def unary_- : Int = ???
def unary_+[U] : Int = ???
def unary_!() : Int = ??? // error
def unary_~(using Int) : Int = ???
end extension