diff options
author | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2009-11-08 15:44:10 +0000 |
---|---|---|
committer | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2009-11-08 15:44:10 +0000 |
commit | 507cd9ef5094f8423c0b897eac0421aeb2e4d4ab (patch) | |
tree | 88da7657a03d23fbffd06569db443c25bbd8223e /src | |
parent | b2bf6d3d0956516a0d86d3d158bf7d94d7ac0cd1 (diff) | |
download | scala-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.scala | 6 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Types.scala | 16 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 68 |
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) |