summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGilles Dubochet <gilles.dubochet@epfl.ch>2009-11-08 15:44:10 +0000
committerGilles Dubochet <gilles.dubochet@epfl.ch>2009-11-08 15:44:10 +0000
commit507cd9ef5094f8423c0b897eac0421aeb2e4d4ab (patch)
tree88da7657a03d23fbffd06569db443c25bbd8223e /src
parentb2bf6d3d0956516a0d86d3d158bf7d94d7ac0cd1 (diff)
downloadscala-507cd9ef5094f8423c0b897eac0421aeb2e4d4ab.tar.gz
scala-507cd9ef5094f8423c0b897eac0421aeb2e4d4ab.tar.bz2
scala-507cd9ef5094f8423c0b897eac0421aeb2e4d4ab.zip
Tighter type checking rules for structural type...
Tighter type checking rules for structural types that fix issues #967, #1004, #1388, #1494, and #1906.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Symbols.scala6
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Types.scala16
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala68
3 files changed, 75 insertions, 15 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
index 25f418bc19..3b9ffa4f58 100644
--- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
@@ -599,6 +599,12 @@ trait Symbols {
isClass && (isAnonymousClass || isRefinementClass || isLocal ||
!owner.isPackageClass && owner.isLocalClass)
+ /** Is this class or type defined as a structural refinement type?
+ */
+ final def isStructuralRefinement: Boolean =
+ (isClass || isType || isModule) && info.normalize/*.underlying*/.isStructuralRefinement
+
+
/** Is this symbol a member of class `clazz'
*/
def isMemberOf(clazz: Symbol) =
diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala
index e155f18489..676f19205a 100644
--- a/src/compiler/scala/tools/nsc/symtab/Types.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Types.scala
@@ -262,6 +262,9 @@ trait Types {
/** Is this type guaranteed not to have `null' as a value? */
def isNotNull: Boolean = false
+ /** Is this type a structural refinement type (it 'refines' members that have not been inherited) */
+ def isStructuralRefinement: Boolean = false
+
/** Does this depend on an enclosing method parameter? */
def isDependent: Boolean = IsDependentCollector.collect(this)
@@ -1232,6 +1235,10 @@ trait Types {
override def narrow: Type = typeSymbol.thisType
override def isNotNull: Boolean = parents exists (_.isNotNull)
+ override def isStructuralRefinement: Boolean =
+ (typeSymbol.isRefinementClass || typeSymbol.isAnonymousClass) &&
+ (decls.toList exists { entry => !entry.isConstructor && entry.allOverriddenSymbols.isEmpty })
+
// override def isNullable: Boolean =
// parents forall (p => p.isNullable && !p.typeSymbol.isAbstractType);
@@ -1729,7 +1736,14 @@ A type's typeSymbol should never be inspected directly.
else if (sym.isModuleClass)
objectPrefix + str
else if (sym.isAnonymousClass && sym.isInitialized && !settings.debug.value)
- thisInfo.parents.mkString("", " with ", "{ ... }")
+ thisInfo.parents.mkString(" with ") + {
+ if (sym.isStructuralRefinement)
+ ((decls.toList filter { entry =>
+ !entry.isConstructor && entry.allOverriddenSymbols.isEmpty && !entry.hasFlag(PRIVATE)
+ }) map { entry => entry.defString }).mkString("{", "; ", "}")
+ else
+ ""
+ }
else if (sym.isRefinementClass && sym.isInitialized)
thisInfo.toString
else str
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 389e26f273..619f5324be 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1573,11 +1573,28 @@ trait Typers { self: Analyzer =>
}
}
- private def checkStructuralCondition(refinement: Symbol, vparam: ValDef) {
- val tp = vparam.symbol.tpe
- if (tp.typeSymbol.isAbstractType && !(tp.typeSymbol.hasTransOwner(refinement)))
- error(vparam.tpt.pos,"Parameter type in structural refinement may not refer to abstract type defined outside that same refinement")
- }
+ /** Check if a method is defined in such a way that it can be called.
+ * A method cannot be called if it is a non-private member of a structural type
+ * and if its parameter's types are not one of
+ * - this.type
+ * - a type member of the structural type
+ * - an abstract type declared outside of the structural type. */
+ def checkMethodStructuralCompatible(meth: Symbol): Unit =
+ if (meth.owner.isStructuralRefinement && meth.allOverriddenSymbols.isEmpty && (!meth.hasFlag(PRIVATE) && meth.privateWithin == NoSymbol)) {
+ val tp: Type = meth.tpe match {
+ case mt: MethodType => mt
+ case pt: PolyType => pt.resultType
+ case _ => NoType
+ }
+ for (paramType <- tp.paramTypes) {
+ if (paramType.typeSymbol.isAbstractType && !(paramType.typeSymbol.hasTransOwner(meth.owner)))
+ unit.error(meth.pos,"Parameter type in structural refinement may not refer to an abstract type defined outside that refinement")
+ else if (paramType.typeSymbol.isAbstractType && !(paramType.typeSymbol.hasTransOwner(meth)))
+ unit.error(meth.pos,"Parameter type in structural refinement may not refer to a type member of that refinement")
+ else if (paramType.isInstanceOf[ThisType] && paramType.typeSymbol == meth.owner)
+ unit.error(meth.pos,"Parameter type in structural refinement may not refer to the type of that refinement (self type)")
+ }
+ }
/** does given name name an identifier visible at this point?
*
@@ -1697,24 +1714,20 @@ trait Typers { self: Analyzer =>
} else {
transformedOrTyped(ddef.rhs, tpt1.tpe)
}
+
+ checkMethodStructuralCompatible(meth)
+
if (meth.isPrimaryConstructor && meth.isClassConstructor &&
phase.id <= currentRun.typerPhase.id && !reporter.hasErrors)
computeParamAliases(meth.owner, vparamss1, rhs1)
if (tpt1.tpe.typeSymbol != NothingClass && !context.returnsSeen) rhs1 = checkDead(rhs1)
- // If only refinement owned methods are checked, invalid code can result; see ticket #2144.
- def requiresStructuralCheck = meth.allOverriddenSymbols.isEmpty && (
- meth.owner.isRefinementClass ||
- (!meth.isConstructor && !meth.isSetter && meth.owner.isAnonymousClass)
- )
- if (requiresStructuralCheck)
- for (vparam <- ddef.vparamss.flatten)
- checkStructuralCondition(meth.owner, vparam)
-
if (phase.id <= currentRun.typerPhase.id && meth.owner.isClass &&
meth.paramss.exists(ps => ps.exists(_.hasFlag(DEFAULTPARAM)) && isRepeatedParamType(ps.last.tpe)))
error(meth.pos, "a parameter section with a `*'-parameter is not allowed to have default arguments")
+ checkMethodStructuralCompatible(meth)
+
treeCopy.DefDef(ddef, typedMods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType
}
@@ -1770,6 +1783,33 @@ trait Typers { self: Analyzer =>
if (settings.YwarnShadow.value) checkShadowings(stat)
enterLabelDef(stat)
}
+ if (phaseId(currentPeriod) <= currentRun.typerPhase.id) {
+ block match {
+ case block @ Block(List(classDef @ ClassDef(_, _, _, _)), newInst @ Apply(Select(New(_), _), _)) =>
+ // The block is an anonymous class definitions/instantiation pair
+ // -> members that are hidden by the type of the block are made private
+ val visibleMembers = pt match {
+ case WildcardType => classDef.symbol.info.decls.toList
+ case BoundedWildcardType(TypeBounds(lo, hi)) => lo.members
+ case _ => pt.members
+ }
+ for (member <- classDef.symbol.info.decls.toList
+ if member.isTerm && !member.isConstructor &&
+ member.allOverriddenSymbols.isEmpty &&
+ (!member.hasFlag(PRIVATE) && member.privateWithin == NoSymbol) &&
+ !(visibleMembers exists { visible =>
+ visible.name == member.name &&
+ member.tpe <:< visible.tpe.substThis(visible.owner, ThisType(classDef.symbol))
+ })
+ ) {
+ member.resetFlag(PROTECTED)
+ member.resetFlag(LOCAL)
+ member.setFlag(PRIVATE)
+ member.privateWithin = NoSymbol
+ }
+ case _ =>
+ }
+ }
val stats1 = typedStats(block.stats, context.owner)
val expr1 = typed(block.expr, mode & ~(FUNmode | QUALmode), pt)
val block1 = treeCopy.Block(block, stats1, expr1)