Skip to content

Move to error case classes (Friday) #2484

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

Merged
merged 3 commits into from
May 19, 2017
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
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ public enum ErrorMessageID {
TypeDoesNotTakeParametersID,
ParameterizedTypeLacksArgumentsID,
VarValParametersMayNotBeCallByNameID,
MissingTypeParameterForID,
DoesNotConformToBoundID,
DoesNotConformToSelfTypeID,
DoesNotConformToSelfTypeCantBeInstantiatedID,
AbstractMemberMayNotHaveModifierID,
TopLevelCantBeImplicitID,
TypesAndTraitsCantBeImplicitID,
OnlyClassesCanBeAbstractID,
AbstractOverrideOnlyInTraitsID,
TraitsMayNotBeFinalID,
NativeMembersMayNotHaveImplementationID,
OnlyClassesCanHaveDeclaredButUndefinedMembersID,
CannotExtendAnyValID,
CannotHaveSameNameAsID,
;

public int errorNumber() {
Expand Down
130 changes: 130 additions & 0 deletions compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import printing.Formatting
import ErrorMessageID._
import Denotations.SingleDenotation
import dotty.tools.dotc.ast.Trees
import dotty.tools.dotc.core.Flags.{FlagSet, Mutable}
import dotty.tools.dotc.core.SymDenotations.SymDenotation

object messages {
Expand Down Expand Up @@ -1351,4 +1352,133 @@ object messages {
| ${"}"}
|"""
}

case class MissingTypeParameterFor(tpe: Type)(implicit ctx: Context)
extends Message(MissingTypeParameterForID) {
val msg = hl"missing type parameter for ${tpe}"
val kind = "Syntax"
val explanation = ""
}

case class DoesNotConformToBound(tpe: Type, which: String, bound: Type)(
err: typer.ErrorReporting.Errors)(implicit ctx: Context)
extends Message(DoesNotConformToBoundID) {
val msg = hl"Type argument ${tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(tpe, bound)}"
val kind = "Type Mismatch"
val explanation = ""
}

case class DoesNotConformToSelfType(category: String, selfType: Type, cls: Symbol,
otherSelf: Type, relation: String, other: Symbol)(
implicit ctx: Context)
extends Message(DoesNotConformToSelfTypeID) {
val msg = hl"""$category: self type $selfType of $cls does not conform to self type $otherSelf
|of $relation $other"""
val kind = "Type Mismatch"
val explanation =
hl"""You mixed in $other which requires self type $otherSelf, but $cls has self type
|$selfType and does not inherit from $otherSelf.
|
|Note: Self types are indicated with the notation
| ${s"class "}$other ${"{ this: "}$otherSelf${" => "}
"""
}

case class DoesNotConformToSelfTypeCantBeInstantiated(tp: Type, selfType: Type)(
implicit ctx: Context)
extends Message(DoesNotConformToSelfTypeCantBeInstantiatedID) {
val msg = hl"""$tp does not conform to its self type $selfType; cannot be instantiated"""
val kind = "Type Mismatch"
val explanation =
hl"""To create an instance of $tp it needs to inherit $selfType in some way.
|
|Note: Self types are indicated with the notation
| ${s"class "}$tp ${"{ this: "}$selfType${" => "}
|"""
}

case class AbstractMemberMayNotHaveModifier(sym: Symbol, flag: FlagSet)(
implicit ctx: Context)
extends Message(AbstractMemberMayNotHaveModifierID) {
val msg = hl"""${"abstract"} $sym may not have `$flag' modifier"""
val kind = "Syntax"
val explanation = ""
}

case class TopLevelCantBeImplicit(sym: Symbol)(
implicit ctx: Context)
extends Message(TopLevelCantBeImplicitID) {
val msg = hl"""${"implicit"} modifier cannot be used for top-level definitions"""
val kind = "Syntax"
val explanation = ""
}

case class TypesAndTraitsCantBeImplicit(sym: Symbol)(
implicit ctx: Context)
extends Message(TypesAndTraitsCantBeImplicitID) {
val msg = hl"""${"implicit"} modifier cannot be used for types or traits"""
val kind = "Syntax"
val explanation = ""
}

case class OnlyClassesCanBeAbstract(sym: Symbol)(
implicit ctx: Context)
extends Message(OnlyClassesCanBeAbstractID) {
val msg = hl"""${"abstract"} modifier can be used only for classes; it should be omitted for abstract members"""
val kind = "Syntax"
val explanation = ""
}

case class AbstractOverrideOnlyInTraits(sym: Symbol)(
implicit ctx: Context)
extends Message(AbstractOverrideOnlyInTraitsID) {
val msg = hl"""${"abstract override"} modifier only allowed for members of traits"""
val kind = "Syntax"
val explanation = ""
}

case class TraitsMayNotBeFinal(sym: Symbol)(
implicit ctx: Context)
extends Message(TraitsMayNotBeFinalID) {
val msg = hl"""$sym may not be ${"final"}"""
val kind = "Syntax"
val explanation =
"A trait can never be final since it is abstract and must be extended to be useful."
}

case class NativeMembersMayNotHaveImplementation(sym: Symbol)(
implicit ctx: Context)
extends Message(NativeMembersMayNotHaveImplementationID) {
val msg = hl"""${"@native"} members may not have an implementation"""
val kind = "Syntax"
val explanation = ""
}

case class OnlyClassesCanHaveDeclaredButUndefinedMembers(sym: Symbol)(
implicit ctx: Context)
extends Message(OnlyClassesCanHaveDeclaredButUndefinedMembersID) {

private val varNote =
if (sym.is(Mutable)) "Note that variables need to be initialized to be defined."
else ""
val msg = hl"""only classes can have declared but undefined members"""
val kind = "Syntax"
val explanation = s"$varNote"
}

case class CannotExtendAnyVal(sym: Symbol)(implicit ctx: Context)
extends Message(CannotExtendAnyValID) {
val msg = hl"""$sym cannot extend ${"AnyVal"}"""
val kind = "Syntax"
val explanation = ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there something we can say here in explanation? I know you wanted to just get everything over to the new format first - but can't help myself 😆

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Traits do not support extending AnyVal" ?
"AnyVal instances must be representable in a single value, which excludes usage of traits"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I leave it up to your discretion :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone tries to make a trait extending AnyVal, they should be told about universal traits (traits extending Any): http://docs.scala-lang.org/overviews/core/value-classes.html

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This leads to the question how we'd support adding relevant links to Messages. Links to the lang specs will be helpful in many cases, Stackoverflow might be good, too.
But links do not always age well.

}

case class CannotHaveSameNameAs(sym: Symbol, cls: Symbol)(implicit ctx: Context)
extends Message(CannotHaveSameNameAsID) {
val msg = hl"""$sym cannot have the same name as ${cls.showLocated} -- class definitions cannot be overridden"""
val kind = "Syntax"
val explanation = ""
}


}
46 changes: 22 additions & 24 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,21 @@ import Constants._
import Scopes._
import CheckRealizable._
import ErrorReporting.errorTree

import annotation.unchecked
import util.Positions._
import util.{Stats, SimpleMap}
import util.{SimpleMap, Stats}
import util.common._
import transform.SymUtils._
import Decorators._
import Uniques._
import ErrorReporting.{err, errorType}
import config.Printers.typr

import collection.mutable
import SymDenotations.NoCompleter
import dotty.tools.dotc.reporting.diagnostic.messages.CantInstantiateAbstractClassOrTrait
import dotty.tools.dotc.reporting.diagnostic.{ErrorMessageID, Message}
import dotty.tools.dotc.reporting.diagnostic.messages._
import dotty.tools.dotc.transform.ValueClasses._

object Checking {
Expand All @@ -42,11 +45,12 @@ object Checking {
def checkBounds(args: List[tpd.Tree], boundss: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context): Unit = {
(args, boundss).zipped.foreach { (arg, bound) =>
if (!bound.isHK && arg.tpe.isHK)
// see MissingTypeParameterFor
ctx.error(ex"missing type parameter(s) for $arg", arg.pos)
}
for ((arg, which, bound) <- ctx.boundsViolations(args, boundss, instantiate))
ctx.error(
ex"Type argument ${arg.tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(arg.tpe, bound)}",
DoesNotConformToBound(arg.tpe, which, bound)(err),
arg.pos.focus)
}

Expand Down Expand Up @@ -111,18 +115,16 @@ object Checking {
val stp = SkolemType(tp)
val selfType = tref.givenSelfType.asSeenFrom(stp, cls)
if (selfType.exists && !(stp <:< selfType))
ctx.error(ex"$tp does not conform to its self type $selfType; cannot be instantiated")
ctx.error(DoesNotConformToSelfTypeCantBeInstantiated(tp, selfType), pos)
}
case _ =>
}

/** Check that type `tp` is realizable. */
def checkRealizable(tp: Type, pos: Position)(implicit ctx: Context): Unit = {
val rstatus = realizability(tp)
if (rstatus ne Realizable) {
def msg = em"$tp is not a legal path\n since it${rstatus.msg}"
if (ctx.scala2Mode) ctx.migrationWarning(msg, pos) else ctx.error(msg, pos)
}
if (rstatus ne Realizable)
ctx.errorOrMigrationWarning(em"$tp is not a legal path\n since it${rstatus.msg}", pos)
}

/** A type map which checks that the only cycles in a type are F-bounds
Expand Down Expand Up @@ -304,50 +306,46 @@ object Checking {

/** Check that symbol's definition is well-formed. */
def checkWellFormed(sym: Symbol)(implicit ctx: Context): Unit = {
//println(i"check wf $sym with flags ${sym.flags}")
def fail(msg: String) = ctx.error(msg, sym.pos)
def varNote =
if (sym.is(Mutable)) "\n(Note that variables need to be initialized to be defined)"
else ""
def fail(msg: Message) = ctx.error(msg, sym.pos)

def checkWithDeferred(flag: FlagSet) =
if (sym.is(flag))
fail(i"abstract member may not have `$flag' modifier")
fail(AbstractMemberMayNotHaveModifier(sym, flag))
def checkNoConflict(flag1: FlagSet, flag2: FlagSet) =
if (sym.is(allOf(flag1, flag2)))
fail(i"illegal combination of modifiers: $flag1 and $flag2 for: $sym")

if (sym.is(ImplicitCommon)) {
if (sym.owner.is(Package))
fail(i"`implicit' modifier cannot be used for top-level definitions")
fail(TopLevelCantBeImplicit(sym))
if (sym.isType)
fail(i"`implicit' modifier cannot be used for types or traits")
fail(TypesAndTraitsCantBeImplicit(sym))
}
if (!sym.isClass && sym.is(Abstract))
fail(i"`abstract' modifier can be used only for classes; it should be omitted for abstract members")
fail(OnlyClassesCanBeAbstract(sym))
if (sym.is(AbsOverride) && !sym.owner.is(Trait))
fail(i"`abstract override' modifier only allowed for members of traits")
fail(AbstractOverrideOnlyInTraits(sym))
if (sym.is(Trait) && sym.is(Final))
fail(i"$sym may not be `final'")
fail(TraitsMayNotBeFinal(sym))
if (sym.hasAnnotation(defn.NativeAnnot)) {
if (!sym.is(Deferred))
fail(i"`@native' members may not have implementation")
fail(NativeMembersMayNotHaveImplementation(sym))
}
else if (sym.is(Deferred, butNot = Param) && !sym.isType && !sym.isSelfSym) {
if (!sym.owner.isClass || sym.owner.is(Module) || sym.owner.isAnonymousClass)
fail(i"only classes can have declared but undefined members$varNote")
fail(OnlyClassesCanHaveDeclaredButUndefinedMembers(sym))
checkWithDeferred(Private)
checkWithDeferred(Final)
checkWithDeferred(Inline)
}
if (sym.isValueClass && sym.is(Trait) && !sym.isRefinementClass)
fail(i"$sym cannot extend AnyVal")
fail(CannotExtendAnyVal(sym))
checkNoConflict(Final, Sealed)
checkNoConflict(Private, Protected)
checkNoConflict(Abstract, Override)
if (sym.isType && !sym.is(Deferred))
for (cls <- sym.allOverriddenSymbols.filter(_.isClass)) {
fail(i"$sym cannot have the same name as ${cls.showLocated} -- class definitions cannot be overridden")
fail(CannotHaveSameNameAs(sym, cls))
sym.setFlag(Private) // break the overriding relationship by making sym Private
}
}
Expand Down Expand Up @@ -610,7 +608,7 @@ trait Checking {
if (tpt.tpe.isHK && !ctx.compilationUnit.isJava) {
// be more lenient with missing type params in Java,
// needed to make pos/java-interop/t1196 work.
errorTree(tpt, ex"missing type parameter for ${tpt.tpe}")
errorTree(tpt, MissingTypeParameterFor(tpt.tpe))
}
else tpt

Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ object RefChecks {
def checkSelfConforms(other: TypeRef, category: String, relation: String) = {
val otherSelf = other.givenSelfType.asSeenFrom(cls.thisType, other.classSymbol)
if (otherSelf.exists && !(cinfo.selfType <:< otherSelf))
ctx.error(ex"$category: self type ${cinfo.selfType} of $cls does not conform to self type $otherSelf of $relation ${other.classSymbol}", cls.pos)
ctx.error(DoesNotConformToSelfType(category, cinfo.selfType, cls, otherSelf, relation, other.classSymbol),
cls.pos)
}
for (parent <- cinfo.classParents)
checkSelfConforms(parent, "illegal inheritance", "parent")
Expand Down
Loading