summaryrefslogblamecommitdiff
path: root/sources/scala/tools/nsc/typechecker/Typers.scala
blob: 441d6ab1e58e60e13c7887921edcda7ad0fe42a9 (plain) (tree)























                                                                



















                                                                                





                                                                 
                                                  
                                                         
       

     
                                                                 
                                                                     
 
                                                                                             

















                                                                                   


                                                              

                                                                         
                                               
 
                                                                                        


                                                              
                            
                                                 

     

                                                                                                                  
                                                              
                                                         
                                           



                                                                                 
                                                   








                                                                                         
                                                                                               





                                                    
                                                                                   
 
                                       
                                        
                                                                                  
                      

                                                           
                                                                                             


                                        
                              







                                                                    
                                                      




                                                                                      
                                                                                             
 

                                                            

                                           








                                                                                               
             
                             
































                                                                                            

                                                                                            















                                                                                   
/* NSC -- new scala compiler
 * Copyright 2005 LAMP/EPFL
 * @author  Martin Odersky
 */
// $Id$
package scala.tools.nsc.typechecker;

import scala.tools.util.Position;

/** Methods to create symbols and to enter them into scopes. */
trait Typers: Analyzer {
  import symtab.Flags;
  import symtab.Flags._;
  import global._;

  abstract class TypeCompleter(val tree: Tree) extends LazyType;

  class Typer(context: Context) {
    import context.unit;

    val typechecker = new TypeChecker(context);

    def typeCompleter(tree: Tree) = new TypeCompleter(tree) {
      override def complete(sym: Symbol): unit = {
        if (settings.debug.value) log("defining " + sym);
        sym.setInfo(typeSig(tree));
        if (settings.debug.value) log("defined " + sym);
        validate(sym);
      }
    }

    def getterTypeCompleter(tree: Tree) = new TypeCompleter(tree) {
      override def complete(sym: Symbol): unit = {
        if (settings.debug.value) log("defining " + sym);
        sym.setInfo(PolyType(List(), typeSig(tree)));
        if (settings.debug.value) log("defined " + sym);
        validate(sym);
      }
    }

    def setterTypeCompleter(tree: Tree) = new TypeCompleter(tree) {
      override def complete(sym: Symbol): unit = {
        if (settings.debug.value) log("defining " + sym);
        sym.setInfo(MethodType(List(typeSig(tree)), definitions.UnitClass.tpe));
        if (settings.debug.value) log("defined " + sym);
        validate(sym);
      }
    }

    def selfTypeCompleter(tree: Tree) = new TypeCompleter(tree) {
      override def complete(sym: Symbol): unit = {
        sym.setInfo(typechecker.transformType(tree).tpe);
      }
    }

    private def deconstIfNotFinal(sym: Symbol, tpe: Type): Type =
      if (sym.isVariable || sym.hasFlag(FINAL)) tpe.deconst else tpe;

    def enterValueParams(owner: Symbol, vparamss: List[List[ValDef]]): List[List[Symbol]] = {
      def enterValueParam(param: ValDef): Symbol = {
	param.symbol = owner.newValueParameter(param.pos, param.name)
	  .setInfo(typeCompleter(param));
        context.scope enter param.symbol;
        param.symbol
      }
      vparamss.map(.map(enterValueParam))
    }

    /** A creator for polytypes. If tparams is empty, simply returns result type */
    private def makePolyType(tparams: List[Symbol], tpe: Type): Type =
      if (tparams.isEmpty) tpe
      else
	PolyType(tparams, tpe match {
	  case PolyType(List(), tpe1) => tpe1
	  case _ => tpe
	});

    private def templateSig(templ: Template): Type = {
      val clazz = context.owner;
      val parents = typechecker.parentTypes(templ) map (.tpe);
      val decls = new Scope();
      new Namer(context.make(templ, clazz, decls)).enterSyms(templ.body);
      ClassInfoType(parents, decls, clazz)    }

    private def classSig(tparams: List[AbsTypeDef], tpt: Tree, impl: Template): Type = {
      val tparamSyms = typechecker.reenterTypeParams(tparams);
      if (!tpt.isEmpty)
        context.owner.typeOfThis = selfTypeCompleter(tpt);
      else tpt.tpe = NoType;
      makePolyType(tparamSyms, templateSig(impl))
    }

    private def methodSig(tparams: List[AbsTypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): Type = {
      val meth = context.owner;
      val tparamSyms = typechecker.reenterTypeParams(tparams);
      val vparamSymss = enterValueParams(meth, vparamss);
      val restype = deconstIfNotFinal(meth,
	if (tpt.isEmpty) {
	  tpt.tpe = if (meth.name == nme.CONSTRUCTOR) context.enclClass.owner.tpe
		    else typechecker.transformExpr(rhs).tpe;
	  tpt.tpe
	} else typechecker.transformType(tpt).tpe);
      def mkMethodType(vparams: List[Symbol], restpe: Type) =
	MethodType(vparams map (.tpe), restpe);
      makePolyType(
	tparamSyms,
	if (vparamSymss.isEmpty) PolyType(List(), restype)
	else (vparamSymss :\ restype)(mkMethodType))
    }

    private def aliasTypeSig(tpsym: Symbol, tparams: List[AbsTypeDef], rhs: Tree): Type =
      makePolyType(typechecker.reenterTypeParams(tparams), typechecker.transformType(rhs).tpe);

    private def typeSig(tree: Tree): Type =
      try {
	val sym: Symbol = tree.symbol;
	tree match {
	  case ClassDef(_, _, tparams, tpt, impl) =>
	    new Typer(context.makeNewScope(tree, sym)).classSig(tparams, tpt, impl)

	  case ModuleDef(_, _, impl) =>
	    val clazz = sym.moduleClass;
            clazz.setInfo(new Typer(context.make(tree, clazz)).templateSig(impl));
            clazz.tpe;

	  case DefDef(_, _, tparams, vparamss, tpt, rhs) =>
	    new Typer(context.makeNewScope(tree, sym)).methodSig(tparams, vparamss, tpt, rhs)

	  case ValDef(_, _, tpt, rhs) =>
            deconstIfNotFinal(sym,
              if (tpt.isEmpty)
                if (rhs.isEmpty) {
		  unit.error(tpt.pos, "missing parameter type");
                  ErrorType
                } else {
                  tpt.tpe = new TypeChecker(context.make(tree, sym))
                    .transformExpr(rhs).tpe;
                  tpt.tpe
                }
              else typechecker.transformType(tpt).tpe)

	  case AliasTypeDef(_, _, tparams, rhs) =>
            new Typer(context.makeNewScope(tree, sym)).aliasTypeSig(sym, tparams, rhs)

	  case AbsTypeDef(_, _, lo, hi) =>
            TypeBounds(typechecker.transformType(lo).tpe, typechecker.transformType(hi).tpe);

          case Import(expr, selectors) =>
            val expr1 = typechecker.transformQualExpr(expr);
	    val base = expr1.tpe;
            typechecker.checkStable(expr1);
            def checkSelectors(selectors: List[Pair[Name, Name]]): unit = selectors match {
              case Pair(from, to) :: rest =>
                if (from != nme.WILDCARD && base != ErrorType &&
		    base.member(from) == NoSymbol && base.member(from.toTypeName) == NoSymbol)
	          unit.error(tree.pos, from.decode + " is not a member of " + expr);
	        if (to != null && to != nme.WILDCARD && (rest exists (sel => sel._2 == to)))
		  unit.error(tree.pos, to.decode + " appears twice as a target of a renaming");
                checkSelectors(rest)
              case Nil =>
	    }
            ImportType(expr1)
        }
      } catch {
        case ex: TypeError =>
	  typechecker.reportTypeError(tree.pos, ex);
	  ErrorType
      }

    /** Check that symbol's definition is well-formed. This means:
     *   - no conflicting modifiers
     *   - `abstract' modifier only for classes
     *   - `override' modifier never for classes
     *   - `def' modifier never for parameters of case classes
     *   - declarations only in traits or abstract classes
     *   - todo: in desugarize: replace ABSTRACT OVERRIDE with ABSOVERRIDE
     */
    def validate(sym: Symbol): unit = {
      def checkNoConflict(flag1: int, flag2: int): unit =
	if (sym.hasFlag(flag1) && sym.hasFlag(flag2))
	  unit.error(sym.pos,
	    if (flag1 == DEFERRED)
	      "abstract member may not have " + Flags.flagsToString(flag2) + " modifier";
	    else
	      "illegal combination of modifiers: " +
	      Flags.flagsToString(flag1) + " and " + Flags.flagsToString(flag2));
      if (sym.hasFlag(ABSTRACT) && !sym.isClass)
	unit.error(sym.pos, "`abstract' modifier can be used only for classes; " +
	  "\nit should be omitted for abstract members");
      if (sym.hasFlag(OVERRIDE | ABSOVERRIDE) && sym.isClass)
	unit.error(sym.pos, "`override' modifier not allowed for classes");
      if (sym.info.symbol == definitions.FunctionClass(0) &&
	  sym.isValueParameter && sym.owner.isClass && sym.owner.hasFlag(CASE))
	unit.error(sym.pos, "pass-by-name arguments not allowed for case class parameters");
      if ((sym.flags & DEFERRED) != 0) {
	if (!sym.isValueParameter && !sym.isTypeParameter &&
	    (!sym.owner.isClass || sym.owner.isModuleClass || sym.owner.isAnonymousClass)) {
	  unit.error(sym.pos,
	    "only classes can have declared but undefined members" +
	    (if (!sym.isVariable) ""
	     else "\n(Note that variables need to be initialized to be defined)"));
	  sym.resetFlag(DEFERRED);
	}
      }
      checkNoConflict(DEFERRED, PRIVATE);
      checkNoConflict(FINAL, SEALED);
      if (!sym.hasFlag(MODULE)) checkNoConflict(FINAL, PRIVATE);
      checkNoConflict(PRIVATE, PROTECTED);
      checkNoConflict(PRIVATE, OVERRIDE);
      checkNoConflict(DEFERRED, FINAL);
    }
  }
}