root/trunk/ProjectFortress/src/com/sun/fortress/scala_src/typechecker/OverloadingChecker.scala @ 3915

Revision 3915, 24.1 KB (checked in by jrhil47, 5 months ago)

[index] Changed all Functional indices to store a thunk to get the return types, since during type checking the return types might need to be lazily evaluated. Also changed Functional indices to yield an Option for return types.
[type checker] Started implementing extraction of bindings from nodes for type environments.

Line 
1/*******************************************************************************
2    Copyright 2009 Sun Microsystems, Inc.,
3    4150 Network Circle, Santa Clara, California 95054, U.S.A.
4    All rights reserved.
5
6    U.S. Government Rights - Commercial software.
7    Government users are subject to the Sun Microsystems, Inc. standard
8    license agreement and applicable provisions of the FAR and its supplements.
9
10    Use is subject to license terms.
11
12    This distribution may include materials developed by third parties.
13
14    Sun, Sun Microsystems, the Sun logo and Java are trademarks or registered
15    trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
16 ******************************************************************************/
17
18package com.sun.fortress.scala_src.typechecker
19
20import _root_.java.util.ArrayList
21import _root_.java.util.{List => JavaList}
22import _root_.java.util.{Set => JavaSet}
23import edu.rice.cs.plt.tuple.{Option => JavaOption}
24import scala.collection.Map
25import scala.collection.Set
26import scala.collection.mutable.HashMap
27import scala.collection.mutable.HashSet
28import com.sun.fortress.compiler.GlobalEnvironment
29import com.sun.fortress.compiler.index.CompilationUnitIndex
30import com.sun.fortress.compiler.index.ObjectTraitIndex
31import com.sun.fortress.compiler.index.ProperTraitIndex
32import com.sun.fortress.compiler.index.TraitIndex
33import com.sun.fortress.compiler.index.{Function => JavaFunction}
34import com.sun.fortress.compiler.index.{Functional => JavaFunctional}
35import com.sun.fortress.compiler.index.{Method => JavaMethod}
36import com.sun.fortress.compiler.disambiguator.ExprDisambiguator.HierarchyHistory
37import com.sun.fortress.compiler.typechecker.TypeAnalyzer
38import com.sun.fortress.exceptions.InterpreterBug
39import com.sun.fortress.exceptions.StaticError
40import com.sun.fortress.exceptions.TypeError
41import com.sun.fortress.nodes._
42import com.sun.fortress.nodes_util.NodeFactory
43import com.sun.fortress.nodes_util.NodeUtil
44import com.sun.fortress.nodes_util.Span
45import com.sun.fortress.parser_util.IdentifierUtil
46import com.sun.fortress.repository.FortressRepository
47import com.sun.fortress.scala_src.useful._
48import com.sun.fortress.scala_src.useful.Lists._
49import com.sun.fortress.scala_src.useful.Options._
50import com.sun.fortress.scala_src.useful.STypesUtil
51import com.sun.fortress.scala_src.useful.Sets._
52import com.sun.fortress.scala_src.nodes._
53
54/* Check the set of overloadings in this compilation unit.
55 *
56 * The following functionals are not checked yet:
57 *     functional methods
58 *     dotted methods
59 *     object constructors
60 *
61 * The following features are not (fully) checked yet:
62 *     static parameters
63 *     exclusion relationships
64 *     varargs parameters
65 *     keyword parameters
66 */
67class OverloadingChecker(compilation_unit: CompilationUnitIndex,
68                         globalEnv: GlobalEnvironment,
69                         repository: FortressRepository) {
70    var typeAnalyzer = TypeAnalyzer.make(new TraitTable(compilation_unit, globalEnv))
71    var errors = List[StaticError]()
72
73    /* Called by com.sun.fortress.compiler.StaticChecker.checkComponent
74     *       and com.sun.fortress.compiler.StaticChecker.checkApi
75     */
76    def checkOverloading(): JavaList[StaticError] = {
77        val fnsInComp = compilation_unit.functions
78        for ( f <- toSet(fnsInComp.firstSet) ; if isDeclaredName(f) ) {
79            checkOverloading(f, toSet(fnsInComp.matchFirst(f)).asInstanceOf[Set[JavaFunctional]])
80        }
81        val typesInComp = compilation_unit.typeConses
82        for ( t <- toSet(typesInComp.keySet) ;
83              if NodeUtil.isTraitOrObject(typesInComp.get(t)) ) {
84            val traitOrObject = typesInComp.get(t).asInstanceOf[TraitIndex]
85            /* All inherited abstract methods in object definitions and
86             * object expressions should be defined,
87             * with compatible signatures and modifiers.
88             */
89            if ( compilation_unit.ast.isInstanceOf[Component] &&
90                 traitOrObject.isInstanceOf[ObjectTraitIndex] ) {
91                val ast = traitOrObject.ast
92                val toCheck = inheritedAbstractMethods(toList(NodeUtil.getExtendsClause(ast)))
93                for ( t <- toCheck.keySet ) {
94                  for ( ds <- toCheck.get(t) ) {
95                    for ( d <- ds ) {
96                        if ( ! implement(d, toList(NodeUtil.getDecls(ast))) )
97                            error(NodeUtil.getSpan(d).toString,
98                                  "The inherited abstract method " + d +
99                                  " from the trait " + t +
100                                  "\n    in the object " + NodeUtil.getName(ast) +
101                                  " is not defined in the component " +
102                                  compilation_unit.ast.getName + ".")
103                     }
104                  }
105                }
106            }
107
108            /* The parameter type of a setter must be the same as the return type
109             * of a getter with the same name, if any.
110             */
111            for ( f <- toSet(traitOrObject.setters.keySet) ) {
112                if ( traitOrObject.getters.keySet.contains(f) ) {
113                    val getter = traitOrObject.getters.get(f)
114                    // Setter declarations are guaranteed to have a single parameter.
115                    val param = traitOrObject.setters.get(f).parameters.get(0)
116                    val span = getter.getSpan.toString
117                    if ( param.getIdType.isSome &&
118                         ! typeAnalyzer.equivalent(param.getIdType.unwrap,
119                                                   getter.getReturnType.unwrap).isTrue )
120                        error(span,
121                              "The parameter type of a setter must be " +
122                              "the same as\n    the return type of a getter " +
123                              "with the same name, if any.")
124                }
125            }
126            val methods = traitOrObject.dottedMethods
127            val staticParameters = new ArrayList[StaticParam]()
128            // Add static parameters of the enclosing trait or object
129            staticParameters.addAll( traitOrObject.staticParameters )
130            // Extend the type analyzer with the collected static parameters
131            val oldTypeAnalyzer = typeAnalyzer
132            for ( f <- toSet(methods.firstSet) ; if isDeclaredName(f) ) {
133                typeAnalyzer = typeAnalyzer.extend(staticParameters, none[WhereClause])
134                checkOverloading(f, toSet(methods.matchFirst(f)).asInstanceOf[Set[JavaFunctional]])
135            }
136            typeAnalyzer = oldTypeAnalyzer
137        }
138        toJavaList(errors)
139    }
140
141    /* Checks the validity of the overloaded function declarations. */
142    private def checkOverloading(name: IdOrOpOrAnonymousName,
143                                 set: Set[JavaFunctional]) = {
144        var signatures = List[((JavaList[StaticParam],Type,Type),Span)]()
145        for ( f <- set ; if isDeclaredFunctional(f) ) {
146            val result = f.getReturnType.unwrap
147            val param = paramsToType(f.parameters, f.getSpan)
148            val sparams = f.staticParameters
149            signatures.find(p => p._1 == (sparams,param,result)) match {
150                case Some((_,span)) =>
151                    error(mergeSpan(span, f.getSpan),
152                          "There are multiple declarations of " +
153                          name + " with the same type: " +
154                          param + " -> " + result)
155                case _ =>
156                    signatures = ((sparams, param, result), f.getSpan) :: signatures
157            }
158        }
159        var index = 1
160        for ( first <- signatures ) {
161            signatures.slice(index, signatures.length)
162            .foreach(second => if (! validOverloading(first, second, signatures) ) {
163                                   val firstO = toString(first)
164                                   val secondO = toString(second)
165                                   val mismatch = if (firstO < secondO)
166                                                      firstO + "\n and " + secondO
167                                                  else
168                                                      secondO + "\n and " + firstO
169                                   error(mergeSpan(first, second),
170                                         "Invalid overloading of " + name +
171                                         ":\n     " + mismatch)
172                               })
173            index += 1
174        }
175    }
176
177    /* Checks the overloading rules: subtype / exclusion / meet */
178    private def validOverloading(first: ((JavaList[StaticParam],Type,Type),Span),
179                                 second: ((JavaList[StaticParam],Type,Type),Span),
180                                 set: List[((JavaList[StaticParam],Type,Type),Span)]) =
181        subtype(first, second) || subtype(second, first) ||
182        exclusion(first, second) || meet(first, second, set)
183
184    /* Checks the overloading rule: subtype */
185    private def subtype(f: JavaFunction, g: JavaFunction): Boolean =
186        subtype(paramsToType(g.parameters, g.getSpan),
187                paramsToType(f.parameters, f.getSpan)) &&
188        subtype(f.getReturnType.unwrap, g.getReturnType.unwrap)
189
190    private def subtype(sub_type: Type, super_type: Type): Boolean =
191        typeAnalyzer.subtype(sub_type, super_type).isTrue
192
193    private def subtype(sub_type: ((JavaList[StaticParam],Type,Type),Span),
194                        super_type: ((JavaList[StaticParam],Type,Type),Span)): Boolean = {
195        val oldTypeAnalyzer = typeAnalyzer
196        // Add static parameters of the method
197        val staticParameters = new ArrayList[StaticParam]()
198        staticParameters.addAll(sub_type._1._1)
199        staticParameters.addAll(super_type._1._1)
200        typeAnalyzer = typeAnalyzer.extend(staticParameters, none[WhereClause])
201        val result = subtype(super_type._1._2, sub_type._1._2) &&
202                     subtype(sub_type._1._3, super_type._1._3)
203        typeAnalyzer = oldTypeAnalyzer
204        result
205    }
206
207    /* Checks the overloading rule: exclusion
208     * Invariant: firstParam is not equal to secondParam
209     * The following types are not yet supported:
210     *     Types tagged with dimensions or units
211     *     Effects on arrow types
212     *     Keyword parameters and varargs parameters
213     *     Intersection types
214     *     Union types
215     *     Fixed-point types
216     */
217    private def exclusion(first: ((JavaList[StaticParam],Type,Type),Span),
218                          second: ((JavaList[StaticParam],Type,Type),Span)): Boolean = {
219        val oldTypeAnalyzer = typeAnalyzer
220        // Add static parameters of the method
221        val staticParameters = new ArrayList[StaticParam]()
222        staticParameters.addAll(first._1._1)
223        staticParameters.addAll(second._1._1)
224        typeAnalyzer = typeAnalyzer.extend(staticParameters, none[WhereClause])
225        val result = new ExclusionOracle(typeAnalyzer,
226                                         new ErrorLog()).excludes(first._1._2,
227                                                                  second._1._2)
228        typeAnalyzer = oldTypeAnalyzer
229        result
230    }
231
232    /* Checks the overloading rule: meet */
233    /* Not yet fully implemented... */
234    private def meet(first: ((JavaList[StaticParam],Type,Type),Span),
235                     second: ((JavaList[StaticParam],Type,Type),Span),
236                     set: List[((JavaList[StaticParam],Type,Type),Span)]) = {
237        val oldTypeAnalyzer = typeAnalyzer
238        // Add static parameters of the method
239        val staticParameters = new ArrayList[StaticParam]()
240        staticParameters.addAll(first._1._1)
241        staticParameters.addAll(second._1._1)
242        typeAnalyzer = typeAnalyzer.extend(staticParameters, none[WhereClause])
243        var result = false
244        val exclusionOracle = new ExclusionOracle(typeAnalyzer, new ErrorLog())
245        val meet = (reduce(typeAnalyzer.meet(first._1._2, second._1._2), exclusionOracle),
246                    reduce(typeAnalyzer.meet(first._1._3, second._1._3), exclusionOracle))
247        for ( f <- set ; if ! result )
248            if ( subtype(f._1._2, meet._1) &&
249                 subtype(meet._1, f._1._2) &&
250                 subtype(meet._2, f._1._3) &&
251                 subtype(f._1._3, meet._2))
252                result = true
253        typeAnalyzer = oldTypeAnalyzer
254        result
255    }
256
257    /* If "t" is an intersection type of "elements",
258     *  - if there are tuple types and non-tuple types in "elements"
259     *    return BottomType
260     *  - if "elements" are all non-tuple types
261     *    check comprises clauses
262     *  - if "elements" are all tuple types
263     *    simplify "t"
264     */
265    private def reduce(t: Type, exclusionOracle: ExclusionOracle): Type = t match {
266        case SIntersectionType(info, elements) =>
267            val (tuples, nots) = elements.partition(NodeUtil.isTupleType)
268            if ( ! tuples.isEmpty && ! nots.isEmpty ) NodeFactory.makeBottomType(info)
269            else if ( tuples.isEmpty ) {
270                // If all the "elements" have comprises clauses
271                // and they all include type "M", then "M" is the meet.
272                existComprisedMeet(elements, exclusionOracle) match {
273                    case Some(m) => m
274                    case _ => t
275                }
276            } else {
277                val size = NodeUtil.getTupleTypeSize(tuples.head)
278                if ( tuples.forall(ty => NodeUtil.getTupleTypeSize(ty) == size &&
279                                         ! NodeUtil.hasVarargs(ty) &&
280                                         ! NodeUtil.hasKeywords(ty)) ) {
281                    var elems = List[Type]()
282                    var i = 0
283                    while ( i < size ) {
284                        elems = elems ::: List(typeAnalyzer.meet(toJavaList(tuples.map(ty => NodeUtil.getTupleTypeElem(ty, i)))))
285                        i += 1
286                    }
287                    val mt = NodeFactory.makeTupleType(NodeUtil.getSpan(t), toJavaList(elems))
288                    NodeFactory.makeIntersectionType(info, toJavaList(mt :: tuples))
289                } else t
290            }
291        case _ => t
292    }
293
294    /* If all the "types" have comprises clauses
295     * and they all include types "comprised",
296     * then if "comprised" = {"M"} and all the others are exclusive
297     *      then "M" is the meet
298     */
299    private def existComprisedMeet(types: List[Type],
300                                   exclusionOracle: ExclusionOracle): Option[TraitType] = {
301      var allComprises = List[(Id, List[TraitType])]()
302      var meets: List[TraitType] = null
303      for ( t <- types ) {
304        if ( t.isInstanceOf[TraitType] ) {
305          val name = t.asInstanceOf[TraitType].getName
306          STypesUtil.getTypes(name, globalEnv, compilation_unit) match {
307            case ti:ProperTraitIndex =>
308              var comprises = List[TraitType]()
309              for ( s <- toSet(ti.comprisesTypes) ) comprises = s::comprises
310              allComprises = (name, comprises)::allComprises
311              if ( ! comprises.isEmpty ) {
312                if ( meets == null ) meets = comprises
313                else meets = meets.intersect(comprises)
314              } else return None // no comprises clause
315            case _ => return None // not a trait
316          }
317        } else return None
318      }
319      if ( meets.length == 1 ) {
320        val meet = meets.head
321        def nonExclusive(pair: (List[TraitType], List[TraitType])): Boolean = {
322          for ( first <- pair._1 - meet ) {
323            for ( second <- pair._2 - meet ) {
324              if ( typeAnalyzer.equivalent(first, second).isFalse &&
325                   ! exclusionOracle.excludes(first, second) )
326                return true
327            }
328          }
329          false
330        }
331        for ( first <- allComprises ) {
332          for ( second <- allComprises ;
333                if ! first._1.getText.equals(second._1.getText) ) {
334            if ( nonExclusive(first._2, second._2) ) return None
335          }
336        }
337        Some(meet)
338      } else  None
339    }
340
341    /* Returns the set of overloaded function declarations
342     * covering the given set of the overloaded declarations.
343     * Invariant: set.size > 1
344     */
345    def coverOverloading(set: Set[JavaFunction]) = {
346        var result = Set[JavaFunction]()
347        for ( f <- set ) { if ( ! coveredBy(f, result) ) result = result + f }
348        result = result.filter(f => f match { case DeclaredFunction(_) => true
349                                              case _ => false } )
350        result.map(f => f match { case DeclaredFunction(fd) => fd })
351    }
352
353    private def coveredBy(f: JavaFunction, set: Set[JavaFunction]): Boolean = {
354        var result = false
355        for ( g <- set ; if ! result ) { if ( coveredBy(f, g) ) result = true }
356        result
357    }
358
359    /* Whether the signature of f is covered by the signature of g */
360    private def coveredBy(f: JavaFunction, g: JavaFunction): Boolean = {
361        val staticParameters = new ArrayList[StaticParam]()
362        // Add static parameters of "f"
363        staticParameters.addAll(f.staticParameters)
364        // If "f" is a functional method,
365        // add static parameters of "f"'s enclosing trait or object
366        if ( NodeUtil.isFunctionalMethod(f) ) {
367            val ind = typeAnalyzer.traitTable.typeCons(NodeUtil.getDeclaringTrait(f))
368            if ( ind.isSome && NodeUtil.isTraitOrObject(ind.unwrap) ) {
369                staticParameters.addAll( NodeUtil.getStaticParameters(ind.unwrap) )
370            }
371        }
372        // If "g" is a functional method,
373        // add static parameters of "g"'s enclosing trait or object
374        if ( NodeUtil.isFunctionalMethod(g) ) {
375            val ind = typeAnalyzer.traitTable.typeCons(NodeUtil.getDeclaringTrait(g))
376            if ( ind.isSome && NodeUtil.isTraitOrObject(ind.unwrap) ) {
377                staticParameters.addAll( NodeUtil.getStaticParameters(ind.unwrap) )
378            }
379        }
380        // Add static parameters of "g"
381        for ( s <- toList(g.staticParameters) ) staticParameters.add(s)
382        // Extend the type analyzer with the collected static parameters
383        val oldTypeAnalyzer = typeAnalyzer
384        // Whether "g"'s parameter type is a subtype of "f"'s parameter type
385        // and "f"'s return type is a subtype of "g"'s return type
386        typeAnalyzer = typeAnalyzer.extend(staticParameters, none[WhereClause])
387        val result = subtype(f, g)
388        typeAnalyzer = oldTypeAnalyzer
389        result
390    }
391
392    def isDeclaredName(f: IdOrOpOrAnonymousName) = f match {
393        case SId(_,_,str) => IdentifierUtil.validId(str)
394        case SOp(_,_,str,_,_) => NodeUtil.validOp(str)
395        case _ => false
396    }
397
398    def isDeclaredFunctional(f: JavaFunctional) = f match {
399        case DeclaredFunction(fd) => true
400        case DeclaredMethod(_,_) => true
401        case _ => false
402    }
403
404    private def mergeSpan(sub_type: ((JavaList[StaticParam],Type,Type),Span),
405                          super_type: ((JavaList[StaticParam],Type,Type),Span)): String =
406        mergeSpan(sub_type._2, super_type._2)
407
408    private def mergeSpan(first: Span, second: Span): String =
409        if (first.toString < second.toString)
410            first.toString + ":\n" + second.toString
411        else
412            second.toString + ":\n" + first.toString
413
414    private def toString(ty: ((JavaList[StaticParam],Type,Type), Span)): String =
415        ty._1._2 + " -> " + ty._1._3 + " @ " + ty._2
416
417    /* Returns the type of the given list of parameters. */
418    private def paramsToType(params: JavaList[Param], span: Span): Type =
419        params.size match {
420            case 0 => NodeFactory.makeVoidType(span)
421            case 1 => paramToType(params.get(0))
422            case _ =>
423            NodeFactory.makeTupleType(NodeUtil.spanAll(params),
424                                      Lists.toJavaList(Lists.toList(params).map(p => paramToType(p))))
425        }
426
427    /* Returns the type of the given parameter. */
428    private def paramToType(param: Param): Type =
429        toOption(param.getIdType) match {
430            case Some(ty) => ty
431            case _ =>
432                toOption(param.getVarargsType) match {
433                    case Some(ty) => ty
434                    case _ =>
435                        val span = NodeUtil.getSpan(param)
436                        error(span.toString,
437                              "Type checking couldn't infer the type of " + param)
438                        NodeFactory.makeVoidType(span)
439                }
440        }
441
442    private def error(loc: String, msg: String) =
443        errors = errors ::: List(TypeError.make(msg, loc.toString))
444
445    /* Returns true if any of the concrete method declarations in "decls"
446       implements the abstract method declaration "d".
447     */
448    private def implement(d: FnDecl, decls: List[Decl]): Boolean =
449        decls.exists( (decl: Decl) => decl match {
450                      case fd@SFnDecl(_,_,_,_,_) => implement(d, fd)
451                      case _ => false
452                    } )
453
454    /* Returns true if the concrete method declaration "decl"
455       implements the abstract method declaration "d".
456     */
457    private def implement(d: FnDecl, decl: FnDecl): Boolean =
458        NodeUtil.getName(d).asInstanceOf[IdOrOp].getText.equals(NodeUtil.getName(decl).asInstanceOf[IdOrOp].getText) &&
459        NodeUtil.getMods(d).containsAll(NodeUtil.getMods(decl)) &&
460        ( typeAnalyzer.equivalent(NodeUtil.getParamType(d),
461                                  NodeUtil.getParamType(decl)).isTrue ||
462          implement(toList(NodeUtil.getParams(d)), toList(NodeUtil.getParams(decl))) ) &&
463        typeAnalyzer.equivalent(NodeUtil.getReturnType(d).unwrap,
464                                NodeUtil.getReturnType(decl).unwrap).isTrue
465
466    /* Returns true if the parameters of the concrete method declaration "params"
467       `implements' the parameters of the abstract method declaration "ps".
468     */
469    private def implement(ps: List[Param], params: List[Param]): Boolean =
470        ! ps.zip(params).exists( (p:(Param,Param)) =>
471                                 typeAnalyzer.equivalent(NodeUtil.getParamType(p._1),
472                                                         NodeUtil.getParamType(p._2)).isFalse &&
473                                 ! p._1.getName.getText.equals("self") )
474
475    private def inheritedAbstractMethods(extended_traits: List[TraitTypeWhere]) =
476        inheritedAbstractMethodsHelper(new HierarchyHistory(), extended_traits)
477
478    private def inheritedAbstractMethodsHelper(hist: HierarchyHistory,
479                                               extended_traits: List[TraitTypeWhere]):
480                                              Map[IdOrOp, Set[FnDecl]] = {
481        var h = hist
482        var map = new HashMap[IdOrOp, Set[FnDecl]]()
483        for ( trait_ <- extended_traits ; if ! h.hasExplored(trait_.getBaseType) ) {
484            trait_.getBaseType match {
485                case ty@STraitType(info, name, args, params) =>
486                    h = h.explore(ty)
487                    val tci = typeAnalyzer.traitTable.typeCons(name)
488                    if ( tci.isSome && tci.unwrap.isInstanceOf[TraitIndex] ) {
489                        val ti = tci.unwrap.asInstanceOf[TraitIndex]
490                        map.put(name, collectAbstractMethods(name, toList(NodeUtil.getDecls(ti.ast))))
491                        map ++= inheritedAbstractMethodsHelper(h, toList(ti.extendsTypes))
492                    } else error(NodeUtil.getSpan(trait_).toString,
493                                 "Trait types are expected in an extends clause but found "
494                                 + ty.toStringVerbose + "\n" + tci.getClass)
495                case SAnyType(_) =>
496                case ty => error(NodeUtil.getSpan(trait_).toString,
497                                 "Trait types are expected in an extends clause but found "
498                                 + ty.toStringVerbose)
499            }
500        }
501        map
502    }
503
504    private def collectAbstractMethods(name: IdOrOp, decls: List[Decl]) = {
505        val set = new HashSet[FnDecl]
506        decls.foreach( (d: Decl) => d match {
507                       case fd@SFnDecl(_,SFnHeader(_,mods,_,_,_,_,_,_),_,body,_) =>
508                           if ( compilation_unit.typeConses.keySet.contains(name) ) {
509                               if ( ! body.isDefined ) set += fd
510                           } else if ( mods.isAbstract ) set += fd
511                       case _ => })
512        set
513    }
514}
Note: See TracBrowser for help on using the browser.