root/trunk/ProjectFortress/src/com/sun/fortress/compiler/typechecker/TypeChecker.java @ 3272

Revision 3272, 253.8 KB (checked in by jmaessen, 11 months ago)

Some more minor cleanup.

Line 
1/*******************************************************************************
2    Copyright 2008 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.compiler.typechecker;
19
20
21import static com.sun.fortress.exceptions.InterpreterBug.bug;
22import static com.sun.fortress.exceptions.StaticError.errorMsg;
23import static edu.rice.cs.plt.tuple.Option.none;
24import static edu.rice.cs.plt.tuple.Option.some;
25import static edu.rice.cs.plt.tuple.Option.wrap;
26import static edu.rice.cs.plt.iter.IterUtil.tripleFirsts;
27import static edu.rice.cs.plt.iter.IterUtil.tripleSeconds;
28import static edu.rice.cs.plt.iter.IterUtil.tripleThirds;
29import static edu.rice.cs.plt.iter.IterUtil.pairFirsts;
30import static edu.rice.cs.plt.iter.IterUtil.pairSeconds;
31import static edu.rice.cs.plt.collect.CollectUtil.makeList;
32
33import java.math.BigInteger;
34import java.util.ArrayList;
35import java.util.Collections;
36import java.util.HashMap;
37import java.util.HashSet;
38import java.util.Iterator;
39import java.util.LinkedList;
40import java.util.List;
41import java.util.ListIterator;
42import java.util.Map;
43import java.util.Set;
44
45import com.sun.fortress.compiler.IndexBuilder;
46import com.sun.fortress.compiler.Types;
47import com.sun.fortress.compiler.disambiguator.ExprDisambiguator.HierarchyHistory;
48import com.sun.fortress.compiler.index.CompilationUnitIndex;
49import com.sun.fortress.compiler.index.DeclaredVariable;
50import com.sun.fortress.compiler.index.Functional;
51import com.sun.fortress.compiler.index.FunctionalMethod;
52import com.sun.fortress.compiler.index.Method;
53import com.sun.fortress.compiler.index.ObjectTraitIndex;
54import com.sun.fortress.compiler.index.ParamVariable;
55import com.sun.fortress.compiler.index.ProperTraitIndex;
56import com.sun.fortress.compiler.index.TraitIndex;
57import com.sun.fortress.compiler.index.TypeConsIndex;
58import com.sun.fortress.compiler.index.Variable;
59import com.sun.fortress.compiler.typechecker.TypeAnalyzer.SubtypeHistory;
60import com.sun.fortress.compiler.typechecker.TypeEnv.BindingLookup;
61import com.sun.fortress.compiler.typechecker.TypesUtil.ArgList;
62import com.sun.fortress.exceptions.InterpreterBug;
63import com.sun.fortress.exceptions.StaticError;
64import com.sun.fortress.exceptions.TypeError;
65import com.sun.fortress.nodes.*;
66import com.sun.fortress.nodes_util.ExprFactory;
67import com.sun.fortress.nodes_util.NodeFactory;
68import com.sun.fortress.nodes_util.NodeUtil;
69import com.sun.fortress.nodes_util.OprUtil;
70import com.sun.fortress.nodes_util.Span;
71import com.sun.fortress.parser_util.FortressUtil;
72import com.sun.fortress.useful.NI;
73import com.sun.fortress.useful.Useful;
74
75import edu.rice.cs.plt.collect.CollectUtil;
76import edu.rice.cs.plt.collect.ConsList;
77import edu.rice.cs.plt.collect.EmptyRelation;
78import edu.rice.cs.plt.collect.IndexedRelation;
79import edu.rice.cs.plt.collect.Relation;
80import edu.rice.cs.plt.collect.UnionRelation;
81import edu.rice.cs.plt.iter.IterUtil;
82import edu.rice.cs.plt.lambda.Box;
83import edu.rice.cs.plt.lambda.Lambda;
84import edu.rice.cs.plt.lambda.Lambda2;
85import edu.rice.cs.plt.tuple.Option;
86import edu.rice.cs.plt.tuple.Pair;
87import edu.rice.cs.plt.tuple.Triple;
88
89/**
90 * The fortress typechecker.<br>
91 *
92 * Notes/Invariants:<br>
93 * - The typechecker can either be in inference mode ({@code postInference==false}) or
94 *   post-inference mode ({@code postInference == true}). In post-inference mode, all of the
95 *   cases of the typechecker that create open constraints must close those constraints immediately.<br>
96 */
97public class TypeChecker extends NodeDepthFirstVisitor<TypeCheckerResult> {
98
99    private static boolean isPostInference(FunctionalRef opref) {
100        return opref.getOverloadings().isSome();
101    }
102
103    private static TypeChecker addSelf(Option<APIName> api_, Id name, TypeChecker newChecker, List<StaticParam> static_params){
104        Id type_name;
105        if( api_.isSome() )
106            type_name = NodeFactory.makeId(api_.unwrap(), name);
107        else
108            type_name = name;
109
110        TraitType self_type = NodeFactory.makeTraitType(type_name,TypeEnv.staticParamsToArgs(static_params));
111        return newChecker.extend(Collections.singletonList(NodeFactory.makeLValue("self", self_type)));
112    }
113    /** Returns an error result if item is NOT an ExprMI */
114    private static Option<TypeCheckerResult> expectExprMI(MathItem item) {
115        boolean is_expr_item = isExprMI(item);
116        if( !is_expr_item ) {
117            String err = "Item at this location must be an expression, not an operator.";
118            TypeCheckerResult err_result = new TypeCheckerResult(item,TypeError.make(err, item));
119            return Option.some(err_result);
120        }
121        else {
122            return Option.none();
123        }
124    }
125    /** Returns an error result if item is NOT a ParenthesisDelimitedMI */
126    private static Option<TypeCheckerResult> expectParenedExprItem(MathItem item) {
127        boolean is_parened = isParenedExprItem(item);
128        if( !is_parened ) {
129            String err = "Argument to function must be parenthesized.";
130            TypeCheckerResult err_result = new TypeCheckerResult(item, TypeError.make(err, item));
131            return Option.some(err_result);
132        }
133        else {
134            return Option.none();
135        }
136    }
137    static private Type getTypeOfLValue(LValue lval) {
138            return lval.getIdType().unwrap();
139    }
140    private static boolean isExprMI(MathItem item) {
141        return item.accept(new NodeDepthFirstVisitor<Boolean>(){
142            @Override public Boolean defaultCase(Node that) { return false; }
143            @Override public Boolean forNonParenthesisDelimitedMI(NonParenthesisDelimitedMI that) { return true; }
144            @Override public Boolean forParenthesisDelimitedMI(ParenthesisDelimitedMI that) { return true; }
145        });
146    }
147    private static boolean isParenedExprItem(MathItem item) {
148        return item.accept(new NodeDepthFirstVisitor<Boolean>(){
149            @Override public Boolean defaultCase(Node that) { return false; }
150            @Override public Boolean forParenthesisDelimitedMI(ParenthesisDelimitedMI that) { return true; }
151        });
152    }
153
154    private static Type typeFromLValues(Span span, List<LValue> bindings) {
155        List<Type> results = new ArrayList<Type>();
156
157        for (LValue binding: bindings) {
158            if (binding.getIdType().isSome()) {
159                results.add(binding.getIdType().unwrap());
160            } else
161                bug(binding, "Missing type.");
162        }
163        return NodeFactory.makeTupleType(span, results);
164    }
165
166    private final CompilationUnitIndex compilationUnit;
167
168    private final Map<Id, Option<Set<Type>>> labelExitTypes; // Note: this is mutable state.
169
170    private final boolean postInference; // Is this pass of the typechecker a post-inference pass?
171
172    private final TypeAnalyzer subtypeChecker;
173
174    private TraitTable table;
175
176    private TypeEnv typeEnv;
177
178    // An informative constraint passed down the ast
179    private final ConstraintFormula downwardConstraint;
180
181    public TypeChecker(TraitTable table,
182            TypeEnv typeEnv,
183            CompilationUnitIndex compilationUnit,
184            boolean postInference) {
185        this.table = table;
186        this.typeEnv = typeEnv;
187        this.compilationUnit = compilationUnit;
188        this.subtypeChecker = TypeAnalyzer.make(table);
189        this.labelExitTypes = new HashMap<Id, Option<Set<Type>>>();
190        this.postInference = postInference;
191        this.downwardConstraint = ConstraintFormula.TRUE;
192    }
193
194    private TypeChecker(TraitTable table,
195            TypeEnv typeEnv,
196            CompilationUnitIndex compilationUnit,
197            TypeAnalyzer subtypeChecker,
198            Map<Id, Option<Set<Type>>> labelExitTypes,
199            boolean postInference,
200            ConstraintFormula downwardsConstraint) {
201        this.table = table;
202        this.typeEnv = typeEnv;
203        this.compilationUnit = compilationUnit;
204        this.subtypeChecker = subtypeChecker;
205        this.labelExitTypes = labelExitTypes;
206        this.postInference = postInference;
207        this.downwardConstraint = downwardsConstraint;
208    }
209
210    /**
211     * Checks whether all the expressions in the block have type void.
212     * List of Exprs passed in so that error messages can be more accurate.
213     */
214    private List<TypeCheckerResult> allVoidButLast(List<TypeCheckerResult> results, List<Expr> block){
215        // every other expression except for the last must be void
216        List<TypeCheckerResult> all_results = new ArrayList<TypeCheckerResult>();
217        String void_err = "All expressions except the last in a block must have () type.";
218        for( int i=0; i<results.size()-1; i++ ) {
219            TypeCheckerResult r_i = results.get(i);
220            if( r_i.type().isSome() ) {
221                all_results.add(this.checkSubtype(r_i.type().unwrap(),
222                        Types.VOID, block.get(i), void_err));
223            }
224        }
225        return all_results;
226    }
227
228    /**
229     * For method calls, return a constraint formula matching the parameters to
230     * the type of the argument. Assumes that this is being used for a method call,
231     * because it makes some assumptions about the type of the argument.
232     */
233    private ConstraintFormula argsMatchParams(List<Param> params, Type arg_type) {
234        final int arg_size;
235        if( arg_type instanceof TupleType )
236            arg_size = ((TupleType)arg_type).getElements().size();
237        else
238            arg_size = 1;
239
240        //handle domain
241        Type domain_type;
242        if(!params.isEmpty()) {
243
244            // Regular parameters & var args
245            List<Type> param_types =
246                IterUtil.fold(params, new LinkedList<Type>(), new Lambda2<LinkedList<Type>,Param,LinkedList<Type>>(){
247                    // How many types have we accumulated thus far?
248                    int typeCount = 0;
249
250                    public LinkedList<Type> value(LinkedList<Type> arg0, Param arg1) {
251                                            if( ! NodeUtil.isVarargsParam(arg1) ) {
252                            typeCount++;
253                            arg0.add(arg1.getIdType().unwrap());
254                            return arg0;
255                        }
256                                            else { // a varargs param, add until the sizes are equal
257                            int to_add = arg_size - typeCount;
258                            while( to_add > 0 ) {
259                                                            arg0.add(arg1.getVarargsType().unwrap());
260                                to_add--;
261                            }
262                            return arg0;
263                        }
264                    }});
265
266            //handle defaults (nyi)
267            if( NodeUtil.isVarargsParam(params.get(params.size()-1))
268                           && params.get(params.size()-1).getDefaultExpr().isSome()){
269                return NI.nyi();
270            }
271
272            domain_type = NodeFactory.makeTupleType(NodeUtil.getSpan(arg_type),param_types);
273        }
274        else {
275            //is void
276            domain_type = Types.VOID;
277        }
278        return this.subtypeChecker.subtype(arg_type, domain_type);
279    }
280
281    /**
282     * Return a failing TypecheckerResult with error message if the given type {@code t} is
283     * not a trait.
284     */
285    private TypeCheckerResult assertTrait(BaseType t, Node ast, String msg, Node error_loc) {
286        TypeCheckerResult err_result = new TypeCheckerResult(ast, TypeError.make(msg, error_loc));
287
288        if( !(t instanceof TraitType) )
289            return err_result;
290
291        Option<TypeConsIndex> type_cons_ = this.table.typeCons(((TraitType)t).getName());
292        if( type_cons_.isSome() && type_cons_.unwrap() instanceof ProperTraitIndex ) {
293            return new TypeCheckerResult(ast);
294        }
295        else {
296            return err_result;
297        }
298    }
299
300    // Checks the chunk given, and returns the result and a new expression.
301    // Requires that all TypeCheckerResults passed in actually have a type.
302    // Must be called on non-empty list.
303    private Pair<TypeCheckerResult,Expr> checkChunk(List<Pair<TypeCheckerResult,Expr>> chunk, FunctionalRef infix_juxt) {
304        assert(!chunk.isEmpty());
305        // The non-functions in each chunk, if any, are replaced by a single element consisting of the non-functions grouped
306        // left-associatively into binary juxtapositions.
307        Option<Expr> last_non_fn = Option.none();
308        List<Expr> fns = new LinkedList<Expr>();
309        for( Pair<TypeCheckerResult,Expr> chunk_element : chunk ) {
310            if( !TypesUtil.isArrows(chunk_element.first().type().unwrap()) ) {
311                // If we've already seen an expr, created a binary juxt with previous
312                if( last_non_fn.isNone() )
313                    last_non_fn = Option.some(chunk_element.second());
314                else
315                    last_non_fn = Option.<Expr>some(ExprFactory.makeOpExpr(infix_juxt,
316                                                                                               last_non_fn.unwrap(),
317                                                                                               chunk_element.second()));
318            }
319            else {
320                fns.add(chunk_element.second());
321            }
322        }
323        // What remains in each chunk is then grouped right-associatively, as fn applications.
324        Option<Expr> result_expr = last_non_fn;
325        Collections.reverse(fns); // reverse so right assoc becomes left assoc
326        for( Expr fn : fns ) {
327            if( result_expr.isNone() )
328                result_expr = Option.some(fn);
329            else
330                result_expr = Option.<Expr>some(ExprFactory.make_RewriteFnApp(fn, result_expr.unwrap()));
331        }
332        // We are done. result_expr must be some or this method wasn't implemented correctly.
333        TypeCheckerResult result = TypeCheckerResult.compose(result_expr.unwrap(), subtypeChecker,
334                CollectUtil.makeList(IterUtil.pairFirsts(chunk)));
335        return Pair.make(result, result_expr.unwrap());
336    }
337
338    /**
339     * Check the subtype relation for the given types.  If subtype <: supertype, then a TypeCheckerResult
340     * for the given node and corresponding type constraints will be returned.  Otherwise, a TypeCheckerResult
341     * for the given node with a generic error message will be returned.
342     */
343    private TypeCheckerResult checkSubtype(Type subtype, Type supertype, Node ast) {
344        return checkSubtype(subtype,
345                supertype,
346                ast,
347                errorMsg("Expected expression of type ", supertype, " ",
348                        "but was type ", subtype));
349    }
350
351    /**
352     * Check the subtype relation for the given types.  If subtype <: supertype, then a TypeCheckerResult
353     * for the given node and corresponding type constraints will be returned.  Otherwise, a TypeCheckerResult
354     * for the given node with the a TypeError and the given error message will be returned.
355     */
356    private TypeCheckerResult checkSubtype(Type subtype, Type supertype, Node ast, String error) {
357        ConstraintFormula constraint = subtypeChecker.subtype(subtype, supertype);
358        if( !constraint.isSatisfiable() ) {
359            // note that if it's satisfiable, it could still be later found to not be
360            return new TypeCheckerResult(ast, TypeError.make(error, ast));
361        } else {
362            return new TypeCheckerResult(ast, constraint);
363        }
364    }
365
366    /**
367     * Check the subtype relation for the given types.  If subtype <: supertype, then a TypeCheckerResult
368     * for the given node and corresponding type constraints will be returned, and the type of this node
369     * will be the given type resultType.  Otherwise, a TypeCheckerResult
370     * for the given node with the a TypeError and the given error message will be returned.
371     */
372    private TypeCheckerResult checkSubtype(Type subtype, Type supertype, Node ast, Type resultType, String error) {
373        ConstraintFormula constraint = subtypeChecker.subtype(subtype, supertype);
374        if( !constraint.isSatisfiable() ) {
375            // note that if it's satisfiable, it could still be later found to not be
376            return new TypeCheckerResult(ast, resultType, TypeError.make(error, ast));
377        } else {
378            return new TypeCheckerResult(ast, resultType, constraint);
379        }
380    }
381
382
383    @Override
384    public TypeCheckerResult defaultCase(Node that) {
385        return new TypeCheckerResult(that);
386    }
387
388    private List<TraitIndex> expectTraitIndeces(List<TraitType> traits) {
389        List<TraitIndex> result = new ArrayList<TraitIndex>(traits.size());
390        for( TraitType type : traits ) {
391            result.add(expectTraitIndex(type));
392        }
393        return result;
394    }
395
396    /**
397     * Look up the index of the given type, expecting it to exist. If it
398     * does not, that represents a bug.
399     */
400    private TraitIndex expectTraitIndex(TraitType trait) {
401        Option<TypeConsIndex> tc_ = table.typeCons(trait.getName());
402        if( tc_.isNone() || !(tc_.unwrap() instanceof TraitIndex) ) {
403            return bug("This should never be anything but a legitimate trait index.");
404        }
405        else {
406            return (TraitIndex)tc_.unwrap();
407        }
408    }
409
410    /** Check for ^ followed by ^ or ^ followed by [], both static errors. */
411    private Option<TypeCheckerResult> exponentiationStaticCheck(Node ast, List<MathItem> items) {
412
413        //Visitor checks for two exponentiations or an exponentiation and a subscript in a row
414        NodeDepthFirstVisitor<TypeCheckerResult> static_error = new NodeDepthFirstVisitor<TypeCheckerResult>() {
415            Option<Node> exponent = Option.none();
416            @Override
417            public TypeCheckerResult defaultCase(Node that) {
418                exponent=Option.none();
419                return new TypeCheckerResult(that);
420            }
421
422            @Override
423            public TypeCheckerResult forExponentiationMI(ExponentiationMI that) {
424                if(exponent.isNone()){
425                    exponent=Option.<Node>some(that);
426                    return new TypeCheckerResult(that);
427                }
428                else{
429                    String err_message = "Two consecutive ^s";
430                    StaticError err=TypeError.make(err_message,
431                                                                       new Span(NodeUtil.getSpan((ASTNode)exponent.unwrap()),
432                                                                                NodeUtil.getSpan(that)).toString());
433                    return new TypeCheckerResult(that,err);
434                }
435            }
436
437            @Override
438            public TypeCheckerResult forSubscriptingMI(SubscriptingMI that) {
439                if(exponent.isNone()){
440                    return new TypeCheckerResult(that);
441                }
442                else{
443                    String err_message = "Exponentiation followed by subscripting is illegal";
444                    StaticError err=TypeError.make(err_message,
445                                                                       new Span(NodeUtil.getSpan((ASTNode)exponent.unwrap()),
446                                                                                NodeUtil.getSpan(that)).toString());
447                    exponent = Option.none();
448                    return new TypeCheckerResult(that,err);
449                }
450            }
451        };
452        List<TypeCheckerResult> static_errors = static_error.recurOnListOfMathItem(items);
453        TypeCheckerResult static_error_result = TypeCheckerResult.compose(ast, subtypeChecker, static_errors);
454        if(!static_error_result.isSuccessful()){
455            return Option.some(static_error_result);
456        }
457        else {
458            return Option.none();
459        }
460    }
461
462    private TypeChecker extend(List<LValue> bindings) {
463        return new TypeChecker(table,
464                typeEnv.extendWithLValues(bindings),
465                compilationUnit,
466                subtypeChecker,
467                labelExitTypes,
468                postInference,
469                downwardConstraint);
470    }
471
472    private TypeChecker extend(List<StaticParam> newStaticParams, List<Param> newParams, Option<WhereClause> whereClause) {
473        return new TypeChecker(table,
474                typeEnv.extendWithParams(newParams).extendWithStaticParams(newStaticParams),
475                compilationUnit,
476                subtypeChecker.extend(newStaticParams, whereClause),
477                labelExitTypes,
478                postInference,
479                downwardConstraint);
480    }
481
482    private TypeChecker extend(List<StaticParam> newStaticParams, Option<List<Param>> newParams, Option<WhereClause> whereClause) {
483        return new TypeChecker(table,
484                typeEnv.extend(newParams).extendWithStaticParams(newStaticParams),
485                compilationUnit,
486                subtypeChecker.extend(newStaticParams, whereClause),
487                labelExitTypes,
488                postInference,
489                downwardConstraint);
490    }
491
492    private TypeChecker extend(List<StaticParam> newStaticParams, Option<WhereClause> whereClause) {
493        return new TypeChecker(table,
494                typeEnv.extendWithStaticParams(newStaticParams),
495                compilationUnit,
496                subtypeChecker.extend(newStaticParams, whereClause),
497                labelExitTypes,
498                postInference,
499                downwardConstraint);
500    }
501
502    private TypeChecker extend(LocalVarDecl decl) {
503        return new TypeChecker(table,
504                typeEnv.extend(decl),
505                compilationUnit,
506                subtypeChecker,
507                labelExitTypes,
508                postInference,
509                downwardConstraint);
510    }
511
512    private TypeChecker extend(Param newParam) {
513        return new TypeChecker(table,
514                typeEnv.extend(newParam),
515                compilationUnit,
516                subtypeChecker,
517                labelExitTypes,
518                postInference,
519                downwardConstraint);
520    }
521
522    private TypeChecker extend(Option<WhereClause> whereClause) {
523        return new TypeChecker(table,
524                typeEnv,
525                compilationUnit,
526                subtypeChecker.extend(Collections.<StaticParam>emptyList(), whereClause),
527                labelExitTypes,
528                postInference,
529                downwardConstraint);
530    }
531
532    public TypeChecker extendWithFnDecls(Relation<IdOrOpOrAnonymousName, FnDecl> fns) {
533        return new TypeChecker(table,
534                typeEnv.extendWithFnDecls(fns),
535                compilationUnit,
536                subtypeChecker,
537                labelExitTypes,
538                postInference,
539                downwardConstraint);
540    }
541
542    public TypeChecker extendWithFunctions(Relation<IdOrOpOrAnonymousName, FunctionalMethod> methods) {
543        return new TypeChecker(table,
544                typeEnv.extendWithFunctions(methods),
545                compilationUnit,
546                subtypeChecker,
547                labelExitTypes,
548                postInference,
549                downwardConstraint);
550    }
551
552    public TypeChecker extendWithMethods(Relation<IdOrOpOrAnonymousName, Method> methods) {
553        return new TypeChecker(table,
554                typeEnv.extendWithMethods(methods),
555                compilationUnit,
556                subtypeChecker,
557                labelExitTypes,
558                postInference,
559                downwardConstraint);
560    }
561
562    public TypeChecker extendWithout(Node declSite, Set<? extends IdOrOpOrAnonymousName> names) {
563        return new TypeChecker(table,
564                typeEnv.extendWithout(declSite, names),
565                compilationUnit,
566                subtypeChecker,
567                labelExitTypes,
568                postInference,
569                downwardConstraint);
570    }
571
572    public TypeChecker extendWithConstraints(Iterable<ConstraintFormula> constraints) {
573        constraints = IterUtil.compose(downwardConstraint, constraints);
574        ConstraintFormula new_constraint = ConstraintFormula.bigAnd(constraints,
575                subtypeChecker.new SubtypeHistory());
576        return new TypeChecker(table,
577                typeEnv,
578                compilationUnit,
579                subtypeChecker,
580                labelExitTypes,
581                postInference,
582                new_constraint);
583    }
584
585    private Option<Type> findFieldInTraitHierarchy(List<TraitType> supers, FieldRef that) {
586
587        List<TraitType> new_supers = new ArrayList<TraitType>();
588        for( TraitType my_super : supers ) {
589            TraitIndex index = expectTraitIndex(my_super);
590
591            final List<StaticParam> trait_static_params = index.staticParameters();
592            final List<StaticArg> trait_static_args = my_super.getArgs();
593
594            // Map to list of supertypes
595            for( TraitTypeWhere ttw : index.extendsTypes() ) {
596                Type t = ttw.getBaseType();
597                Type t_ = (Type)t.accept(new StaticTypeReplacer(trait_static_params, trait_static_args));
598                new_supers.addAll(traitTypesCallable(t_));
599            }
600
601            // check if trait has a getter
602            Map<Id,Method> getters=index.getters();
603            if(getters.containsKey(that.getField())) {
604                Method field=(Method)getters.get(that.getField()).instantiate(trait_static_params, trait_static_args);
605                return Option.some(field.getReturnType());
606            }
607            else {
608                //check if trait is an object
609                if(index instanceof ObjectTraitIndex) {
610                    //Check if object has field
611                    ObjectTraitIndex object_index=(ObjectTraitIndex)index;
612                    Map<Id,Variable> fields=object_index.fields();
613                    if(fields.containsKey(that.getField())){
614                        Variable field=fields.get(that.getField());
615
616                        if( field instanceof ParamVariable ) {
617                            ParamVariable param = (ParamVariable)field;
618                            Param field_node = param.ast();
619
620                            Type field_type =
621                                field_node.accept(new NodeAbstractVisitor<Type>() {
622                                    @Override
623                                    public Type forParam(Param that) {
624                                                                            if ( NodeUtil.isVarargsParam(that) )
625                                                                                return that.getIdType().unwrap();
626                                                                            else
627                                                                                return that.getVarargsType().unwrap();
628                                                                        }
629                                });
630
631                            return Option.some(field_type);
632                        }
633                        else if( field instanceof DeclaredVariable ) {
634                            DeclaredVariable var = (DeclaredVariable)field;
635                            LValue bind = var.ast();
636                            return Option.some(bind.getIdType().unwrap());
637                        }
638                        else {
639                            return bug("Field of an object should not be a Singleton Object." + field);
640                        }
641                    }
642                }
643                //error no such field
644
645            }
646        }
647
648        if(!new_supers.isEmpty() ) {
649            // recur
650            return this.findFieldInTraitHierarchy(new_supers, that);
651        }
652        else {
653            return Option.none();
654        }
655    }
656
657    // This method does a lot: Basically all of the hard work of finding an appropriate overloading, including looking
658    // in parent traits.
659    // TODO: This method is in need of a serious overhaul. It is way too similar to
660    // TypesUtil.applicationType...
661    private Pair<List<Method>,List<TypeCheckerResult>> findMethodsInTraitHierarchy(final IdOrOpOrAnonymousName method_name, List<TraitType> supers,
662            Type arg_type, List<StaticArg> in_static_args,
663            final Node that) {
664        final List<TypeCheckerResult> all_results= new ArrayList<TypeCheckerResult>();
665        List<Method> candidates=new ArrayList<Method>();
666        List<TraitType> new_supers=new ArrayList<TraitType>();
667        SubtypeHistory history = subtypeChecker.new SubtypeHistory();
668
669        for(TraitType type: supers) {
670            TraitIndex trait_index = expectTraitIndex(type);
671
672            final List<StaticArg> extended_type_args = type.getArgs();
673            final List<StaticParam> extended_type_params = trait_index.staticParameters();
674            // get all of the types this type extends. Note that because we can extend types with our own static args,
675            // we must visit the extended type with a static type replacer.
676            for( TraitTypeWhere ttw : trait_index.extendsTypes() ) {
677                Type t = (Type)ttw.getBaseType().accept(new StaticTypeReplacer(extended_type_params, extended_type_args));
678                new_supers.addAll(traitTypesCallable(t));
679            }
680
681            // Get methods with the right name:
682            // Add all dotted methods,
683            Set<Method> methods_with_name = trait_index.dottedMethods().matchFirst(method_name);
684
685            for( Method m : methods_with_name ) {
686                List<StaticArg> static_args = new ArrayList<StaticArg>(in_static_args);
687                // same number of static args
688                if( m.staticParameters().size() > 0 && in_static_args.size() == 0 ) {
689                    // we need to infer static arguments
690
691                    List<StaticArg> static_inference_params =
692                        CollectUtil.makeList(
693                                IterUtil.map(m.staticParameters(), new Lambda<StaticParam,StaticArg>(){
694                                    public StaticArg value(StaticParam arg0) {
695                                        // TODO if parameters are anything but TypeParam, we don't know
696                                        // how to infer it yet.
697                                        // Otherwise, we've got all static parameters
698                                                                            if( NodeUtil.isTypeParam(arg0) ){
699                                            Set<BaseType> bounds = CollectUtil.asSet(arg0.getExtendsClause());
700                                            Type ivt = NodeFactory.make_InferenceVarType(NodeUtil.getSpan(method_name));
701                                            ConstraintFormula constraint=TypeChecker.this.subtypeChecker.subtype(ivt, NodeFactory.makeIntersectionType(bounds));
702                                            all_results.add(new TypeCheckerResult(that, Option.<Type>none(), constraint));
703                                            return NodeFactory.makeTypeArg(NodeFactory.makeSpan(ivt), ivt);
704                                        }
705                                        else{
706                                            return NI.nyi();
707                                        }
708                                    }}));
709                    static_args = static_inference_params;
710                }
711                else if(m.staticParameters().size()!=static_args.size()) {
712                    // we don't need to infer, and they have different numbers of args
713                    continue;
714                }
715                // instantiate method params with method and type args
716                Functional im_maybe = m.instantiate(Useful.concat(m.staticParameters(), extended_type_params),
717                        Useful.concat(static_args, extended_type_args));
718                // we know this cast works, instantiate contract
719                Method im = (Method)im_maybe;
720                // Do they have the same number of parameters, or at least can they be matched.
721                ConstraintFormula mc = this.argsMatchParams(im.parameters(), arg_type);
722                // constraint & any relevent downward constraints satisfiable?
723                if(mc.and(downwardConstraint, history) .isSatisfiable()) {
724                    //add method to candidates
725                    candidates.add(im);
726                    all_results.add(new TypeCheckerResult(that,Option.<Type>none(),mc));
727                }
728
729            }
730        }
731        //If you have a super type check it for overloadings
732        if(!new_supers.isEmpty()){
733
734            Pair<List<Method>, List<TypeCheckerResult>> temp = findMethodsInTraitHierarchy(method_name, new_supers, arg_type, in_static_args, that);
735            candidates.addAll(temp.first());
736            all_results.addAll(all_results);
737        }
738        return Pair.make(candidates,all_results);
739    }
740
741    private TypeCheckerResult findSetterInTraitHierarchy(IdOrOpOrAnonymousName field_name, List<TraitType> supers,
742            Type arg_type, Node ast){
743        List<TraitType> new_supers = new ArrayList<TraitType>();
744        for( TraitType my_super : supers ) {
745            TraitIndex trait_index = expectTraitIndex(my_super);
746
747
748            // Map to list of supertypes
749            for( TraitTypeWhere ttw : trait_index.extendsTypes() ) {
750                new_supers.addAll(traitTypesCallable(ttw.getBaseType()));
751            }
752
753            // check if trait has a getter
754            Map<Id,Method> setters=trait_index.setters();
755            if(setters.containsKey(field_name)) {
756                Method field=setters.get(field_name);
757                ConstraintFormula works =  argsMatchParams(field.parameters(),arg_type);
758                if(!works.isSatisfiable()){
759                    String errmes = "Argument to setter has wrong type";
760                    StaticError err = TypeError.make(errmes, ast);
761                    return new TypeCheckerResult(ast,Option.<Type>none(),Collections.singletonList(err),works);
762                }
763                else{
764                    return new TypeCheckerResult(ast,works);
765                }
766            }
767            else {
768                // we used to check for a field but we don't think that's right.
769                //check if trait is an object
770                //                 if(trait_index instanceof ObjectTraitIndex){
771                //                 //Check if object has field
772                //                 ObjectTraitIndex object_index=(ObjectTraitIndex)trait_index;
773                //                 Map<Id,Variable> fields=object_index.fields();
774                //                 if(fields.containsKey(field_name)){
775                //                 Variable field=fields.get(field_name);
776                //                 Option<BindingLookup> type=this.typeEnv.binding(field_name);
777                //                 return this.checkSubtype(arg_type,type.unwrap().getType().unwrap(),ast, "Argument to field has wrong type");
778                //                 }
779                //                 }
780                //error no such field
781            }
782            //error receiver not a trait
783        }
784
785        if(!new_supers.isEmpty() ) {
786            // recur
787            return this.findSetterInTraitHierarchy(field_name, new_supers, arg_type, ast );
788        }
789        else {
790            String errmes = "Setter for field "+ field_name +" not found.";
791            return new TypeCheckerResult(ast,TypeError.make(errmes, ast));
792        }
793
794    }
795
796    /**
797     * Given a list of functional refs and the argument to which they are to be applied, find the FunctionalRef
798     * whose type is the statically most applicable to the given arg.
799     */
800    private TypeCheckerResult findStaticallyMostApplicableFn(List<? extends Pair<? extends FunctionalRef, Type>> fns, Type arg_type, FunctionalRef ref, IdOrOp name) {
801        final List<Pair<FunctionalRef, Type>> pruned_fns = new ArrayList<Pair<FunctionalRef, Type>>(fns.size());
802        // prune down the list of fns to just the ones that apply
803        for( Pair<? extends FunctionalRef, Type> fn : fns ) {
804            // If applicationType indicates the method applies, we keep it in pruned_fns
805            Type fn_type = fn.second();
806            Option<?> applies = TypesUtil.applicationType(subtypeChecker, fn_type, new ArgList(arg_type), downwardConstraint);
807            if( applies.isSome() )
808                pruned_fns.add(Pair.<FunctionalRef, Type>make(fn.first(), fn_type));
809        }
810
811        if( pruned_fns.isEmpty() ){
812            //No statically most applicable Fn
813            String err = "No applicable overloading of " + name + " exists for argument of type " + arg_type.toString();
814            TypeCheckerResult err_result = new TypeCheckerResult(ref, TypeError.make(err, ref));
815            return TypeCheckerResult.compose(ref, subtypeChecker, err_result);
816        }
817        // then, find the one with out of that group with the most specific arguments.
818        final Box<Option<Pair<FunctionalRef, Type>>> most_applicable = new Box<Option<Pair<FunctionalRef, Type>>>(){
819            private Option<Pair<FunctionalRef, Type>> ma = none();
820            public void set(Option<Pair<FunctionalRef, Type>> arg0) { ma = arg0; }
821            public Option<Pair<FunctionalRef, Type>> value() { return ma; }
822        };
823        // Loops through all the pruned_fns finding one with the most specific arg type
824        for( final Pair<FunctionalRef, Type> pruned_fn : pruned_fns ) {
825            Type cur_type_ = pruned_fn.second();
826            for( final Type cur_type : TypesUtil.conjuncts(cur_type_) ) {
827                cur_type.accept(new TypeAbstractVisitor_void() {
828                    @Override
829                    public void forArrowType(final ArrowType cur_type) {
830                        if( most_applicable.value().isNone() ) {
831                            most_applicable.set(some(Pair.<FunctionalRef,Type>make(pruned_fn.first(), cur_type)));
832                            return;
833                        }
834                        // do some gnarly double dispatch
835                        most_applicable.value().unwrap().second().accept(new TypeAbstractVisitor_void() {
836                            @Override
837                            public void forArrowType(ArrowType most_appl) {
838                                // Where is arg of cur_type <: arg of most_appl?
839                                ConstraintFormula sub =
840                                    subtypeChecker.subtype(Types.stripKeywords(cur_type.getDomain()),
841                                            Types.stripKeywords(most_appl.getDomain()));
842                                if( sub.solve().isTrue() ) {
843                                    // TODO: We should actually throw an error if they are equal!
844                                    most_applicable.set(some(Pair.<FunctionalRef,Type>make(pruned_fn.first(), cur_type)));
845                                }
846                            }
847                            @Override public void forType(Type that) { bug("An applicable function should have an arrow type: " + that); }
848                        });
849                    }
850                    @Override public void forType(Type that) {
851                        bug("An applicable function should have an arrow type: " + that);
852                    }
853                });
854            }
855        }
856        // Unwrap should always work b/c we already tested pruned_fns for empty, and
857        // on the first iteration of the loop, most_applicable is always set to some.
858        return new TypeCheckerResult(most_applicable.value().unwrap().first(), most_applicable.value().unwrap().second());
859    }
860
861    private static List<Pair<FunctionalRef, Type>> destructFnOverloadings(List<FunctionalRef> overloadings) {
862        List<Pair<FunctionalRef, Type>> result = new ArrayList<Pair<FunctionalRef, Type>>(overloadings.size());
863        for( FunctionalRef overloading : overloadings ) {
864                    if ( overloading.getOverloadingType().isNone() )
865                        bug(overloading,
866                            "Type checker should have type for the overloading of "
867                            + overloading.getOriginalName());
868                    result.add(Pair.make(overloading, overloading.getOverloadingType().unwrap()));
869        }
870        return result;
871    }
872
873    @Override
874    public TypeCheckerResult for_RewriteFnApp(_RewriteFnApp that) {
875        TypeCheckerResult arg_result = recur(that.getArgument());
876
877        if( postInference && that.getFunction() instanceof FnRef &&
878                    isPostInference((FnRef)that.getFunction()) ) {
879                    FnRef fns = (FnRef)that.getFunction();
880            // if we have finished typechecking, and we have encountered a reference to an overloaded function
881
882            if( !arg_result.isSuccessful() ) {
883                return TypeCheckerResult.compose(that, subtypeChecker, arg_result);
884            }
885
886            Type arg_type = arg_result.type().unwrap();
887            TypeCheckerResult fn_result = findStaticallyMostApplicableFn(destructFnOverloadings(fns.getOverloadings().unwrap()),
888                                                                                     arg_type, fns, fns.getOriginalName());
889            // Now just check the rewritten expression by the normal means
890            return for_RewriteFnAppOnly(that, Option.<TypeCheckerResult>none(), fn_result, arg_result);
891        }
892        else {
893            // Add constraints from the arguments, since they may affect overloading choice
894                    TypeCheckerResult fn_result = recur(that.getFunction());
895            Iterable<ConstraintFormula> arg_constraint = Collections.singletonList(arg_result.getNodeConstraints());
896            //debugging
897            ConstraintFormula temp = ConstraintFormula.TRUE;
898            for(ConstraintFormula i: arg_constraint){
899                temp.and(i, this.subtypeChecker.new SubtypeHistory());
900            }
901            Boolean blah = temp.isSatisfiable();
902            //end debugging
903            return this.extendWithConstraints(arg_constraint).for_RewriteFnAppOnly(that,
904                    Option.<TypeCheckerResult>none(), fn_result, arg_result);
905        }
906    }
907
908    public TypeCheckerResult for_RewriteFnAppOnly(_RewriteFnApp that, Option<TypeCheckerResult> exprType_result,
909            TypeCheckerResult function_result, TypeCheckerResult argument_result) {
910        // check sub expressions
911        if( function_result.type().isNone() || argument_result.type().isNone() )
912            return TypeCheckerResult.compose(that, subtypeChecker, function_result, argument_result);
913        Option<Pair<Type,ConstraintFormula>> app_result =
914            TypesUtil.applicationType(subtypeChecker, function_result.type().unwrap(),
915                    new ArgList(argument_result.type().unwrap()), downwardConstraint);
916        Option<Type> result_type;
917        TypeCheckerResult result;
918
919        if( app_result.isSome() ) {
920            result = new TypeCheckerResult(that,app_result.unwrap().second());
921            result_type = Option.some(app_result.unwrap().first());
922        }
923        else {
924            String err = "Applicable overloading of function " + that.getFunction() +
925                            " could not be found for argument type " + argument_result.type(); // error message needs work
926            result = new TypeCheckerResult(that, TypeError.make(err, that));
927            result_type = Option.none();
928        }
929
930        _RewriteFnApp new_node = ExprFactory.make_RewriteFnApp(NodeUtil.getSpan(that),
931                NodeUtil.isParenthesized(that),
932                result_type,
933                (Expr) function_result.ast(),
934                (Expr) argument_result.ast());
935
936        // On the post-inference pass, an application could produce Inference vars
937        TypeCheckerResult successful = new TypeCheckerResult(that);
938        if( postInference && TypesUtil.containsInferenceVarTypes(new_node) ) {
939            // close constraints
940            Pair<Boolean,Node> temp1 = TypesUtil.closeConstraints(new_node, subtypeChecker, function_result, argument_result, result);
941            new_node = (_RewriteFnApp)temp1.second();
942            Boolean ok = temp1.first();
943            if(result_type.isSome()){
944                Pair<Boolean,Node> temp2 = TypesUtil.closeConstraints(result_type.unwrap(), subtypeChecker, function_result, argument_result, result);
945                result_type = Option.some((Type)temp2.second());
946                ok&=temp2.first();
947            }
948            if(!ok){
949                String err = "Applicable overloading of function " + that.getFunction() + " could not be found for argument type " + argument_result.type();
950                successful = new TypeCheckerResult(that,TypeError.make(err, that));
951            }
952        }
953
954        return TypeCheckerResult.compose(new_node, result_type,
955                subtypeChecker, function_result, argument_result, result, successful);
956    }
957
958    public TypeCheckerResult for_RewriteObjectRefOnly(VarRef that, TypeCheckerResult exprType_result,
959                                                          TypeCheckerResult obj_result,
960                                                          List<TypeCheckerResult> staticArgs_result) {
961            Type t;
962            ConstraintFormula accumulated_constraints = ConstraintFormula.TRUE;
963            if( obj_result.type().isNone() ) {
964                return TypeCheckerResult.compose(that, subtypeChecker, obj_result,
965                                                 TypeCheckerResult.compose(that, subtypeChecker, staticArgs_result));
966            }
967            else if( (obj_result.type().unwrap() instanceof TraitType) ) {
968                t=obj_result.type().unwrap();
969                TraitType _t = (TraitType)t;
970
971                if ( NodeUtil.isGenericSingletonType(_t) ) {
972                    // instantiate with static parameters
973                    Option<ConstraintFormula> constraint = StaticTypeReplacer.argsMatchParams(that.getStaticArgs(),
974                                                                                              _t.getStaticParams(),
975                                                                                              this.subtypeChecker);
976                    if(constraint.isSome()) {
977                        // make a trait type that is GenericType instantiated
978                        t = NodeFactory.makeTraitType(NodeUtil.getSpan(_t),
979                                                      NodeUtil.isParenthesized(_t),
980                                                      _t.getName(),
981                                                      that.getStaticArgs());
982                        accumulated_constraints = constraint.unwrap();
983                    }
984                    else {
985                        // error
986                        String err = "Generic object, " + _t + " instantiated with invalid arguments, " + that.getStaticArgs();
987                        TypeCheckerResult e_result = new TypeCheckerResult(that, TypeError.make(err, that));
988                        return TypeCheckerResult.compose(that, subtypeChecker, obj_result, e_result,
989                                                         TypeCheckerResult.compose(that, subtypeChecker, staticArgs_result));
990                    }
991                }
992            }
993            else {
994                return bug("Unexpected type for ObjectRef.");
995            }
996
997            Node new_node = ExprFactory.makeVarRef(NodeUtil.getSpan(that), NodeUtil.isParenthesized(that),
998                                                   Option.<Type>some(t),
999                                                   (Id) obj_result.ast(),
1000                                                   (List<StaticArg>) TypeCheckerResult.astFromResults(staticArgs_result),
1001                                                   that.getLexicalDepth());
1002            return TypeCheckerResult.compose(new_node, t, subtypeChecker, obj_result,
1003                                             TypeCheckerResult.compose(that, subtypeChecker, staticArgs_result),
1004                                             new TypeCheckerResult(new_node,accumulated_constraints));
1005    }
1006
1007    @Override
1008    public TypeCheckerResult forAmbiguousMultifixOpExpr(final AmbiguousMultifixOpExpr that) {
1009        // See if we can successfully typecheck this expression as a multifix one.
1010        TypeCheckerResult multi_result =
1011            (ExprFactory.makeOpExpr(NodeUtil.getSpan(that),
1012                                                NodeUtil.isParenthesized(that),
1013                                                NodeUtil.getExprType(that),
1014                                                that.getMultifix_op(),
1015                                                that.getArgs()).accept(this));
1016
1017        if( multi_result.isSuccessful() ) {
1018            return multi_result;
1019        }
1020        else {
1021            if( that.getArgs().size() < 2 )
1022                bug("This never should have been neither multifix nor infix.");
1023
1024            // If not, do it as a collection of left-associated infix expressions
1025            return
1026            IterUtil.fold(IterUtil.skipFirst(that.getArgs()), IterUtil.first(that.getArgs()),
1027                    new Lambda2<Expr,Expr,Expr>(){
1028                public Expr value(Expr arg0, Expr arg1) {
1029                    return ExprFactory.makeOpExpr(that.getInfix_op(), arg0, arg1);
1030                }}).accept(this);
1031        }
1032
1033        //I am pretty sure this method rebuilds the ast correctly and fills in the exprType field without any changes
1034
1035    }
1036
1037    @Override
1038    public TypeCheckerResult forAnyType(AnyType that) {
1039        return new TypeCheckerResult(that);
1040    }
1041
1042    @Override
1043    public TypeCheckerResult forAPIName(APIName that) {
1044        return new TypeCheckerResult(that);
1045    }
1046
1047    // This case is only called for single element arrays ( e.g., [5] )
1048    // and not for pieces of ArrayElements
1049    @Override
1050    public TypeCheckerResult forArrayElementOnly(ArrayElement that, TypeCheckerResult exprType_result,
1051            List<TypeCheckerResult> staticArgs_result,
1052            TypeCheckerResult element_result) {
1053
1054        if( element_result.type().isNone() ) {
1055            return TypeCheckerResult.compose(that, subtypeChecker, element_result,
1056                    TypeCheckerResult.compose(that, subtypeChecker, staticArgs_result));
1057        }
1058
1059        List<StaticArg> staticArgs = that.getStaticArgs();
1060        Id array = Types.getArrayKName(1);
1061        Option<TypeConsIndex> ind=table.typeCons(array);
1062        if(ind.isNone()){
1063            array = Types.ARRAY_NAME;
1064            ind = table.typeCons(array);
1065            if(ind.isNone()){
1066                bug(array+"not in table");
1067            }
1068        }
1069        TraitIndex index = (TraitIndex)ind.unwrap();
1070        if(staticArgs.isEmpty()){
1071            TypeArg elem = NodeFactory.makeTypeArg(element_result.type().unwrap());
1072            IntArg lower = NodeFactory.makeIntArgVal(NodeUtil.getSpan(that),""+0);
1073            IntArg size = NodeFactory.makeIntArgVal(NodeUtil.getSpan(that),""+1);
1074            Type t=Types.makeArrayKType(1, Useful.list(elem, lower, size));
1075            Node new_node=ExprFactory.makeArrayElement(NodeUtil.getSpan(that), NodeUtil.isParenthesized(that),
1076                                                                   Option.some(t),
1077                                                                   (List<StaticArg>) TypeCheckerResult.astFromResults(staticArgs_result),
1078                                                                   (Expr) element_result.ast());
1079            return TypeCheckerResult.compose(new_node,t,this.subtypeChecker, element_result);
1080        }
1081        else{
1082            Option<ConstraintFormula> constraint = StaticTypeReplacer.argsMatchParams(that.getStaticArgs(), index.staticParameters(), this.subtypeChecker);
1083            if(constraint.isSome()){
1084                TypeCheckerResult res=this.checkSubtype(element_result.type().unwrap(),
1085                        ((TypeArg)that.getStaticArgs().get(0)).getTypeArg(), that,
1086                        element_result.type().unwrap()+" must be a subtype of "+((TypeArg)that.getStaticArgs().get(0)).getTypeArg());
1087                Type t=Types.makeArrayKType(1, that.getStaticArgs());
1088                Node new_node=ExprFactory.makeArrayElement(NodeUtil.getSpan(that), NodeUtil.isParenthesized(that),
1089                                                                           Option.some(t),
1090                                                                           (List<StaticArg>) TypeCheckerResult.astFromResults(staticArgs_result),
1091                                                                           (Expr) element_result.ast());
1092                return TypeCheckerResult.compose(new_node,t,this.subtypeChecker, element_result, res,
1093                        TypeCheckerResult.compose(new_node,this.subtypeChecker,staticArgs_result),
1094                        new TypeCheckerResult(new_node, constraint.unwrap()));
1095            }
1096            else{
1097                String err = "Explicit static arguments do not match required arguments for Array1 (" + index.staticParameters() + ".)";
1098                TypeCheckerResult err_result = new TypeCheckerResult(that, TypeError.make(err, that));
1099                return TypeCheckerResult.compose(that, subtypeChecker, err_result, element_result,
1100                        TypeCheckerResult.compose(that, subtypeChecker, staticArgs_result));
1101            }
1102        }
1103    }
1104
1105
1106    // This method is pretty long because we have to create a new visitor that visits ArrayElements and ArrayElement
1107    // knowing that we are inside of another ArrayElement.
1108
1109    private static Pair<Type,List<BigInteger>> getTypeAndBoundsFromArray(Type that){
1110        TraitType type;
1111        if(that instanceof TraitType){
1112            type=(TraitType)that;
1113        }
1114        else{
1115            return InterpreterBug.bug("Not an Array");
1116        }
1117        if(type.getName().toString().startsWith("FortressLibrary.Array")){
1118            List<BigInteger> dims = new ArrayList<BigInteger>();
1119            for(int i=2; i<type.getArgs().size(); i+=2){
1120                StaticArg arg = type.getArgs().get(i);
1121                if(arg instanceof IntArg){
1122                    IntExpr iexpr = ((IntArg)arg).getIntVal();
1123                    if(iexpr instanceof IntBase){
1124                        IntLiteralExpr dim = ((IntBase) iexpr).getIntVal();
1125                        BigInteger n = dim.getIntVal();
1126                        dims.add(n);
1127                    }
1128                    else{
1129                        return NI.nyi();
1130                    }
1131                }
1132                else{
1133                    return InterpreterBug.bug("Array type changed");
1134                }
1135            }
1136            Type t;
1137            if(type.getArgs().get(0) instanceof TypeArg){
1138                t = ((TypeArg)type.getArgs().get(0)).getTypeArg();
1139            }
1140            else{
1141                return InterpreterBug.bug("Array type changed");
1142            }
1143            return Pair.make(t, dims);
1144        }
1145        else{
1146            return InterpreterBug.bug("Not an Array");
1147        }
1148    }
1149
1150    @Override
1151    public TypeCheckerResult forArrayElements(ArrayElements that){
1152            Span span = NodeUtil.getSpan(that);
1153        List<TypeCheckerResult> subarrays = this.recurOnListOfArrayExpr(that.getElements());
1154        Lambda<TypeCheckerResult,Option<Pair<Type,List<BigInteger>>>> get = new Lambda<TypeCheckerResult,Option<Pair<Type,List<BigInteger>>>>(){
1155            public Option<Pair<Type, List<BigInteger>>> value(TypeCheckerResult arg0) {
1156                if(arg0.type().isSome()){
1157                    return Option.some(TypeChecker.this.getTypeAndBoundsFromArray(arg0.type().unwrap()));
1158                }
1159                else{
1160                    return Option.none();
1161                }
1162            }
1163        };
1164        Iterable <Option<Pair<Type, List<BigInteger>>>> temp = IterUtil.map(subarrays, get);
1165        List<Type> types = new ArrayList<Type>();
1166        List<List<BigInteger>> dims = new ArrayList<List<BigInteger>>();
1167        boolean failed=false;
1168        List<TypeCheckerResult> all_results = new ArrayList<TypeCheckerResult>(subarrays);
1169        for(Option<Pair<Type,List<BigInteger>>> i: temp){
1170            if(i.isNone()){
1171                //one of your subarrays already failed
1172                failed=true;
1173            }
1174            else{
1175                types.add(i.unwrap().first());
1176                dims.add(i.unwrap().second());
1177            }
1178        }
1179        List<BigInteger> first = dims.get(0);
1180        Boolean same_size = true;
1181        for(List<BigInteger> f: dims){
1182            same_size&=f.equals(first);
1183        }
1184        Type array_type = this.subtypeChecker.join(types);
1185
1186        if(!same_size){
1187            all_results.add(new TypeCheckerResult(that, TypeError.make("Not all subarrays the same size ", that)));
1188        }
1189
1190
1191
1192        // Now try to get array type for the dimension we have
1193        int dim = that.getDimension();
1194        Id array = Types.getArrayKName(dim);
1195        Option<TypeConsIndex> ind=table.typeCons(array);
1196        if(ind.isNone()){
1197            array = Types.ARRAY_NAME;
1198            ind = table.typeCons(array);
1199            if(ind.isNone()){
1200                bug(array+"not in table");
1201            }
1202        }
1203
1204        TraitIndex trait_index = (TraitIndex)ind.unwrap();
1205
1206
1207        Type return_type;
1208        ConstraintFormula accumulated_constraints = ConstraintFormula.TRUE;
1209        if(that.getStaticArgs().isEmpty()){
1210            if(failed || !same_size){
1211                return TypeCheckerResult.compose(that, subtypeChecker, all_results);
1212            }
1213            // then we just use what we determine to be true
1214            List<StaticArg> inferred_args = new ArrayList<StaticArg>(1+dim*2);
1215            inferred_args.add(NodeFactory.makeTypeArg(array_type));
1216            IntArg lower_bound = NodeFactory.makeIntArgVal(span,"0");
1217            IntArg size = NodeFactory.makeIntArgVal(span,
1218                                                                ((Integer)subarrays.size()).toString());
1219            inferred_args.add(lower_bound);
1220            inferred_args.add(size);
1221            for(int i=0;i<dim-1;i++) {
1222                BigInteger s = first.get(i);
1223                lower_bound = NodeFactory.makeIntArgVal(span,"0");
1224                size = NodeFactory.makeIntArgVal(span,s.toString());
1225                inferred_args.add(lower_bound);
1226                inferred_args.add(size);
1227            }
1228            // then instantiate and return
1229            return_type = Types.makeArrayKType(dim, inferred_args);
1230        }
1231        else{
1232            List<StaticArg> sargs = that.getStaticArgs();
1233            Option<ConstraintFormula> constraints = StaticTypeReplacer.argsMatchParams(sargs, trait_index.staticParameters(), this.subtypeChecker);
1234            if(constraints.isSome()) {
1235                // First arg MUST BE a TypeArg, and it must be a supertype of the elements
1236                Type declared_type = ((TypeArg)that.getStaticArgs().get(0)).getTypeArg();
1237                all_results.add(this.checkSubtype(array_type, declared_type, that, "Array elements must be a subtype of explicity declared type" + declared_type + "."));
1238                //Check infered dims against explicit dims
1239                return_type = Types.makeArrayKType(dim, sargs);
1240                accumulated_constraints=constraints.unwrap();
1241            }
1242            else {
1243                // wrong args passed
1244                all_results.add(new TypeCheckerResult(that, TypeError.make("Explicit static arguments don't matched required arguments for " + trait_index + ".", that)));
1245                return TypeCheckerResult.compose(that ,subtypeChecker, all_results);
1246            }
1247            if(failed || !same_size){
1248                return TypeCheckerResult.compose(that, Option.some(return_type) ,subtypeChecker, all_results);
1249            }
1250        }
1251
1252
1253
1254        Lambda<TypeCheckerResult,ArrayExpr> get_expr = new Lambda<TypeCheckerResult,ArrayExpr>(){
1255            public ArrayExpr value(TypeCheckerResult arg0) {
1256                return (ArrayExpr)arg0.ast();
1257            }
1258        };
1259        ArrayElements new_node=ExprFactory.makeArrayElements(NodeUtil.getSpan(that),
1260                                                                     NodeUtil.isParenthesized(that),
1261                                                                     Option.some(return_type) ,
1262                                                                     that.getStaticArgs(),
1263                                                                     that.getDimension(),
1264                                                                     Useful.list(IterUtil.map(subarrays, get_expr)),
1265                                                                     that.isOutermost());
1266
1267        all_results.add(new TypeCheckerResult(new_node, accumulated_constraints));
1268
1269        return TypeCheckerResult.compose(new_node, Option.some(return_type) ,subtypeChecker, all_results);
1270
1271    }
1272
1273    @Override
1274    public TypeCheckerResult forAsExpr(AsExpr that) {
1275        Type ascriptedType = that.getAnnType();
1276        TypeCheckerResult expr_result = that.getExpr().accept(this);
1277        Type exprType = expr_result.type().isSome() ? expr_result.type().unwrap() : Types.BOTTOM;
1278        // node rebuilding handled in forTypeAnnotatedExprOnly
1279        return forTypeAnnotatedExprOnly(that,
1280                expr_result,
1281                errorMsg("Attempt to ascribe expression of type ",
1282                        exprType, " to non-supertype ", ascriptedType));
1283    }
1284
1285    @Override
1286    public TypeCheckerResult forAsIfExpr(AsIfExpr that) {
1287        Type assumedType = that.getAnnType();
1288        TypeCheckerResult expr_result = that.getExpr().accept(this);
1289        Type exprType = expr_result.type().isSome() ? expr_result.type().unwrap() : Types.BOTTOM;
1290        // node rebuilding handled in forTypeAnnotatedExprOnly
1291        return forTypeAnnotatedExprOnly(that,
1292                expr_result,
1293                errorMsg("Attempt to assume type ", assumedType,
1294                        " from non-subtype ", exprType));
1295    }
1296
1297    @Override
1298    public TypeCheckerResult forAssignment(Assignment that) {
1299            if ( that.getOpsForLhs().isSome() ) // postInference pass
1300                return for_Assignment( that );
1301
1302        Pair<List<Type>, TypeCheckerResult> tuple_types_ = requireTupleType(that.getRhs(), that.getLhs().size());
1303
1304        // Get the types for the RHS, which must be a tuple if the LHS is
1305        TypeCheckerResult rhs_result = tuple_types_.second();
1306        List<Type> rhs_types = tuple_types_.first();
1307
1308        if( !rhs_result.isSuccessful() )
1309            return TypeCheckerResult.compose(that, subtypeChecker, rhs_result);
1310        if( rhs_types.size() != that.getLhs().size() )
1311            return bug("requireTupleType should always return a list of types equal to the size it is passed.");
1312
1313        List<TypeCheckerResult> lhs_results = new ArrayList<TypeCheckerResult>(that.getLhs().size());
1314        List<FunctionalRef> op_refs = that.getAssignOp().isSome() ? new ArrayList<FunctionalRef>(that.getLhs().size()) : Collections.<FunctionalRef>emptyList();
1315
1316        // Go through LHS, checking each one
1317        Iterator<Type> rhs_type_iter = rhs_types.iterator();
1318        for( Lhs lhs : that.getLhs() ) {
1319            Type rhs_type = rhs_type_iter.next();
1320            Pair<Option<FunctionalRef>, TypeCheckerResult> p = checkLhsAssignment(lhs, rhs_type, that.getAssignOp());
1321            lhs_results.add(p.second());
1322
1323            if( p.first().isSome() )
1324                op_refs.add(p.first().unwrap());
1325        }
1326
1327        Assignment new_node;
1328        if( that.getAssignOp().isSome() ) {
1329            // Create a new Assignment, with an FunctionalRef for each LHS
1330            new_node = ExprFactory.makeAssignment(NodeUtil.getSpan(that),
1331                                                  NodeUtil.isParenthesized(that),
1332                                                  Option.<Type>some(Types.VOID),
1333                                                  (List<Lhs>)TypeCheckerResult.astFromResults(lhs_results),
1334                                                  that.getAssignOp(),
1335                                                  (Expr)rhs_result.ast(),
1336                                                  Option.<List<FunctionalRef>>some(op_refs));
1337        }
1338        else {
1339            // Create a new Assignment
1340            new_node = ExprFactory.makeAssignment(NodeUtil.getSpan(that),
1341                    NodeUtil.isParenthesized(that),
1342                    Option.<Type>some(Types.VOID),
1343                    (List<Lhs>)TypeCheckerResult.astFromResults(lhs_results),
1344                    that.getAssignOp(),
1345                    (Expr)rhs_result.ast(),
1346                                        Option.<List<FunctionalRef>>none());
1347        }
1348        // This case could not result in open constraints: If there is an FunctionalRef, this must not be
1349        // the postInference pass, because AssignmentNodes should exist instead. If there is
1350        // no FunctionalRef, then no open constraints can be created.
1351        return TypeCheckerResult.compose(new_node, Types.VOID, subtypeChecker, rhs_result,
1352                TypeCheckerResult.compose(new_node, subtypeChecker, lhs_results));
1353    }
1354
1355    private TypeCheckerResult for_Assignment(Assignment that) {
1356        Pair<List<Type>, TypeCheckerResult> tuple_types_ = requireTupleType(that.getRhs(), that.getLhs().size());
1357
1358        // Get the types for the RHS, which must be a tuple if the LHS is
1359        TypeCheckerResult rhs_result = tuple_types_.second();
1360        List<Type> rhs_types = tuple_types_.first();
1361
1362        if( !rhs_result.isSuccessful() )
1363            return TypeCheckerResult.compose(that, subtypeChecker, rhs_result);
1364        if( rhs_types.size() != that.getLhs().size() )
1365            return bug("requireTupleType should always return a list of types equal to the size it is passed.");
1366
1367        List<TypeCheckerResult> lhs_results = new ArrayList<TypeCheckerResult>(that.getLhs().size());
1368        List<FunctionalRef> op_refs = that.getAssignOp().isSome() ? new ArrayList<FunctionalRef>(that.getLhs().size()) : Collections.<FunctionalRef>emptyList();
1369
1370        // Go through LHS, checking each one
1371        Iterator<Type> rhs_type_iter = rhs_types.iterator();
1372        for( Lhs lhs : that.getLhs() ) {
1373            Type rhs_type = rhs_type_iter.next();
1374            Pair<Option<FunctionalRef>, TypeCheckerResult> p = checkLhsAssignment(lhs, rhs_type, that.getAssignOp());
1375            lhs_results.add(p.second());
1376
1377            if( p.first().isSome() )
1378                op_refs.add(p.first().unwrap());
1379        };
1380
1381        // Create a Assignment, with an FunctionalRef for each LHS
1382        Assignment new_node = ExprFactory.makeAssignment(NodeUtil.getSpan(that),
1383                                                     NodeUtil.isParenthesized(that),
1384                                                     Option.<Type>some(Types.VOID),
1385                                                     (List<Lhs>)TypeCheckerResult.astFromResults(lhs_results),
1386                                                     that.getAssignOp(),
1387                                                     (Expr)rhs_result.ast(),
1388                                                     Option.<List<FunctionalRef>>some(op_refs));
1389
1390        TypeCheckerResult result = TypeCheckerResult.compose(new_node, Types.VOID, subtypeChecker, rhs_result,
1391                TypeCheckerResult.compose(new_node, subtypeChecker, lhs_results));
1392
1393        // The application of operators could cause Inference Vars to be introduced
1394        TypeCheckerResult successful = new TypeCheckerResult(that);
1395        if( postInference && TypesUtil.containsInferenceVarTypes(new_node) ) {
1396            Pair<Boolean,Node> temp = TypesUtil.closeConstraints(new_node, result);
1397            new_node = (Assignment)temp.second();
1398            if(!temp.first()){
1399                String err = "No overloading for " + that.getAssignOp().unwrap();
1400                successful = new TypeCheckerResult(that, TypeError.make(err,that));
1401            }
1402        }
1403        return TypeCheckerResult.compose(new_node, Types.VOID, subtypeChecker, result, successful);
1404    }
1405    /**
1406     * Checks that something of type rhs_type can be assigned to the given lhs. If opr is some, we have to
1407     * take that in to account as well. This method is still pretty complicated...
1408     */
1409    private Pair<Option<FunctionalRef>, TypeCheckerResult> checkLhsAssignment(final Lhs lhs, final Type rhs_type, final Option<FunctionalRef> opr) {
1410
1411        // Different assignment rules for each type of LHS...
1412        Pair<TypeCheckerResult, Type> result_and_arg_type = lhs.accept(new NodeAbstractVisitor<Pair<TypeCheckerResult, Type>>(){
1413            @Override public Pair<TypeCheckerResult, Type> forFieldRef(FieldRef that) {
1414                TypeCheckerResult obj_result = recur(that.getObj());
1415                TypeCheckerResult field_result = recur(that); // only used for ast, and type if opr is SOME
1416                if( !obj_result.isSuccessful() )
1417                    return Pair.<TypeCheckerResult,Type>make(TypeCheckerResult.compose(that, subtypeChecker, obj_result), Types.BOTTOM);
1418
1419                Type obj_type = obj_result.type().unwrap();
1420                List<TraitType> traits = traitTypesCallable(obj_type);
1421                TypeCheckerResult r = findSetterInTraitHierarchy(that.getField(), traits, rhs_type, that);
1422
1423                if( opr.isSome() && field_result.isSuccessful() ) {
1424                    // Return the type of the field for opr application
1425                    return Pair.<TypeCheckerResult,Type>make(TypeCheckerResult.compose(field_result.ast(), subtypeChecker, r, field_result, obj_result), field_result.type().unwrap());
1426                }
1427                else if( opr.isSome() && !field_result.isSuccessful() ) {
1428                    return Pair.<TypeCheckerResult,Type>make(TypeCheckerResult.compose(that, subtypeChecker, r, field_result, obj_result), Types.BOTTOM);
1429                }
1430                else {
1431                    return Pair.<TypeCheckerResult, Type>make(TypeCheckerResult.compose(field_result.ast(), subtypeChecker, r, obj_result), Types.BOTTOM);
1432                }
1433            }
1434
1435            @Override public Pair<TypeCheckerResult, Type> forLValue(LValue that) {
1436                if( !that.isMutable() ) {
1437                    String err = "Left-hand side of assignment must be mutable.";
1438                    return Pair.<TypeCheckerResult,Type>make(new TypeCheckerResult(that, TypeError.make(err, that)), Types.BOTTOM);
1439                }
1440                else {
1441                    Type lhs_type = that.getIdType().unwrap();
1442                    return Pair.make(checkSubtype(rhs_type,
1443                            lhs_type, that,
1444                            "Type of right-hand side of assignment, " + rhs_type + ", must be a sub-type of left-hand side, " + lhs_type + "."),
1445                            lhs_type);
1446                }
1447            }
1448
1449            @Override
1450            public Pair<TypeCheckerResult, Type> forParam(Param that) {
1451                            if ( ! NodeUtil.isVarargsParam(that) ) {
1452                if( !NodeUtil.isMutable(that) ) {
1453                    String err = "Left-hand side of assignment must be mutable.";
1454                    return Pair.<TypeCheckerResult,Type>make(new TypeCheckerResult(that, TypeError.make(err, that)), Types.BOTTOM);
1455                }
1456                else {
1457                    Type lhs_type = that.getIdType().unwrap();
1458                    return Pair.make(checkSubtype(rhs_type,
1459                            lhs_type, that,
1460                            "Type of right-hand side of assignment, " + rhs_type + ", must be a sub-type of left-hand side, " + lhs_type + "."),
1461                            lhs_type);
1462                }
1463                            } else
1464                                return bug(that, "Varargs parameter should not appear in the left-hand side of an assignment.");
1465            }
1466
1467            @Override
1468            public Pair<TypeCheckerResult, Type> forSubscriptExpr(SubscriptExpr that) {
1469                // If there is an op, we must typechecker that as a normal read reference
1470                TypeCheckerResult read_result = recur(that); // only used if opr is SOME
1471
1472                // make sure there is a subscript setter for type
1473                // This method is very similar to forSubscriptExprOnly in Typechecker
1474                // except that we must graft the new RHS type onto the end of subs_types
1475                // to see if there is an appropriate setter method.
1476                TypeCheckerResult obj_result = that.getObj().accept(TypeChecker.this);
1477                List<TypeCheckerResult> subs_result = TypeChecker.this.recurOnListOfExpr(that.getSubs());
1478                // ignore op_result...
1479                List<TypeCheckerResult> staticArgs_result = TypeChecker.this.recurOnListOfStaticArg(that.getStaticArgs());
1480
1481                TypeCheckerResult all_result =
1482                    TypeCheckerResult.compose(that, subtypeChecker, obj_result,
1483                            TypeCheckerResult.compose(that, subtypeChecker, subs_result),
1484                            TypeCheckerResult.compose(that, subtypeChecker, staticArgs_result));
1485
1486                if( obj_result.type().isNone() ) return Pair.<TypeCheckerResult,Type>make(all_result,Types.BOTTOM);
1487                for( TypeCheckerResult r : subs_result )
1488                    if( r.type().isNone() ) return Pair.<TypeCheckerResult,Type>make(all_result,Types.BOTTOM);
1489
1490                // get types
1491                Type obj_type = obj_result.type().unwrap();
1492                List<Type> subs_types = CollectUtil.makeList(IterUtil.map(subs_result, new Lambda<TypeCheckerResult,Type>(){
1493                    public Type value(TypeCheckerResult arg0) { return arg0.type().unwrap(); }}));
1494                // put rhs type on the end
1495                subs_types = Useful.concat(subs_types, Collections.singletonList(rhs_type));
1496
1497                TypeCheckerResult final_result =
1498                    TypeChecker.this.subscriptHelper(that, that.getOp(), obj_type, subs_types, that.getStaticArgs());
1499
1500                SubscriptExpr new_node = (SubscriptExpr)read_result.ast();
1501
1502                if(opr.isSome() && read_result.isSuccessful() ) {
1503                    return Pair.<TypeCheckerResult,Type>make(TypeCheckerResult.compose(new_node,
1504                            read_result.type(), subtypeChecker, read_result, final_result, all_result), Types.BOTTOM);
1505                }
1506                else if( opr.isSome()) {
1507                    return Pair.make(TypeCheckerResult.compose(new_node,
1508                            read_result.type(), subtypeChecker, read_result, final_result, all_result), read_result.type().unwrap());
1509                }
1510                else {
1511                    return Pair.<TypeCheckerResult,Type>make(TypeCheckerResult.compose(new_node,
1512                            read_result.type(), subtypeChecker, final_result, all_result), Types.BOTTOM);
1513                }
1514            }
1515
1516            @Override
1517            public Pair<TypeCheckerResult, Type> forVarRef(VarRef that) {
1518                TypeCheckerResult var_result = recur(that);
1519                if( !var_result.isSuccessful() ) return Pair.<TypeCheckerResult, Type>make(var_result, Types.BOTTOM);
1520
1521                TypeCheckerResult sub_result = checkSubtype(rhs_type, var_result.type().unwrap(), lhs,
1522                        "Type of right-hand side of assignment, " +rhs_type+ ", must be subtype of left-hand side, " + var_result.type() + ".");
1523
1524                TypeEnv env = that.getVarId().getApiName().isSome() ? returnTypeEnvForApi(that.getVarId().getApiName().unwrap()) : typeEnv;
1525                Option<BindingLookup> bl = env.binding(that.getVarId());
1526                if( bl.isNone() ) return bug("Inconsistent results from TypeEnv and typechecking VarRef");
1527
1528                // make sure it's immutable
1529                if( !(bl.unwrap().isMutable()) ) {
1530                    String err = "Left-hand side of assignment must be mutable.";
1531                    return Pair.<TypeCheckerResult,Type>make(new TypeCheckerResult(that, TypeError.make(err, that)), Types.BOTTOM);
1532                }
1533                else {
1534                    return Pair.make(TypeCheckerResult.compose(var_result.ast(), subtypeChecker, sub_result, var_result), var_result.type().unwrap());
1535                }
1536            }
1537
1538        });
1539
1540        final Type arg_type = result_and_arg_type.second();
1541        // Find the arrow type of the opr, if it is some
1542        Option<TypeCheckerResult> opr_type = (new NodeDepthFirstVisitor<TypeCheckerResult>() {
1543            @Override public TypeCheckerResult forFnRef(FnRef that) {
1544                            return TypeChecker.this.recur(that);
1545                        }
1546            @Override public TypeCheckerResult forOpRef(OpRef that) {
1547                            if ( isPostInference(that) ) {
1548                                Type args_type = NodeFactory.makeTupleType(NodeUtil.getSpan(that),
1549                                                                           Useful.list(arg_type, rhs_type));
1550                                return findStaticallyMostApplicableFn(TypeChecker.destructOpOverLoading(that.getOverloadings().unwrap()),
1551                                                                      args_type, that, that.getOriginalName());
1552                            }
1553
1554                            TypeCheckerResult op_result = TypeChecker.this.recur(that);
1555                            if( !op_result.isSuccessful() ) return op_result;
1556
1557                            Option<Pair<Type, ConstraintFormula>> app_result =
1558                            TypesUtil.applicationType(subtypeChecker, op_result.type().unwrap(), new ArgList(arg_type, rhs_type), downwardConstraint);
1559
1560                            if( app_result.isNone() ) {
1561                                String err = "No overloading of " + that.getOriginalName() + " can be found that applies to types " +
1562                                    arg_type + " and " + rhs_type + ".";
1563                                return new TypeCheckerResult(that, TypeError.make(err, that));
1564                            }
1565                            else {
1566                                TypeCheckerResult app_result_ = new TypeCheckerResult(that, app_result.unwrap().second());
1567                                return TypeCheckerResult.compose(op_result.ast(), subtypeChecker, app_result_, op_result);
1568                            }
1569                        }
1570                    }).recurOnOptionOfFunctionalRef(opr);
1571
1572        TypeCheckerResult result = TypeCheckerResult.compose(result_and_arg_type.first().ast(), subtypeChecker, result_and_arg_type.first(),
1573                                                                     TypeCheckerResult.compose(result_and_arg_type.first().ast(), subtypeChecker, opr_type));
1574
1575        return Pair.<Option<FunctionalRef>,TypeCheckerResult>make((Option<FunctionalRef>)TypeCheckerResult.astFromResult(opr_type), result);
1576    }
1577
1578    /**
1579     * An assertion that the type of the given expression should be a tuple type of the given size.
1580     * If it is of a different size, an error will be reported. If is not an instance of TupleType,
1581     * a tuple of inference variables will be created instead, with the restriction that it must have the
1582     * same type as e's type.
1583     */
1584    private Pair<List<Type>, TypeCheckerResult> requireTupleType(final Expr e, int size) {
1585        final List<Type> inference_var_types = NodeFactory.make_InferenceVarTypes(NodeUtil.getSpan(e), size); // may not be used
1586        final TypeCheckerResult e_result = recur(e);
1587
1588        if( !e_result.isSuccessful() )
1589            return Pair.make(inference_var_types, e_result);
1590
1591        if( size == 1 ) {
1592            return Pair.make(Collections.singletonList(e_result.type().unwrap()), e_result);
1593        }
1594
1595        return e_result.type().unwrap().accept(new TypeAbstractVisitor<Pair<List<Type>, TypeCheckerResult>>() {
1596            @Override
1597            public Pair<List<Type>, TypeCheckerResult> forTupleType(TupleType that) {
1598                return Pair.make(that.getElements(), e_result);
1599            }
1600            @Override
1601            public Pair<List<Type>, TypeCheckerResult> forType(Type that) {
1602                Type tuple_type = NodeFactory.makeTupleType(NodeUtil.getSpan(that),
1603                                                                            inference_var_types);
1604                TypeCheckerResult sub_1 = checkSubtype(tuple_type, that, e);
1605                TypeCheckerResult sub_2 = checkSubtype(that, tuple_type, e);
1606                TypeCheckerResult tcr = TypeCheckerResult.compose(e, subtypeChecker, sub_1, sub_2, e_result);
1607                return Pair.make(inference_var_types, tcr);
1608            }
1609        });
1610    }
1611
1612
1613    private TypeCheckerResult forAtomic(Expr body, final String errorMsg) {
1614        TypeChecker newChecker = new TypeChecker(table,
1615                //staticParamEnv,
1616                typeEnv,
1617                compilationUnit,
1618                subtypeChecker,
1619                labelExitTypes,
1620                postInference,
1621                downwardConstraint) {
1622            @Override public TypeCheckerResult forSpawn(Spawn that) {
1623                // Use TypeChecker's forSpawn method, but compose an error onto the result
1624                return TypeCheckerResult.compose(
1625                        that,
1626                        subtypeChecker,
1627                        new TypeCheckerResult(that,
1628                                TypeError.make(errorMsg,
1629                                        that)), that.accept(TypeChecker.this));
1630            }
1631        };
1632        return body.accept(newChecker);
1633    }
1634
1635    @Override
1636    public TypeCheckerResult forAtomicExpr(AtomicExpr that) {
1637        TypeCheckerResult expr_result =
1638            forAtomic(that.getExpr(),
1639                    errorMsg("A 'spawn' expression must not occur inside an 'atomic' expression."));
1640
1641        AtomicExpr new_node = ExprFactory.makeAtomicExpr(NodeUtil.getSpan(that),
1642                NodeUtil.isParenthesized(that),
1643                expr_result.type(),
1644                (Expr)expr_result.ast());
1645        return TypeCheckerResult.compose(new_node, expr_result.type(), subtypeChecker, expr_result);
1646    }
1647
1648    @Override
1649    public TypeCheckerResult forBigFixity(BigFixity that) {
1650        return new TypeCheckerResult(that);
1651    }
1652
1653    @Override
1654    public TypeCheckerResult forCaseExpr(CaseExpr that) {
1655        Option<TypeCheckerResult> param_result = this.recurOnOptionOfExpr(that.getParam());
1656        Option<TypeCheckerResult> compare_result = this.recurOnOptionOfFunctionalRef(that.getCompare());
1657        TypeCheckerResult equalsOp_result = that.getEqualsOp().accept(this);
1658        TypeCheckerResult inOp_result = that.getInOp().accept(this);
1659        NodeDepthFirstVisitor<Triple<CaseClause, TypeCheckerResult,TypeCheckerResult>> temp = new NodeDepthFirstVisitor<Triple<CaseClause, TypeCheckerResult,TypeCheckerResult>>() {
1660            @Override
1661            public Triple<CaseClause, TypeCheckerResult, TypeCheckerResult> forCaseClause(
1662                    CaseClause that) {
1663                TypeCheckerResult match_result = that.getMatchClause().accept(TypeChecker.this);
1664                TypeCheckerResult body_result = that.getBody().accept(TypeChecker.this);
1665                CaseClause new_node = NodeFactory.makeCaseClause(NodeUtil.getSpan(that), (Expr)match_result.ast(), (Block)body_result.ast(),
1666                                                                     that.getOp());
1667                return Triple.<CaseClause,TypeCheckerResult,TypeCheckerResult>make(new_node, match_result, body_result);
1668            }
1669        };
1670        List<Triple<CaseClause, TypeCheckerResult,TypeCheckerResult>> clauses_result = temp.recurOnListOfCaseClause(that.getClauses());
1671        Option<TypeCheckerResult> elseClause_result = recurOnOptionOfBlock(that.getElseClause());
1672
1673        // Checker that clauses all typechecked properly
1674        for(Triple<CaseClause, TypeCheckerResult,TypeCheckerResult> clause_result: clauses_result){
1675            if(clause_result.second().type().isNone() || clause_result.third().type().isNone()) {
1676                return TypeCheckerResult.compose(that, subtypeChecker,
1677                        TypeCheckerResult.compose(that, subtypeChecker, CollectUtil.makeList(IterUtil.tripleSeconds(clauses_result))),
1678                        TypeCheckerResult.compose(that, subtypeChecker, CollectUtil.makeList(IterUtil.tripleThirds(clauses_result))));
1679            }
1680        }
1681
1682        // Check that compare typechecked, if it exists
1683        if( compare_result.isSome()) {
1684            if(compare_result.unwrap().type().isNone()){
1685                return TypeCheckerResult.compose(that, subtypeChecker, compare_result.unwrap());
1686            }
1687        }
1688        // Check elseClause
1689        if(elseClause_result.isSome()){
1690            if(elseClause_result.unwrap().type().isNone()){
1691                return TypeCheckerResult.compose(that, subtypeChecker, elseClause_result.unwrap());
1692            }
1693        }
1694        // Check if we are dealing with a normal case (i.e. not an extremum)
1695        if (that.getParam().isSome()) {
1696            if(param_result.unwrap().type().isNone()){
1697                return TypeCheckerResult.compose(that, subtypeChecker, param_result.unwrap());
1698            }
1699
1700            if(equalsOp_result.type().isNone() || inOp_result.type().isNone()){
1701                return bug("Equals or In does not have a type");
1702            }
1703
1704            return forCaseExprNormal(that);
1705        } else {
1706            return forExtremumOnly(that, compare_result.unwrap(), clauses_result);
1707        }
1708    }
1709
1710    // Handle regular (non-extremum) case expressions
1711    private TypeCheckerResult forCaseExprNormal(CaseExpr that) {
1712        Option<TypeCheckerResult> else_result_ = recurOnOptionOfBlock(that.getElseClause());
1713        Option<TypeCheckerResult> param_result_ = recurOnOptionOfExpr(that.getParam());
1714        Option<TypeCheckerResult> compare_result_ = recurOnOptionOfFunctionalRef(that.getCompare());
1715        TypeCheckerResult equals_result = recur(that.getEqualsOp());
1716        TypeCheckerResult in_result = recur(that.getInOp());
1717
1718        // Check all of these subexpressions
1719        if( (else_result_.isSome() && !else_result_.unwrap().isSuccessful()) || (param_result_.isSome() && !param_result_.unwrap().isSuccessful()) ||
1720                (compare_result_.isSome() && !compare_result_.unwrap().isSuccessful()) || !equals_result.isSuccessful() || !in_result.isSuccessful() ) {
1721            TypeCheckerResult.compose(that, subtypeChecker, equals_result, in_result,
1722                    TypeCheckerResult.compose(that, subtypeChecker, else_result_, param_result_, compare_result_));
1723        }
1724
1725        List<TypeCheckerResult> clause_results = new ArrayList<TypeCheckerResult>(that.getClauses().size());
1726        List<Type> clause_types = new ArrayList<Type>(that.getClauses().size());
1727
1728        for( CaseClause clause : that.getClauses() ) {
1729            Type param_type = param_result_.unwrap().type().unwrap(); // assured by the fact that we are caseExprNormal
1730
1731            if( postInference ) {
1732                // after inference, the case clause had better have the op field set
1733                            if( clause.getOp().isNone() )
1734                                    return bug("All case clauses should be rewritten to reflect the chosen compare op.");
1735
1736                Pair<Type, TypeCheckerResult> p = for_CaseClauseGetType(clause, param_result_);
1737                clause_results.add(p.second());
1738                clause_types.add(p.first());
1739            }
1740            else {
1741                // during inference, we'll try to apply the given compare op (if there is one) and otherwise try
1742                // equals and in. We will set the op field with a chosen opr.
1743                Pair<Type, TypeCheckerResult> p = forCaseClauseRewriteAndGetType(clause, param_result_, compare_result_, equals_result, in_result);
1744                clause_results.add(p.second());
1745                clause_types.add(p.first());
1746            }
1747        }
1748
1749        // Also, do else block if necessary
1750        if( else_result_.isSome() ) {
1751            clause_types.add(else_result_.unwrap().type().unwrap());
1752        }
1753
1754        Type result_type = NodeFactory.makeIntersectionType(CollectUtil.asSet(clause_types));
1755        CaseExpr new_node = ExprFactory.makeCaseExpr(NodeUtil.getSpan(that),
1756                NodeUtil.isParenthesized(that),
1757                some(result_type),
1758                (Option<Expr>)TypeCheckerResult.astFromResult(param_result_),
1759                (Option<FunctionalRef>)TypeCheckerResult.astFromResult(compare_result_),
1760                (FunctionalRef)equals_result.ast(),
1761                (FunctionalRef)in_result.ast(),
1762                (List<CaseClause>)TypeCheckerResult.astFromResults(clause_results),
1763                (Option<Block>)TypeCheckerResult.astFromResult(else_result_));
1764        TypeCheckerResult result = TypeCheckerResult.compose(new_node, subtypeChecker, equals_result, in_result,
1765                TypeCheckerResult.compose(new_node, subtypeChecker, clause_results),
1766                TypeCheckerResult.compose(new_node, subtypeChecker, param_result_, compare_result_, else_result_));
1767
1768        // The application of operators (IN, EQUALS) could cause Inference Vars to be introduced
1769        TypeCheckerResult successful = new TypeCheckerResult(that);
1770        if( postInference && TypesUtil.containsInferenceVarTypes(new_node) ) {
1771            Pair<Boolean,Node> temp1 = TypesUtil.closeConstraints(new_node, result);
1772            new_node = (CaseExpr)temp1.second();
1773            Pair<Boolean,Node> temp2 = TypesUtil.closeConstraints(result_type, result);
1774            result_type = (Type)temp2.second();
1775            if(!temp1.first() || !temp2.first()){
1776                String err = "No overloading of " + (that.getCompare().isSome()? that.getCompare().unwrap() + "." : that.getEqualsOp() + " or " + that.getInOp() + ".");
1777                successful = new TypeCheckerResult(that, TypeError.make(err,that));
1778            }
1779        }
1780        return TypeCheckerResult.compose(new_node, result_type, subtypeChecker, result);
1781    }
1782
1783    /**
1784     * Typecheck the clause, using one of the ops (compare, equals, or in), return the type of the right-hand side block, and
1785     * rewrite the CaseClause to set the op field. All TypeCheckerResults must contain types, and must contain the AST type
1786     * that is reflected by their name.
1787     */
1788    private Pair<Type, TypeCheckerResult> forCaseClauseRewriteAndGetType(CaseClause clause, Option<TypeCheckerResult> param_result_,
1789            Option<TypeCheckerResult> compare_result_, TypeCheckerResult equals_result, TypeCheckerResult in_result) {
1790        TypeCheckerResult match_result = recur(clause.getMatchClause());
1791        TypeCheckerResult block_result = recur(clause.getBody());
1792
1793        // make sure subexpressions were well-typed
1794        if( !match_result.isSuccessful() || !block_result.isSuccessful() )
1795            return Pair.<Type,TypeCheckerResult>make(Types.BOTTOM, TypeCheckerResult.compose(clause, subtypeChecker, match_result, block_result));
1796
1797        Type match_type = match_result.type().unwrap();
1798        Type block_type = block_result.type().unwrap();
1799        FunctionalRef chosen_op;
1800        Option<Pair<Type, ConstraintFormula>> application_result;
1801        Type param_type = param_result_.unwrap().type().unwrap();
1802        ArgList args = new ArgList(param_type, match_type);
1803
1804        // If compare is some, we use that operator
1805        if( compare_result_.isSome() ) {
1806            chosen_op = (FunctionalRef)compare_result_.unwrap().ast();
1807            Type compare_type = compare_result_.unwrap().type().unwrap();
1808            application_result = TypesUtil.applicationType(subtypeChecker, compare_type, args, downwardConstraint);
1809        }
1810        else {
1811            // Check both = and IN operators
1812            // we first want to do <: generator test.
1813            // If both are sat, we use =, if only gen_subtype_c is sat, we use IN
1814            ConstraintFormula gen_subtype_c =
1815                subtypeChecker.subtype(match_type, Types.makeGeneratorType(NodeFactory.make_InferenceVarType(NodeUtil.getSpan(clause))));
1816            ConstraintFormula gen_subtype_p =
1817                subtypeChecker.subtype(param_type, Types.makeGeneratorType(NodeFactory.make_InferenceVarType(NodeUtil.getSpan(clause))));
1818
1819            if( gen_subtype_c.isSatisfiable() && !gen_subtype_p.isSatisfiable() ) {
1820                // Implicit IN
1821                chosen_op = (FunctionalRef)in_result.ast();
1822                Type in_type = in_result.type().unwrap();
1823                application_result = TypesUtil.applicationType(subtypeChecker, in_type, args, downwardConstraint);
1824            }
1825            else {
1826                // Implicit =
1827                chosen_op = (FunctionalRef)equals_result.ast();
1828                Type equals_type = equals_result.type().unwrap();
1829                application_result = TypesUtil.applicationType(subtypeChecker, equals_type, args, downwardConstraint);
1830            }
1831        }
1832
1833        if( application_result.isNone() ) {
1834            // error
1835            String err = "No overloading of the operator " + chosen_op + " could be found for arguments of type " +
1836            param_type + " (Param Type)  and " + match_type + " (Match Type).";
1837            TypeCheckerResult e_r = new TypeCheckerResult(clause, TypeError.make(err, clause));
1838            return Pair.<Type,TypeCheckerResult>make(block_type, TypeCheckerResult.compose(clause, subtypeChecker, e_r, match_result, block_result));
1839        }
1840        else {
1841            CaseClause new_node = NodeFactory.makeCaseClause(NodeUtil.getSpan(clause),
1842                                                             (Expr)match_result.ast(),
1843                                                             (Block)block_result.ast(),
1844                                                             Option.<FunctionalRef>some(chosen_op));
1845            TypeCheckerResult app_result = new TypeCheckerResult(new_node, application_result.unwrap().second());
1846            Type app_type = application_result.unwrap().first();
1847            // We still must ensure the type of the application is a Boolean
1848            TypeCheckerResult bool_result = checkSubtype(app_type, Types.BOOLEAN, new_node,
1849                    "Result of application of " + chosen_op + " to param and match expression must have type boolean, but had type " + app_type + ".");
1850            return Pair.make(block_type, TypeCheckerResult.compose(new_node, subtypeChecker, bool_result, app_result, match_result, block_result));
1851        }
1852    }
1853    /**
1854         * pointInference pass
1855     * Typecheck the clause AND return the type of the right-hand side.
1856     */
1857    private Pair<Type, TypeCheckerResult> for_CaseClauseGetType(CaseClause clause, Option<TypeCheckerResult> param_result_) {
1858            // after inference, the case clause had better have the op field set
1859            if( clause.getOp().isNone() )
1860                return bug("All case clauses should be rewritten to reflect the chosen compare op.");
1861
1862            FunctionalRef op = clause.getOp().unwrap();
1863
1864        TypeCheckerResult match_result = recur(clause.getMatchClause());
1865        TypeCheckerResult block_result = recur(clause.getBody());
1866
1867        // make sure subexpressions were well-typed
1868        if( !match_result.isSuccessful() || !block_result.isSuccessful() )
1869            return Pair.<Type,TypeCheckerResult>make(Types.BOTTOM, TypeCheckerResult.compose(clause, subtypeChecker, match_result, block_result));
1870
1871        Type match_type = match_result.type().unwrap();
1872        Type block_type = block_result.type().unwrap();
1873        Type param_type = param_result_.unwrap().type().unwrap();
1874
1875        if( isPostInference(op) ) {
1876            // Our operator is an overloading, so we should find the statically most applicable one and rewrite
1877            Type arg_type = NodeFactory.makeTupleType(NodeUtil.getSpan(clause),
1878                                                                  Useful.list(param_type, match_type));
1879            TypeCheckerResult app_result_1 =
1880                            findStaticallyMostApplicableFn(destructOpOverLoading(op.getOverloadings().unwrap()),
1881                                                           arg_type, op, op.getOriginalName());
1882
1883            if( app_result_1.isSuccessful() ) {
1884                Type arrow_type = app_result_1.type().unwrap();
1885                Option<Pair<Type, ConstraintFormula>> app_result_2 =
1886                    TypesUtil.applicationType(subtypeChecker, arrow_type, new ArgList(param_type, match_type), downwardConstraint);
1887
1888                if( app_result_2.isSome() ) {
1889                    CaseClause new_node = NodeFactory.makeCaseClause(NodeUtil.getSpan(clause),
1890                                                                             (Expr)match_result.ast(),
1891                                                                             (Block)block_result.ast(),
1892                                                                             Option.<FunctionalRef>some((FunctionalRef)app_result_1.ast()));
1893                    TypeCheckerResult app_result_3 = new TypeCheckerResult(new_node, app_result_2.unwrap().second());
1894                    Type app_type = app_result_2.unwrap().first();
1895                    // We still must ensure the type of the application is a Boolean
1896                    TypeCheckerResult bool_result = checkSubtype(app_type, Types.BOOLEAN, new_node,
1897                                                                                     "Result of application of " + op + " to param and match expression must have type boolean, but had type " + app_type + ".");
1898                    return Pair.make(block_type, TypeCheckerResult.compose(new_node, subtypeChecker, bool_result, app_result_3, match_result, block_result));
1899                }
1900                else {
1901                    // should be impossible, unless applicationType and findStaticallyMostApplicable don't agree...
1902                    return bug("applicationType and findStaticallyMostApplicable do not agree to function applicability. " + clause);
1903                }
1904            }
1905            else {
1906                return Pair.make(block_type, TypeCheckerResult.compose(clause, subtypeChecker, app_result_1, match_result, block_result, param_result_.unwrap()));
1907            }
1908        }
1909        else {
1910            // no overloading, so just do the normal thing
1911                    TypeCheckerResult op_result = recur(op);
1912            if( !op_result.isSuccessful() ) return Pair.make(block_type, TypeCheckerResult.compose(clause, subtypeChecker, op_result));
1913            Option<Pair<Type, ConstraintFormula>> app_result_1 =
1914                TypesUtil.applicationType(subtypeChecker, op_result.type().unwrap(), new ArgList(param_type, match_type), downwardConstraint);
1915            if( app_result_1.isSome() ) {
1916                CaseClause new_node = NodeFactory.makeCaseClause(NodeUtil.getSpan(clause),
1917                                                                     (Expr)match_result.ast(),
1918                                                                     (Block)block_result.ast(),
1919                                                                     Option.<FunctionalRef>some((FunctionalRef)op_result.ast()));
1920                TypeCheckerResult app_result_2 = new TypeCheckerResult(new_node, app_result_1.unwrap().second());
1921                Type app_type = app_result_1.unwrap().first();
1922                // We still must ensure the type of the application is a Boolean
1923                TypeCheckerResult bool_result = checkSubtype(app_type, Types.BOOLEAN, new_node,
1924                                                                             "Result of application of " + op + " to param and match expression must have type boolean, but had type " + app_type + ".");
1925                return Pair.make(block_type, TypeCheckerResult.compose(new_node, subtypeChecker, bool_result, app_result_2, match_result, block_result));
1926            }
1927            else {
1928                // error
1929                            String err = "No overloading of the operator " + op + " could be found for arguments of type " +
1930                param_type + " (Param Type)  and " + match_type + " (Match Type).";
1931                TypeCheckerResult e_r = new TypeCheckerResult(clause, TypeError.make(err, clause));
1932                return Pair.<Type,TypeCheckerResult>make(block_type, TypeCheckerResult.compose(clause, subtypeChecker, e_r, match_result, block_result));
1933            }
1934        }
1935    }
1936
1937
1938    @Override
1939    public TypeCheckerResult forCatch(Catch that) {
1940        // We have to pass the name down so it can be bound to each exn type in turn
1941        Id bound_name = that.getName();
1942        List<TypeCheckerResult> clause_results =
1943            recurOnCatchClausesWithIdToBind(that.getClauses(),bound_name);
1944        // Gather all the types of the catch clauses
1945        List<Type> clause_types = new ArrayList<Type>(clause_results.size());
1946        for( TypeCheckerResult r : clause_results ) {
1947            if( r.type().isSome() )
1948                clause_types.add(r.type().unwrap());
1949        }
1950        // resulting type is the join of those types
1951        Type result_type = subtypeChecker.join(clause_types);
1952
1953        Catch new_node = NodeFactory.makeCatch(NodeUtil.getSpan(that),
1954                that.getName(),
1955                (List<CatchClause>)TypeCheckerResult.astFromResults(clause_results));
1956
1957        return TypeCheckerResult.compose(new_node, result_type,
1958                subtypeChecker, clause_results).addNodeTypeEnvEntry(new_node, typeEnv);
1959    }
1960
1961    /**
1962     * Given the CatchClause and an Id, the Id will be bound to the exception type that the catch
1963     * clause declares to catch, and then its body will be type-checked.
1964     */
1965    private TypeCheckerResult forCatchClauseWithIdToBind(CatchClause that,
1966            Id id_to_bind) {
1967        TypeCheckerResult match_result = that.getMatchType().accept(this);
1968        // type must be an exception
1969        TypeCheckerResult is_exn_result = checkSubtype(that.getMatchType(), Types.EXCEPTION, that.getMatchType(),
1970                "Catch clauses must catch sub-types of Exception, but " + that.getMatchType() + " is not.");
1971
1972        // bind id and check the body
1973        LValue lval = NodeFactory.makeLValue(id_to_bind, that.getMatchType());
1974        TypeChecker extend_tc = this.extend(Collections.singletonList(lval));
1975        TypeCheckerResult body_result = that.getBody().accept(extend_tc);
1976
1977        // result has body type
1978        Option<Type> result_type = body_result.type();
1979
1980        CatchClause new_node = NodeFactory.makeCatchClause(NodeUtil.getSpan(that),
1981                (BaseType)match_result.ast(),
1982                (Block)body_result.ast());
1983
1984        return TypeCheckerResult.compose(new_node, result_type, subtypeChecker, match_result, is_exn_result, body_result);
1985    }
1986
1987    @Override
1988    public TypeCheckerResult forChainExpr(ChainExpr that) {
1989        TypeCheckerResult first_result = that.getFirst().accept(this);
1990        List<TypeCheckerResult> bool_exprs = new ArrayList<TypeCheckerResult>(that.getLinks().size());
1991        List<TypeCheckerResult> link_result = new ArrayList<TypeCheckerResult>(that.getLinks().size());
1992        List<TypeCheckerResult> temps_result = new ArrayList<TypeCheckerResult>(that.getLinks().size());
1993
1994        Expr prev = that.getFirst();
1995
1996        for(Link link : that.getLinks()) {
1997            FunctionalRef op = link.getOp();
1998            Expr next = link.getExpr();
1999
2000            link_result.add(link.accept(this));
2001
2002            // Check a temporary binary op, to see if the result is <: Boolean
2003            OpExpr tempOpExpr = ExprFactory.makeOpExpr(FortressUtil.spanTwo(prev,next),
2004                                                                   op, prev, next);
2005            TypeCheckerResult temp_op_result = tempOpExpr.accept(this);
2006            temps_result.add(temp_op_result);
2007
2008            if(temp_op_result.type().isSome()) {
2009                bool_exprs.add(this.checkSubtype(temp_op_result.type().unwrap(), Types.BOOLEAN, tempOpExpr));
2010            }
2011            prev = next;
2012        }
2013
2014        ChainExpr new_node = ExprFactory.makeChainExpr(NodeUtil.getSpan(that),
2015                NodeUtil.isParenthesized(that),
2016                Option.<Type>some(Types.BOOLEAN),
2017                (Expr)first_result.ast(),
2018                (List<Link>)TypeCheckerResult.astFromResults(link_result));
2019
2020        return TypeCheckerResult.compose(new_node, Types.BOOLEAN, subtypeChecker, first_result,
2021                TypeCheckerResult.compose(new_node, subtypeChecker, link_result),
2022                TypeCheckerResult.compose(new_node, subtypeChecker, bool_exprs),
2023                TypeCheckerResult.compose(new_node, subtypeChecker, temps_result));
2024    }
2025
2026    @Override
2027    public TypeCheckerResult forCharLiteralExpr(CharLiteralExpr that) {
2028        CharLiteralExpr new_node = ExprFactory.makeCharLiteralExpr(NodeUtil.getSpan(that),
2029                                                                           NodeUtil.isParenthesized(that),
2030                                                                           Option.<Type>some(Types.CHAR),
2031                                                                           that.getText(),
2032                                                                           that.getCharVal());
2033        return new TypeCheckerResult(new_node, Types.CHAR);
2034    }
2035
2036    @Override
2037    public TypeCheckerResult forComponentOnly(Component that,
2038                                                  TypeCheckerResult info,
2039                                                  TypeCheckerResult name_result,
2040                                                  List<TypeCheckerResult> imports_result,
2041                                                  List<TypeCheckerResult> decls_result,
2042                                                  List<TypeCheckerResult> exports_result) {
2043        Component new_comp =
2044                    NodeFactory.makeComponent(NodeUtil.getSpan(that),
2045                                              (APIName)name_result.ast(),
2046                                              (List<Import>)TypeCheckerResult.astFromResults(imports_result),
2047                                              (List<Decl>)TypeCheckerResult.astFromResults(decls_result),
2048                                              that.is_native(),
2049                                              (List<APIName>)TypeCheckerResult.astFromResults(exports_result));
2050
2051        return TypeCheckerResult.compose(new_comp,
2052                subtypeChecker,
2053                name_result,
2054                TypeCheckerResult.compose(new_comp, subtypeChecker, imports_result),
2055                TypeCheckerResult.compose(new_comp, subtypeChecker, exports_result),
2056                TypeCheckerResult.compose(new_comp, subtypeChecker, decls_result));
2057    }
2058
2059    @Override
2060    public TypeCheckerResult forContractOnly(Contract that,
2061                                                 TypeCheckerResult info,
2062            Option<List<TypeCheckerResult>> requires_result,
2063            Option<List<TypeCheckerResult>> ensures_result,
2064            Option<List<TypeCheckerResult>> invariants_result) {
2065        TypeCheckerResult result = new TypeCheckerResult(that);
2066
2067        // Check that each 'requires' expression is Boolean
2068        if (requires_result.isSome()) {
2069            for (TypeCheckerResult r : requires_result.unwrap()) {
2070                if (r.type().isNone()) continue;
2071                Type exprType = r.type().unwrap();
2072                result = TypeCheckerResult.compose(
2073                        that,
2074                        subtypeChecker,
2075                        result, checkSubtype(exprType,
2076                                Types.BOOLEAN,
2077                                r.ast(),
2078                                errorMsg("Attempt to use expression of type ", exprType,
2079                                        " in a 'requires' clause, instead of ",Types.BOOLEAN)));
2080            }
2081        }
2082
2083        Contract new_node = NodeFactory.makeContract(NodeUtil.getSpan(that),
2084                (Option<List<Expr>>)TypeCheckerResult.astFromResults(requires_result),
2085                (Option<List<EnsuresClause>>)TypeCheckerResult.astFromResults(ensures_result),
2086                (Option<List<Expr>>)TypeCheckerResult.astFromResults(invariants_result));
2087
2088        return TypeCheckerResult.compose(new_node,
2089                subtypeChecker,
2090                TypeCheckerResult.compose(new_node, subtypeChecker, requires_result),
2091                TypeCheckerResult.compose(new_node, subtypeChecker, ensures_result),
2092                TypeCheckerResult.compose(new_node, subtypeChecker, invariants_result), result);
2093    }
2094
2095    public TypeCheckerResult forBlock(Block that) {
2096            if ( that.isAtomicBlock() )
2097                return forAtomic(ExprFactory.makeBlock(NodeUtil.getSpan(that), NodeUtil.isParenthesized(that),
2098                                                       NodeUtil.getExprType(that), that.getLoc(),
2099                                                       false, that.isWithinDo(), that.getExprs()),
2100                                                 errorMsg("A 'spawn' expression must not occur inside",
2101                                                          "an 'atomic' do block."));
2102
2103            Option<TypeCheckerResult> loc_result_ = this.recurOnOptionOfExpr(that.getLoc());
2104            TypeCheckerResult loc_result = loc_result_.isNone() ? new TypeCheckerResult(that) :
2105                loc_result_.unwrap();
2106
2107            TypeCheckerResult region_result = new TypeCheckerResult(that);
2108            if (loc_result_.isSome() && loc_result_.unwrap().type().isSome()) {
2109                Type locType = loc_result_.unwrap().type().unwrap();
2110                region_result = checkSubtype(locType,
2111                                             Types.REGION,
2112                                             that.getLoc().unwrap(),
2113                                             errorMsg("Location of 'do' block must ",
2114                                                      "have type Region: ", locType));
2115            }
2116            List<TypeCheckerResult> exprs_result = this.recurOnListOfExpr(that.getExprs());
2117            // Type is type of last expression or void if none.
2118            Option<Type> result_type;
2119            List<Expr> es;
2120            List<TypeCheckerResult> body_void;
2121            if ( exprs_result.isEmpty() ) {
2122                result_type = Option.<Type>some(Types.VOID);
2123                es = Collections.<Expr>emptyList();
2124                body_void = Collections.<TypeCheckerResult>emptyList();
2125            } else {
2126                result_type = IterUtil.last(exprs_result).type();
2127                es = (List<Expr>)TypeCheckerResult.astFromResults(exprs_result);
2128                body_void = allVoidButLast(exprs_result,that.getExprs());
2129            }
2130            Block new_node = ExprFactory.makeBlock(NodeUtil.getSpan(that),
2131                                                   NodeUtil.isParenthesized(that),
2132                                                   result_type,
2133                                                   (Option<Expr>)TypeCheckerResult.astFromResult(loc_result_),
2134                                                   that.isAtomicBlock(), that.isWithinDo(),
2135                                                   (List<Expr>)TypeCheckerResult.astFromResults(exprs_result));
2136            return TypeCheckerResult.compose(new_node,
2137                                             result_type,
2138                                             subtypeChecker,
2139                                             TypeCheckerResult.compose(new_node, subtypeChecker, body_void),
2140                                             loc_result,
2141                                             region_result,
2142                                             TypeCheckerResult.compose(new_node, subtypeChecker, exprs_result));
2143    }
2144
2145    @Override
2146    public TypeCheckerResult forDoOnly(Do that, TypeCheckerResult exprType_result, List<TypeCheckerResult> fronts_result) {
2147        // Get union of all clauses' types
2148        List<Type> frontTypes = new ArrayList<Type>();
2149        for (TypeCheckerResult frontResult : fronts_result) {
2150            if (frontResult.type().isSome()) {
2151                frontTypes.add(frontResult.type().unwrap());
2152            }
2153        }
2154
2155        Type result_type = subtypeChecker.join(frontTypes);
2156        Do new_node = ExprFactory.makeDo(NodeUtil.getSpan(that),
2157                NodeUtil.isParenthesized(that),
2158                some(result_type),
2159                (List<Block>)TypeCheckerResult.astFromResults(fronts_result));
2160
2161        return TypeCheckerResult.compose(new_node, result_type, subtypeChecker, fronts_result);
2162    }
2163
2164    @Override
2165    public TypeCheckerResult forEnclosingFixity(EnclosingFixity that) {
2166        return new TypeCheckerResult(that);
2167    }
2168
2169    @Override
2170    public TypeCheckerResult forExit(Exit that) {
2171        assert (that.getTarget().isSome()); // Filled in by disambiguator
2172        Id labelName = that.getTarget().unwrap();
2173        Option<BindingLookup> b = typeEnv.binding(labelName);
2174        assert (that.getReturnExpr().isSome()); // Filled in by disambiguator
2175        if (b.isNone()) {
2176            TypeCheckerResult withResult = that.getReturnExpr().unwrap().accept(this);
2177            return TypeCheckerResult.compose(
2178                    that,
2179                    Types.BOTTOM,
2180                    subtypeChecker,
2181                    new TypeCheckerResult(that,
2182                            TypeError.make(errorMsg("Could not find 'label' expression with name: ",
2183                                    labelName),
2184                                    labelName)), withResult);
2185        }
2186        Type targetType = b.unwrap().getType().unwrap(null);
2187        if (!(targetType instanceof LabelType)) {
2188            TypeCheckerResult withResult = that.getReturnExpr().unwrap().accept(this);
2189            return TypeCheckerResult.compose(
2190                    that,
2191                    Types.BOTTOM,
2192                    subtypeChecker,
2193                    new TypeCheckerResult(that,
2194                            TypeError.make(errorMsg("Target of 'exit' expression is not a label name: ",
2195                                    labelName),
2196                                    labelName)), withResult);
2197        }
2198
2199        // Append the 'with' type to the list for this label
2200        TypeCheckerResult withResult = that.getReturnExpr().unwrap().accept(this);
2201        if (withResult.type().isNone()) {
2202            labelExitTypes.put(labelName, Option.<Set<Type>>none());
2203        } else {
2204            assert (labelExitTypes.get(labelName).isSome());
2205            labelExitTypes.get(labelName).unwrap().add(withResult.type().unwrap());
2206        }
2207
2208        Exit new_node = ExprFactory.makeExit(NodeUtil.getSpan(that),
2209                NodeUtil.isParenthesized(that),
2210                Option.<Type>some(Types.BOTTOM),
2211                that.getTarget(),
2212                some((Expr)withResult.ast()));
2213
2214        return TypeCheckerResult.compose(new_node, Types.BOTTOM, subtypeChecker, withResult);
2215    }
2216
2217    // Works for extremum expressions
2218    // case most < of ... end
2219    private TypeCheckerResult forExtremumOnly(CaseExpr that, TypeCheckerResult compare_result,
2220            List<Triple<CaseClause, TypeCheckerResult, TypeCheckerResult>> clauses_result) {
2221
2222        Iterable<Type> candidate_types =
2223            IterUtil.map(IterUtil.tripleSeconds(clauses_result), new Lambda<TypeCheckerResult,Type>(){
2224                public Type value(TypeCheckerResult arg0) { return arg0.type().unwrap(); }});
2225
2226        Iterable<Type> rhs_types =
2227            IterUtil.map(IterUtil.tripleThirds(clauses_result), new Lambda<TypeCheckerResult,Type>(){
2228                public Type value(TypeCheckerResult arg0) { return arg0.type().unwrap(); }});
2229
2230        Type union_of_candidate_types = this.subtypeChecker.join(candidate_types);
2231        Type total_op_order = Types.makeTotalOperatorOrder(union_of_candidate_types,
2232                                                                   (Op)that.getCompare().unwrap().getOriginalName());
2233
2234        TypeCheckerResult total_result = checkSubtype(union_of_candidate_types, total_op_order, that,
2235                "In an extremum expression, the union of all candidate types must be a subtype of " +
2236                "TotalOperatorOrder[union,<,<=,>=,>,"+that.getCompare().unwrap().getOriginalName()+"] but it is not. " +
2237                "The union is " + union_of_candidate_types);
2238
2239        Type result_type = subtypeChecker.join(rhs_types);
2240        CaseExpr new_node = ExprFactory.makeCaseExpr(NodeUtil.getSpan(that),
2241                NodeUtil.isParenthesized(that),
2242                some(result_type),
2243                that.getParam(), // implicitly NONE
2244                that.getCompare(),
2245                that.getEqualsOp(),
2246                that.getInOp(),
2247                CollectUtil.makeList(IterUtil.tripleFirsts(clauses_result)),
2248                that.getElseClause());
2249
2250        return TypeCheckerResult.compose(new_node, result_type, subtypeChecker, total_result, compare_result,
2251                TypeCheckerResult.compose(new_node, subtypeChecker, CollectUtil.makeList(IterUtil.tripleSeconds(clauses_result))),
2252                TypeCheckerResult.compose(new_node, subtypeChecker, CollectUtil.makeList(IterUtil.tripleThirds(clauses_result))));
2253    }
2254
2255    @Override
2256    public TypeCheckerResult forFieldRefOnly(FieldRef that, TypeCheckerResult exprType_result,
2257            TypeCheckerResult obj_result, TypeCheckerResult field_result) {
2258
2259        // We need the type of the receiver
2260        if( obj_result.type().isNone() ) {
2261            return TypeCheckerResult.compose(that, subtypeChecker, obj_result);
2262        }
2263        //check whether receiver can have fields
2264        Type recvr_type=obj_result.type().unwrap();
2265        TypeCheckerResult result;
2266        Option<Type> result_type;
2267
2268        List<TraitType> trait_types = traitTypesCallable(recvr_type);
2269
2270        if( !trait_types.isEmpty() ) {
2271            Option<Type> f_type = this.findFieldInTraitHierarchy(trait_types, that);
2272            if( f_type.isSome() ) {
2273                result = new TypeCheckerResult(that);
2274                result_type = f_type;
2275            }
2276            else {
2277                String no_field = "Field " + that.getField() + " could not be found in type" +
2278                recvr_type + ".";
2279                result = new TypeCheckerResult(that, TypeError.make(no_field, that));
2280                result_type = Option.none();
2281            }
2282        }
2283        else {
2284            String not_trait = "Receiver of field expression must be a trait or object, but "
2285                + recvr_type + " is neither.";
2286            result = new TypeCheckerResult(that, TypeError.make(not_trait, that));
2287            result_type = Option.none();
2288        }
2289
2290        FieldRef new_node = ExprFactory.makeFieldRef(NodeUtil.getSpan(that),
2291                NodeUtil.isParenthesized(that),
2292                result_type,
2293                (Expr)obj_result.ast(),
2294                that.getField());
2295
2296        return TypeCheckerResult.compose(new_node, result_type, this.subtypeChecker, obj_result, result);
2297    }
2298
2299    @Override
2300    public TypeCheckerResult forFloatLiteralExpr(FloatLiteralExpr that) {
2301        return new TypeCheckerResult(that, Types.FLOAT_LITERAL);
2302    }
2303
2304    @Override
2305    public TypeCheckerResult forFnDecl(FnDecl that) {
2306                if (NodeUtil.getBody(that).isNone())
2307                    return super.forFnDecl(that);
2308
2309        TypeChecker newChecker = this.extend(NodeUtil.getStaticParams(that), NodeUtil.getParams(that), NodeUtil.getWhereClause(that));
2310
2311        TypeCheckerResult result = new TypeCheckerResult(that);
2312        TypeCheckerResult contractResult;
2313        Option<Contract> contract;
2314        if ( NodeUtil.getContract(that).isSome() ) {
2315            contractResult = NodeUtil.getContract(that).unwrap().accept(newChecker);
2316            contract = Option.some((Contract)contractResult.ast());
2317        } else {
2318            contractResult = result;
2319            contract = Option.<Contract>none();
2320        }
2321        TypeCheckerResult bodyResult = NodeUtil.getBody(that).unwrap().accept(newChecker);
2322
2323        if( !contractResult.isSuccessful() || !bodyResult.isSuccessful() ) {
2324            return TypeCheckerResult.compose(that, subtypeChecker, result, bodyResult, contractResult);
2325        }
2326
2327
2328
2329        Option<Type> returnType = NodeUtil.getReturnType(that);
2330        if (bodyResult.type().isSome()) {
2331            Type bodyType = bodyResult.type().unwrap();
2332            if (returnType.isNone()) {
2333                returnType = wrap(bodyType);
2334            }
2335
2336//                         System.err.println("Location: " + NodeUtil.getSpan(that));
2337//                         System.err.println("body type: " + bodyType.getClass() + ":" + bodyType);
2338//                         if (bodyType instanceof TraitType) {
2339//                             System.err.println("name: " + ((TraitType)bodyType).getName());
2340//                             System.err.println("args:" + ((TraitType)bodyType).getArgs());
2341//                         }
2342//                         System.err.println("return type: " + returnType.unwrap().getClass() + ":" + returnType.unwrap());
2343//                         if (returnType.unwrap() instanceof TraitType) {
2344//                             System.err.println("name: " + ((TraitType)returnType.unwrap()).getName());
2345//                             System.err.println("args:" + ((TraitType)returnType.unwrap()).getArgs());
2346//                         }
2347//                         System.err.println("equal? " + bodyType.equals(returnType.unwrap()));
2348            result = newChecker.checkSubtype(bodyType,
2349                                                         returnType.unwrap(),
2350                                                         that,
2351                                                         errorMsg("Function body has type ", bodyType, ", but ",
2352                                                                  "declared return type is ", returnType.unwrap()));
2353        }
2354
2355        FnDecl new_node = NodeFactory.makeFnDecl(NodeUtil.getSpan(that),
2356                                                         NodeUtil.getMods(that),
2357                                                         NodeUtil.getName(that),
2358                                                         NodeUtil.getStaticParams(that),
2359                                                         NodeUtil.getParams(that),
2360                                                         returnType,
2361                                                         NodeUtil.getThrowsClause(that),
2362                                                         NodeUtil.getWhereClause(that),
2363                                                         contract,
2364                                                         Option.<Expr>some((Expr)bodyResult.ast()));
2365        return TypeCheckerResult.compose(new_node, subtypeChecker, contractResult,
2366                bodyResult, result)
2367                .addNodeTypeEnvEntry(new_node, typeEnv)
2368                .removeStaticParamsFromScope(NodeUtil.getStaticParams(that));
2369    }
2370
2371    @Override
2372    public TypeCheckerResult forFnExpr(FnExpr that) {
2373        // Fn expressions have arrow type. They cannot have static arguments.
2374        // They cannot have where clauses.
2375
2376        Option<TypeCheckerResult> returnType_result = recurOnOptionOfType(NodeUtil.getReturnType(that));
2377
2378        List<TypeCheckerResult> all_results = new ArrayList<TypeCheckerResult>();
2379
2380        Option<List<TypeCheckerResult>> throwsClause_result = recurOnOptionOfListOfBaseType(NodeUtil.getThrowsClause(that));
2381
2382
2383        List<TypeCheckerResult> params_result = recurOnListOfParam(NodeUtil.getParams(that));
2384        // Grab bindings and introduce them. For the time-being, they must have types.
2385        TypeChecker extended_checker = this;
2386        for( Param p : NodeUtil.getParams(that) ) {
2387            extended_checker = extended_checker.extend(p);
2388        }
2389        TypeCheckerResult body_result = that.getBody().accept(extended_checker);
2390        // Get all results together
2391        all_results.addAll(params_result);
2392        all_results.add(body_result);
2393        //get domain type
2394        List<Type> dlist = new ArrayList<Type>();
2395        Boolean varargs=false;
2396        for(Param p: NodeUtil.getParams(that)){
2397                    if( ! NodeUtil.isVarargsParam(p) ){
2398                        if(p.getIdType().isSome()){
2399                            dlist.add(p.getIdType().unwrap());
2400                        }
2401                        else{
2402                            NI.nyi();
2403                        }
2404                    } else {
2405                        dlist.add(p.getVarargsType().unwrap());
2406                        varargs=true;
2407                    }
2408        }
2409        TupleType domain;
2410        if(varargs){
2411            Type var = dlist.remove(dlist.size()-1);
2412            domain = NodeFactory.makeTupleType(NodeUtil.getSpan(that), false, dlist,
2413                                                           Option.<Type>some(var),
2414                                                           Collections.<KeywordType>emptyList());
2415        }
2416        else{
2417            domain = NodeFactory.makeTupleType(NodeUtil.getSpan(that), dlist);
2418        }
2419
2420        if( returnType_result.isSome() )
2421            all_results.add(returnType_result.unwrap());
2422        if( throwsClause_result.isSome() )
2423            all_results.addAll(throwsClause_result.unwrap());
2424
2425        // If return type is given, we check that it is a supertype of the inferred super-type
2426        // and we use it, otherwise the return type is what we infer.
2427        Type return_type;
2428        if( body_result.type().isNone() ) {
2429            // We've got errors in the body
2430            return_type = Types.BOTTOM;
2431        }
2432        else if( NodeUtil.getReturnType(that).isSome() ) {
2433            return_type = NodeUtil.getReturnType(that).unwrap();
2434            TypeCheckerResult subtype_result =
2435                this.checkSubtype(body_result.type().unwrap(), return_type, that.getBody(),
2436                        "Type of body of Fn expression must be a subtype of declared return type ("+
2437                        return_type +") but is " + body_result.type().unwrap() +".");
2438            all_results.add(subtype_result);
2439        }
2440        else {
2441            return_type = body_result.type().unwrap();
2442        }
2443
2444        // All throws types must be a subtype of exception
2445        if( NodeUtil.getThrowsClause(that).isSome() ) {
2446            for( BaseType exn : NodeUtil.getThrowsClause(that).unwrap() ) {
2447                all_results.add(this.checkSubtype(exn, Types.CHECKED_EXCEPTION, that,
2448                        "Types in throws clause must be subtypes of CheckedException, but "+
2449                        exn + " is not."));
2450            }
2451        }
2452
2453        ArrowType arr = NodeFactory.makeArrowType(FortressUtil.spanTwo(domain, return_type),
2454                domain, return_type);
2455        FnExpr new_node = ExprFactory.makeFnExpr(NodeUtil.getSpan(that),
2456                NodeUtil.isParenthesized(that),
2457                NodeUtil.getExprType(that),
2458                NodeUtil.getName(that),
2459                NodeUtil.getStaticParams(that),
2460                NodeUtil.getParams(that),
2461                (Option<Type>)TypeCheckerResult.astFromResult(returnType_result),
2462                NodeUtil.getWhereClause(that),
2463                (Option<List<BaseType>>)TypeCheckerResult.astFromResults(throwsClause_result),
2464                (Expr)body_result.ast() );
2465
2466        return TypeCheckerResult.compose(new_node, arr,
2467                this.subtypeChecker, all_results).addNodeTypeEnvEntry(new_node, typeEnv);
2468    }
2469
2470    @Override
2471    public TypeCheckerResult forFnRefOnly(final FnRef that, TypeCheckerResult exprType_result,
2472                                              List<TypeCheckerResult> staticArgs_result,
2473                                              TypeCheckerResult originalName_result,
2474                                              List<TypeCheckerResult> fns_result,
2475                                              Option<List<TypeCheckerResult>> overloadings_result,
2476                                              Option<TypeCheckerResult> type_result) {
2477
2478        // Could we give a type to each of our fns?
2479        for( TypeCheckerResult r : fns_result ) {
2480            if( !r.isSuccessful() )
2481                return TypeCheckerResult.compose(that, subtypeChecker, fns_result);
2482        }
2483
2484        // Gather types of all overloadings
2485        List<Type> overloaded_types = CollectUtil.makeList(IterUtil.map(fns_result, new Lambda<TypeCheckerResult, Type>() {
2486            public Type value(TypeCheckerResult arg0) { return arg0.type().unwrap(); }}));
2487
2488        Node new_node;
2489        Option<Type> type;
2490        ConstraintFormula constraints=ConstraintFormula.TRUE;
2491
2492        // if no static args are provided, and we have several overloadings, we
2493        // will create a FnRef with the overloadings field set
2494        if( that.getStaticArgs().isEmpty()  && TypesUtil.overloadingRequiresStaticArgs(overloaded_types) ) {
2495            // if there is an overloading that requires static args, and we don't have any, we'll
2496            // create a _FnInstantitedOverloading
2497            List<FunctionalRef> fn_overloadings = new ArrayList<FunctionalRef>();
2498            List<Type> arrow_types = new ArrayList<Type>();
2499            ConstraintFormula accumulated_constraints = ConstraintFormula.TRUE;
2500            for( Type overloaded_type : overloaded_types ) {
2501                for( Type overloading : TypesUtil.conjuncts(overloaded_type) ) {
2502                    // If type needs static args, create inference vars, and apply to the signature
2503                    Option<Pair<Pair<Type,ConstraintFormula>,List<StaticArg>>> new_type_and_args_ =
2504                        overloading.accept(new TypeAbstractVisitor<Option<Pair<Pair<Type,ConstraintFormula>,List<StaticArg>>>>() {
2505                            @Override
2506                            public Option<Pair<Pair<Type,ConstraintFormula>, List<StaticArg>>> forArrowType(
2507                                    ArrowType gen_arr_type) {
2508                                // If the arrow type is generic, it needs static args, so make up inference variables
2509                                List<StaticArg> new_args =
2510                                    TypesUtil.staticArgsFromTypes(NodeFactory.make_InferenceVarTypes(NodeUtil.getSpan(that), NodeUtil.getStaticParams(gen_arr_type).size()));
2511                                Option<Pair<Type,ConstraintFormula>> instantiated_type = TypesUtil.applyStaticArgsIfPossible(gen_arr_type, new_args, TypeChecker.this.subtypeChecker);
2512
2513                                if( instantiated_type.isNone() )
2514                                    return none();
2515                                else
2516                                    return some(Pair.make(instantiated_type.unwrap(), new_args));
2517                            }
2518
2519                            @Override
2520                            public Option<Pair<Pair<Type,ConstraintFormula>, List<StaticArg>>> forType(Type that) {
2521                                // any other sort of Type does not get rewritten, and has no args
2522                                return some(Pair.make(Pair.make(that, ConstraintFormula.TRUE), Collections.<StaticArg>emptyList()));
2523                            }
2524                        });
2525
2526                    // For now, we don't consider overloadings that require inference of bools/nats/ints
2527                    if( new_type_and_args_.isNone() ) continue;
2528                    Type new_type = new_type_and_args_.unwrap().first().first();
2529                    List<StaticArg> new_args = new_type_and_args_.unwrap().second();
2530                    // create new FnRef for this application of static params
2531                    FnRef fn_ref = ExprFactory.makeFnRef(NodeUtil.getSpan(that),
2532                                                                             NodeUtil.isParenthesized(that),
2533                                                                             some(new_type),
2534                                                                             new_args,
2535                                                                             that.getLexicalDepth(),
2536                                                                             that.getOriginalName(),
2537                                                                             that.getNames(),
2538                                                                             that.getOverloadings(),
2539                                                                             Option.<Type>none());
2540                    fn_overloadings.add(ExprFactory.make_RewriteFnRefOverloading(NodeUtil.getSpan(that), fn_ref, new_type));
2541                    arrow_types.add(new_type);
2542                    accumulated_constraints=accumulated_constraints.and(new_type_and_args_.unwrap().first().second(),this.subtypeChecker.new SubtypeHistory());
2543                }
2544            }
2545
2546            type = (arrow_types.isEmpty()) ?
2547                    Option.<Type>none() :
2548                        Option.<Type>some(NodeFactory.makeIntersectionType(NodeFactory.makeSetSpan("impossible", arrow_types), arrow_types));
2549                    constraints = accumulated_constraints;
2550                    new_node = ExprFactory.makeFnRef(NodeUtil.getSpan(that),
2551                                                                         NodeUtil.isParenthesized(that),
2552                                                                         type,
2553                                                                         that.getStaticArgs(),
2554                                                                         that.getLexicalDepth(),
2555                                                                         that.getOriginalName(),
2556                                                                         that.getNames(),
2557                                                                         Option.<List<FunctionalRef>>some(fn_overloadings),
2558                                                                         Option.<Type>none());
2559        }
2560        else {
2561            // otherwise, we just operate according to the normal procedure, apply args or none were necessary
2562            List<Type> arrow_types = new ArrayList<Type>();
2563            ConstraintFormula accumulated_constraints=ConstraintFormula.TRUE;
2564            for( Type ty : overloaded_types ) {
2565                Option<Pair<Type,ConstraintFormula>> instantiated_type = TypesUtil.applyStaticArgsIfPossible(ty, that.getStaticArgs(), this.subtypeChecker);
2566                if( instantiated_type.isSome() ){
2567                    arrow_types.add(instantiated_type.unwrap().first());
2568                    accumulated_constraints=accumulated_constraints.and(instantiated_type.unwrap().second(),this.subtypeChecker.new SubtypeHistory());
2569                }
2570            }
2571            if (arrow_types.isEmpty()) {
2572                type = Option.<Type>none();
2573            } else if (arrow_types.size() == 1) { // special case to allow for simpler types
2574                type = Option.<Type>some(arrow_types.get(0));
2575            } else {
2576                type = Option.<Type>some(NodeFactory.makeIntersectionType(NodeFactory.makeSetSpan("impossible", arrow_types), arrow_types));
2577            }
2578
2579            type = (arrow_types.isEmpty()) ?
2580                    Option.<Type>none() :
2581                        Option.<Type>some(NodeFactory.makeIntersectionType(NodeFactory.makeSetSpan("impossible", arrow_types), arrow_types));
2582                    constraints = accumulated_constraints;
2583                    new_node = ExprFactory.makeFnRef(NodeUtil.getSpan(that),
2584                                                                         NodeUtil.isParenthesized(that),
2585                                                                         type,
2586                                                                         that.getStaticArgs(),
2587                                                                         that.getLexicalDepth(),
2588                                                                         that.getOriginalName(),
2589                                                                         that.getNames(),
2590                                                                         Option.<List<FunctionalRef>>none(),
2591                                                                         Option.<Type>none());
2592        }
2593
2594        return TypeCheckerResult.compose(new_node, type, subtypeChecker,
2595                TypeCheckerResult.compose(new_node, subtypeChecker, fns_result),
2596                TypeCheckerResult.compose(new_node, subtypeChecker, staticArgs_result),
2597                new TypeCheckerResult(new_node, constraints));
2598    }
2599
2600    @Override
2601    public TypeCheckerResult forFor(For that) {
2602        Pair<List<TypeCheckerResult>,List<LValue>> pair = recurOnListsOfGeneratorClauseBindings(that.getGens());
2603        Option<TypeCheckerResult> type_result = recurOnOptionOfType(NodeUtil.getExprType(that));
2604        TypeChecker extend = this.extend(pair.second());
2605        TypeCheckerResult body_result = that.getBody().accept(extend);
2606        return forForOnly(that, type_result, pair.first(), body_result);
2607    }
2608
2609    public TypeCheckerResult forForOnly(For that, Option<TypeCheckerResult> exprType_result, List<TypeCheckerResult> gens_result, TypeCheckerResult body_result) {
2610
2611        if( body_result.type().isNone() ) {
2612            return TypeCheckerResult.compose(that, Types.VOID, subtypeChecker, body_result,
2613                    TypeCheckerResult.compose(that, Types.VOID, subtypeChecker, gens_result));
2614        }
2615
2616        // Body must be a void type
2617        String err = "Body type of a for loop must have type VOID but has type " + body_result.type().unwrap();
2618        TypeCheckerResult body_void = this.checkSubtype(body_result.type().unwrap(), Types.VOID, that.getBody(), err);
2619
2620        For for_ = ExprFactory.makeFor(NodeUtil.getSpan(that),
2621                NodeUtil.isParenthesized(that),
2622                Option.<Type>some(Types.VOID),
2623                (List<GeneratorClause>)TypeCheckerResult.astFromResults(gens_result),
2624                (Block)body_result.ast());
2625
2626        TypeCheckerResult result = TypeCheckerResult.compose(for_, subtypeChecker, body_void, body_result,
2627                TypeCheckerResult.compose(for_, subtypeChecker, gens_result)).addNodeTypeEnvEntry(for_, typeEnv);
2628
2629        return TypeCheckerResult.compose(for_, Types.VOID, subtypeChecker, result);
2630    }
2631
2632    private Pair<TypeCheckerResult, List<LValue>> forGeneratorClauseGetBindings(GeneratorClause that,
2633            boolean mustBeCondition) {
2634        TypeCheckerResult init_result = that.getInit().accept(this);
2635        final Pair<TypeCheckerResult, List<LValue>> p = forGeneratorClauseOnlyGetBindings(that, init_result, mustBeCondition);
2636
2637        if( postInference ) {
2638            Boolean ok = true;
2639            List<LValue> closed_binds = new ArrayList<LValue>();
2640            for(LValue b : p.second()){
2641                Pair<Boolean,Node> temp = TypesUtil.closeConstraints(b, subtypeChecker, p.first());
2642                closed_binds.add((LValue)temp.second());
2643                ok&=temp.first();
2644            }
2645            TypeCheckerResult successful = new TypeCheckerResult(that);
2646            if(!ok){
2647                String err = "Couldn't close binding.";
2648                successful = new TypeCheckerResult(that,TypeError.make(err,that));
2649            }
2650            return Pair.make(TypeCheckerResult.compose(that, this.subtypeChecker, p.first(), successful), closed_binds);
2651        }
2652        else {
2653            return p;
2654        }
2655    }
2656
2657    private Pair<TypeCheckerResult, List<LValue>> forGeneratorClauseOnlyGetBindings(GeneratorClause that,
2658            TypeCheckerResult init_result, boolean mustBeCondition) {
2659
2660        if( that.getBind().isEmpty()) {
2661            GeneratorClause new_node = NodeFactory.makeGeneratorClause(NodeUtil.getSpan(that),
2662                    that.getBind(),
2663                    (Expr)init_result.ast());
2664
2665
2666            // If bindings are empty, then init_result must be of type boolean, a filter, 13.14
2667                        TypeCheckerResult bool_result;
2668
2669                        if (init_result.type().isNone()) {
2670                            String err = "Filter expressions in generator clauses must have type boolean, but " +
2671                                that.getInit() + " was not well typed.";
2672                            bool_result = new TypeCheckerResult(that.getInit(), TypeError.make(err, that.getInit()));
2673                        } else {
2674                            bool_result =
2675                this.checkSubtype(init_result.type().unwrap(), Types.BOOLEAN, that.getInit(),
2676                        "Filter expressions in generator clauses must have type " + Types.BOOLEAN + ", but " +
2677                        that.getInit() + " had type " + init_result.type().unwrap() + ".");
2678                        }
2679            return Pair.make(TypeCheckerResult.compose(new_node, subtypeChecker, init_result, bool_result),
2680                    Collections.<LValue>emptyList());
2681        }
2682
2683        // Otherwise, we may actually have bindings!
2684        int bindings_count = that.getBind().size();
2685
2686        List<LValue> result_bindings;
2687        Type lhstype;
2688        // Now create the bindings
2689
2690        if( bindings_count == 1 ){
2691            // Just one binding
2692            lhstype = NodeFactory.make_InferenceVarType(NodeUtil.getSpan(that.getBind().get(0)));
2693            LValue lval = NodeFactory.makeLValue(that.getBind().get(0), lhstype);
2694            result_bindings = Collections.singletonList(lval);
2695        }
2696        else {
2697            // Because generator_type is almost certainly a _InferenceVar, we have to declare a new tuple
2698            // that is the size of the binding and declare one to be a sub-type of the other.
2699            List<Type> inference_vars = new ArrayList<Type>(bindings_count);
2700            for( int i = 0; i<bindings_count;i++ ) {
2701                inference_vars.add(NodeFactory.make_InferenceVarType(NodeUtil.getSpan(that.getBind().get(i))));
2702            }
2703            lhstype=Types.makeTuple(inference_vars);
2704            // Now just create the lvalues with the newly created inference variable type
2705            Iterator<Id> id_iter = that.getBind().iterator();
2706            result_bindings = new ArrayList<LValue>(bindings_count);
2707            for( Type inference_var : inference_vars ) {
2708                result_bindings.add(NodeFactory.makeLValue(id_iter.next(), inference_var));
2709            }
2710        }
2711
2712        TypeCheckerResult this_result = TypeCheckerResult.compose(that, subtypeChecker, init_result);
2713        GeneratorClause new_node=that;
2714        if(init_result.type().isSome()){
2715            Type init_type = init_result.type().unwrap();
2716            String type_err_msg = "Init expression of generator must be a sub-type of " +
2717            (mustBeCondition ? "Condition" : "Generator") +
2718            " but is type " + init_result.type().unwrap() + ".";
2719            // Get the type of the Generator
2720
2721            Pair<TypeCheckerResult, Type> generator_pair = mustBeCondition ? this.getConditionType(init_type, that.getInit(), type_err_msg) :
2722                this.getGeneratorType(init_type, that.getInit(), type_err_msg);
2723
2724            Type generator_type = generator_pair.second();
2725            String tup_err_msg = "If more than one variable is bound in a generator, generator must have tuple type "+
2726            "but " + that.getInit() + " does not or has different number of arguments.";
2727            TypeCheckerResult subtype=this.checkSubtype(lhstype, generator_type, that.getInit(),tup_err_msg);
2728            TypeCheckerResult suptype=this.checkSubtype(generator_type, lhstype, that.getInit(),tup_err_msg);
2729            this_result = TypeCheckerResult.compose(that, subtypeChecker, suptype, subtype, generator_pair.first());
2730            new_node = NodeFactory.makeGeneratorClause(NodeUtil.getSpan(that),
2731                    that.getBind(),
2732                    (Expr)init_result.ast());
2733        }
2734        return Pair.make(TypeCheckerResult.compose(new_node, subtypeChecker, this_result, init_result), result_bindings);
2735    }
2736
2737    @Override
2738    public TypeCheckerResult forId(Id name) {
2739        Option<APIName> apiName = name.getApiName();
2740        if (apiName.isSome()) {
2741            APIName api = apiName.unwrap();
2742            TypeEnv apiTypeEnv = returnTypeEnvForApi(api);
2743
2744            Option<Type> type = apiTypeEnv.type(name);
2745            if (type.isSome()) {
2746                Type _type = type.unwrap();
2747                if (_type instanceof NamedType) { // Do we need to qualify?
2748                    NamedType _namedType = (NamedType)_type;
2749
2750                    // Type was declared in that API, so it's not qualified;
2751                    // prepend it with the API.
2752                    if (_namedType.getName().getApiName().isNone()) {
2753                        _type = NodeFactory.makeNamedType(api, (NamedType) type.unwrap());
2754                    }
2755                }
2756                return new TypeCheckerResult(name, _type);
2757            } else {
2758                // Operators are never qualified in source code, so if 'name' is qualified and not
2759                // found, it must be a Id, not a Op.
2760                StaticError error = TypeError.make(errorMsg("Attempt to reference unbound variable: ", name),
2761                        name);
2762                return new TypeCheckerResult(name, error);
2763            }
2764        }
2765        Option<Type> type = typeEnv.type(name);
2766        if (type.isSome()) {
2767            Type _type = type.unwrap();
2768            if (_type instanceof LabelType) { // then name must be an Id
2769                return new TypeCheckerResult(name, makeLabelNameError((Id)name));
2770            } else {
2771                return new TypeCheckerResult(name, _type);
2772            }
2773        } else {
2774            StaticError error;
2775            error = TypeError.make(errorMsg("Variable '", name, "' not found."),
2776                    name);
2777            return new TypeCheckerResult(name, error);
2778        }
2779    }
2780
2781    // This method not only typechecks an IfClause, but also returns the type of that clause's
2782    // body. We don't include this in the TypeCheckerResult because an IfClause is not an Expr
2783    // and we didn't want to confuse things by giving it a type.
2784    private Pair<TypeCheckerResult, Option<Type>> forIfClauseWithType(IfClause that) {
2785        // For generalized 'if' we must introduce new bindings.
2786        Pair<TypeCheckerResult, List<LValue>> result_and_binds =
2787            this.forGeneratorClauseGetBindings(that.getTestClause(), true);
2788
2789        // Destruct result
2790        TypeCheckerResult test_result = result_and_binds.first();
2791        List<LValue> bindings = result_and_binds.second();
2792
2793        // Check body with new bindings
2794        TypeChecker tc_with_new_bindings = this.extend(bindings);
2795        TypeCheckerResult body_result = that.getBody().accept(tc_with_new_bindings);
2796        //return forIfClauseOnly(that, test_result, body_result);
2797
2798        Option<Type> result_type = body_result.type();
2799        IfClause new_node = NodeFactory.makeIfClause(NodeUtil.getSpan(that),
2800                (GeneratorClause)test_result.ast(),
2801                (Block)body_result.ast());
2802
2803        TypeCheckerResult tcr = TypeCheckerResult.compose(new_node,
2804                subtypeChecker, test_result, body_result).addNodeTypeEnvEntry(new_node, typeEnv);
2805
2806        return Pair.make(tcr, result_type);
2807    }
2808
2809    @Override
2810    public TypeCheckerResult forIf(If that) {
2811
2812        Option<TypeCheckerResult> elseClause_result = this.recurOnOptionOfBlock(that.getElseClause());
2813        List<Pair<TypeCheckerResult,Option<Type>>> clauses_result_ = new ArrayList<Pair<TypeCheckerResult,Option<Type>>>(that.getClauses().size());
2814        for( IfClause clause : that.getClauses() ) {
2815            clauses_result_.add(this.forIfClauseWithType(clause));
2816        }
2817        List<TypeCheckerResult> clauses_result = CollectUtil.makeList(IterUtil.pairFirsts(clauses_result_));
2818        List<Option<Type>> clause_types_ = CollectUtil.makeList(IterUtil.pairSeconds(clauses_result_));
2819
2820        Type result_type;
2821        List<TypeCheckerResult> clauses_void;
2822
2823        if (elseClause_result.isSome()) {
2824            List<Type> clause_types = new ArrayList<Type>(that.getClauses().size());
2825            TypeCheckerResult elseResult = elseClause_result.unwrap();
2826
2827            // Get union of all clauses' types
2828            for (Option<Type> clause_type_ : clause_types_) {
2829                if (clause_type_.isSome()) {
2830                    clause_types.add(clause_type_.unwrap());
2831                }
2832            }
2833            if (elseResult.type().isSome()) {
2834                clause_types.add(elseResult.type().unwrap());
2835            }
2836            clauses_void = Collections.emptyList();
2837            result_type = subtypeChecker.join(clause_types);
2838        } else {
2839            // Check that each if/elif clause has void type
2840            clauses_void = new ArrayList<TypeCheckerResult>(that.getClauses().size());
2841            for (Option<Type> clause_type_ : clause_types_) {
2842                if ( clause_type_.isSome() ) {
2843                    Type clause_type = clause_type_.unwrap();
2844                    clauses_void.add(checkSubtype(clause_type, Types.VOID, that,
2845                            errorMsg("An 'if' clause without corresponding 'else' has type ",
2846                                    clause_type, " instead of type ()")));
2847                }
2848            }
2849            result_type = Types.VOID;
2850        }
2851
2852        TypeCheckerResult elseClause_result_ = elseClause_result.isNone() ? new TypeCheckerResult(that) : elseClause_result.unwrap();
2853
2854        If new_node = ExprFactory.makeIf(NodeUtil.getSpan(that),
2855                NodeUtil.isParenthesized(that),
2856                some(result_type),
2857                (List<IfClause>)TypeCheckerResult.astFromResults(clauses_result),
2858                (Option<Block>)TypeCheckerResult.astFromResult(elseClause_result));
2859
2860        return TypeCheckerResult.compose(new_node, result_type, subtypeChecker,
2861                TypeCheckerResult.compose(new_node, subtypeChecker, clauses_result),
2862                TypeCheckerResult.compose(new_node, subtypeChecker, clauses_void),
2863                elseClause_result_);
2864    }
2865
2866    @Override
2867    public TypeCheckerResult forImportStar(ImportStar that) {
2868        return new TypeCheckerResult(that);
2869    }
2870
2871    @Override
2872    public TypeCheckerResult forInFixity(InFixity that) {
2873        return new TypeCheckerResult(that);
2874    }
2875
2876    @Override
2877    public TypeCheckerResult forIntArgOnly(IntArg that,
2878                                               TypeCheckerResult info,
2879                                               TypeCheckerResult val_result) {
2880        return new TypeCheckerResult(that);
2881    }
2882
2883    @Override
2884    public TypeCheckerResult forIntLiteralExpr(IntLiteralExpr that) {
2885        IntLiteralExpr new_node = ExprFactory.makeIntLiteralExpr(NodeUtil.getSpan(that),
2886                                                                         NodeUtil.isParenthesized(that),
2887                                                                         Option.<Type>some(Types.INT_LITERAL),
2888                                                                         that.getText(), that.getIntVal());
2889        return new TypeCheckerResult(new_node, Types.INT_LITERAL);
2890    }
2891
2892    @Override
2893    public TypeCheckerResult forLabel(Label that) {
2894
2895        // Make sure this label isn't already bound
2896        Option<BindingLookup> b = typeEnv.binding(that.getName());
2897        if (b.isSome()) {
2898            TypeCheckerResult bodyResult = that.getBody().accept(this);
2899            return TypeCheckerResult.compose(that,
2900                    subtypeChecker,
2901                    new TypeCheckerResult(that, TypeError.make(errorMsg("Cannot use an existing identifier ",
2902                            "for a 'label' expression: ",
2903                            that.getName()),
2904                            that)), bodyResult);
2905        }
2906
2907        // Check for nested label of same name
2908        if (labelExitTypes.containsKey(that.getName())) {
2909            TypeCheckerResult bodyResult = that.getBody().accept(this);
2910            return TypeCheckerResult.compose(that,
2911                    subtypeChecker,
2912                    new TypeCheckerResult(that, TypeError.make(errorMsg("Name of 'label' expression ",
2913                            "already in scope: ", that.getName()),
2914                            that)), bodyResult);
2915        }
2916
2917        // Initialize the set of exit types
2918        labelExitTypes.put(that.getName(), some((Set<Type>)new HashSet<Type>()));
2919
2920        // Extend the checker with this label name in the type env
2921        TypeChecker newChecker = this.extend(Collections.singletonList(NodeFactory.makeLValue(that.getName(), Types.LABEL)));
2922        TypeCheckerResult bodyResult = that.getBody().accept(newChecker);
2923
2924        // If the body was typed, union all the exit types with it.
2925        // If any exit type is none, then don't type this label.
2926        Option<Type> labelType = none();
2927        if (bodyResult.type().isSome()) {
2928            Option<Set<Type>> exitTypes = labelExitTypes.get(that.getName());
2929            if (exitTypes.isSome()) {
2930                Set<Type> _exitTypes = exitTypes.unwrap();
2931                _exitTypes.add(bodyResult.type().unwrap());
2932                labelType = some(subtypeChecker.join(_exitTypes));
2933            }
2934        }
2935
2936        // Destroy the mappings for this label
2937        labelExitTypes.remove(that.getName());
2938
2939        Label new_node = ExprFactory.makeLabel(NodeUtil.getSpan(that),
2940                NodeUtil.isParenthesized(that),
2941                labelType,
2942                that.getName(),
2943                (Block)bodyResult.ast());
2944
2945        return TypeCheckerResult.compose(new_node, labelType,
2946                subtypeChecker, bodyResult).addNodeTypeEnvEntry(new_node, typeEnv);
2947    }
2948
2949    @Override
2950    public TypeCheckerResult forLetFn(LetFn that) {
2951        Relation<IdOrOpOrAnonymousName, FnDecl> fnDefs = new IndexedRelation<IdOrOpOrAnonymousName, FnDecl>(false);
2952        for (FnDecl fnDef : that.getFns()) {
2953            fnDefs.add(NodeUtil.getName(fnDef), fnDef);
2954        }
2955
2956        TypeChecker newChecker = this.extendWithFnDecls(fnDefs);
2957        List<TypeCheckerResult> fn_results = newChecker.recurOnListOfFnDecl(that.getFns());
2958
2959        // A LetFn is like a let. It has a body, and it's type is the type of the body
2960        List<TypeCheckerResult> body_results = newChecker.recurOnListOfExpr(that.getBody());
2961        List<TypeCheckerResult> body_void = newChecker.allVoidButLast(body_results, that.getBody());
2962        Option<Type> body_type = that.getBody().size() == 0 ?
2963                Option.<Type>some(Types.VOID) :
2964                    body_results.get(body_results.size()-1).type();
2965
2966                LetFn new_node = ExprFactory.makeLetFn(NodeUtil.getSpan(that),
2967                        NodeUtil.isParenthesized(that),
2968                        body_type,
2969                        (List<Expr>)TypeCheckerResult.astFromResults(body_results),
2970                        (List<FnDecl>)TypeCheckerResult.astFromResults(fn_results));
2971
2972                TypeCheckerResult result =
2973                    TypeCheckerResult.compose(new_node, body_type, subtypeChecker,
2974                            TypeCheckerResult.compose(new_node, subtypeChecker, body_results),
2975                            TypeCheckerResult.compose(new_node, subtypeChecker, body_void),
2976                            TypeCheckerResult.compose(new_node, subtypeChecker, fn_results));
2977
2978                return result.addNodeTypeEnvEntry(new_node, typeEnv);
2979    }
2980
2981    @Override
2982    public TypeCheckerResult forLocalVarDecl(LocalVarDecl that) {
2983        TypeCheckerResult result = new TypeCheckerResult(that);
2984
2985        // Create type for LHS
2986        Type lhs_type =
2987            Types.MAKE_TUPLE.value(IterUtil.map(that.getLhs(), new Lambda<LValue,Type>(){
2988                public Type value(LValue arg0) { return getTypeOfLValue(arg0); }}));
2989
2990        Option<TypeCheckerResult> _rhs_result = this.recurOnOptionOfExpr(that.getRhs());
2991        if ( _rhs_result.isSome() ) {
2992            TypeCheckerResult rhs_result = _rhs_result.unwrap();
2993            result = TypeCheckerResult.compose(that, subtypeChecker, result, rhs_result);
2994
2995            if( rhs_result.type().isSome() ) {
2996                Type rhs_type = rhs_result.type().unwrap();
2997                result = TypeCheckerResult.compose(that, subtypeChecker, result,
2998                        checkSubtype(rhs_type, lhs_type, that,
2999                                "Right-hand side, " + rhs_type + ", must be a subtype of left-hand side, " + lhs_type + "."));
3000            }
3001        }
3002
3003        // Extend typechecker with new bindings and with constraints from the RHS types
3004        TypeChecker newChecker = this.extend(that);
3005        Iterable<ConstraintFormula> rhs_constraint = IterUtil.make(result.getNodeConstraints());
3006        newChecker = newChecker.extendWithConstraints(rhs_constraint);
3007
3008        // A LocalVarDecl is like a let. It has a body, and it's type is the type of the body
3009        List<TypeCheckerResult> body_results = newChecker.recurOnListOfExpr(that.getBody());
3010        List<TypeCheckerResult> body_void = newChecker.allVoidButLast(body_results, that.getBody());
3011        Option<Type> body_type = that.getBody().size() == 0 ?
3012                Option.<Type>some(Types.VOID) :
3013                    body_results.get(body_results.size()-1).type();
3014
3015                LocalVarDecl new_node = ExprFactory.makeLocalVarDecl(NodeUtil.getSpan(that),
3016                        NodeUtil.isParenthesized(that),
3017                        body_type,
3018                        (List<Expr>)TypeCheckerResult.astFromResults(body_results),
3019                        that.getLhs(),
3020                        (Option<Expr>)TypeCheckerResult.astFromResult(_rhs_result));
3021                return TypeCheckerResult.compose(new_node,
3022                        body_type, subtypeChecker, result,
3023                        TypeCheckerResult.compose(new_node, subtypeChecker, _rhs_result),
3024                        TypeCheckerResult.compose(new_node, subtypeChecker, body_results),
3025                        TypeCheckerResult.compose(new_node, subtypeChecker, body_void)).addNodeTypeEnvEntry(new_node, typeEnv);
3026    }
3027
3028    @Override
3029        public TypeCheckerResult forJuxt(Juxt that) {
3030            if ( that.isTight() ) {
3031                // Just create a MathPrimary
3032                Expr front = that.getExprs().get(0);
3033
3034                // If this juxt is actually a fn app, then rewrite to a fn app.
3035                if (that.isFnApp()) {
3036                    // Make sure it is just two exprs.
3037                    if (that.getExprs().size() != 2) {
3038                        bug(String.format("TightJuxt denoted as function application but has %d (!= 2) exprs.",
3039                                          that.getExprs().size()));
3040                    }
3041                    Expr arg = that.getExprs().get(1);
3042                    _RewriteFnApp fnApp = ExprFactory.make_RewriteFnApp(front, arg);
3043
3044                    // Simulate a TypeCheckerResult for the front, giving it a fresh arrow type.
3045                    Type freshArrow = NodeFactory.makeArrowType(NodeUtil.getSpan(that),
3046                                                                NodeFactory.make_InferenceVarType(NodeUtil.getSpan(that)),
3047                                                                NodeFactory.make_InferenceVarType(NodeUtil.getSpan(that)));
3048                    TypeCheckerResult front_result = new TypeCheckerResult(front, freshArrow);
3049
3050                    // Type check the other nodes and recur on the fn app.
3051                    Option<TypeCheckerResult> exprType_result = this.recurOnOptionOfType(NodeUtil.getExprType(that));
3052                    TypeCheckerResult arg_result = arg.accept(this);
3053                    TypeCheckerResult fnApp_result = this.for_RewriteFnAppOnly(fnApp,
3054                                                                               exprType_result,
3055                                                                               front_result,
3056                                                                               arg_result);
3057                    return TypeCheckerResult.compose(that,
3058                                                     fnApp_result.type(),
3059                                                     subtypeChecker,
3060                                                     fnApp_result);
3061                }
3062
3063                Iterable<Expr> rest = IterUtil.skipFirst(that.getExprs());
3064                List<MathItem> items = CollectUtil.makeList(IterUtil.map(rest, new Lambda<Expr,MathItem>(){
3065                            public MathItem value(Expr arg0) {
3066                                if( NodeUtil.isParenthesized(arg0) || arg0 instanceof TupleExpr || arg0 instanceof VoidLiteralExpr)
3067                                    return ExprFactory.makeParenthesisDelimitedMI(NodeUtil.getSpan(arg0),arg0);
3068                                else
3069                                    return ExprFactory.makeNonParenthesisDelimitedMI(NodeUtil.getSpan(arg0),arg0);
3070                            }}));
3071                MathPrimary new_primary = ExprFactory.makeMathPrimary(NodeUtil.getSpan(that),
3072                                                               NodeUtil.isParenthesized(that),
3073                                                               NodeUtil.getExprType(that),
3074                                                               that.getMultiJuxt(),
3075                                                               that.getInfixJuxt(),
3076                                                               front, items);
3077        return new_primary.accept(this);
3078            } else
3079                return super.forJuxt(that);
3080        }
3081
3082    @Override
3083    public TypeCheckerResult forJuxtOnly(Juxt that, TypeCheckerResult exprType_result,
3084                                             TypeCheckerResult multiJuxt_result,
3085                                             TypeCheckerResult infixJuxt_result,
3086                                             List<TypeCheckerResult> exprs_result) {
3087            if (! that.isTight() ) {
3088                // The implementation of this method is very similar to tight juxt except
3089                // the ordering of association is different.
3090                // Notice also that tightJuxt has to be recursive, but loose juxt is not, due to specification.
3091
3092                // Did any subexpressions fail to typecheck?
3093                for( TypeCheckerResult r : exprs_result ) {
3094                    if( r.type().isNone())
3095                        return TypeCheckerResult.compose(that, subtypeChecker, exprs_result);
3096                }
3097
3098                if( that.getExprs().size() != exprs_result.size() ) {
3099                    bug("Number of types don't match number of sub-expressions");
3100                }
3101                // Specification describes chunks, which are elements group together. Chunking process goes first.
3102                List<Pair<TypeCheckerResult,Expr>> checked_chunks = new LinkedList<Pair<TypeCheckerResult,Expr>>();
3103                {
3104                    List<Pair<TypeCheckerResult,Expr>> cur_chunk = new LinkedList<Pair<TypeCheckerResult,Expr>>();
3105                    Iterator<Expr> expr_iter = that.getExprs().iterator();
3106                    boolean seen_non_fn = false;
3107                    // First the loose juxtaposition is broken into nonempty chunks; wherever there is a non-function element followed
3108                    // by a function element, the latter begins a new chunk. Thus a chunk consists of some number (possibly zero) of
3109                    // functions followed by some number (possibly zero) of non-functions.
3110                    for( TypeCheckerResult r : exprs_result ) {
3111                        boolean is_arrow = TypesUtil.isArrows(r.type().unwrap());
3112                        if( is_arrow && seen_non_fn ) {
3113                            // finished last chunk
3114                            Pair<TypeCheckerResult,Expr> checked_chunk = this.checkChunk(cur_chunk, that.getInfixJuxt());
3115                            checked_chunks.add(checked_chunk);
3116                            cur_chunk.clear();
3117                            seen_non_fn = false;
3118                        }
3119                        if( is_arrow ){
3120                            cur_chunk.add(Pair.make(r, expr_iter.next()));
3121                        }
3122                        else {
3123                            seen_non_fn = true;
3124                            cur_chunk.add(Pair.make(r, expr_iter.next()));
3125                        }
3126                    }
3127                    // Last chunk needs to be checked, if there is one
3128                    if( !cur_chunk.isEmpty() ) {
3129                        checked_chunks.add(checkChunk(cur_chunk, that.getInfixJuxt()));
3130                    }
3131                }
3132                // After chunking
3133                List<Expr> new_juxt_exprs = CollectUtil.makeList(IterUtil.pairSeconds(checked_chunks));
3134                List<TypeCheckerResult> new_juxt_results = CollectUtil.makeList(IterUtil.pairFirsts(checked_chunks));
3135
3136                if( checked_chunks.size() == 1 ) {
3137                    Expr expr = new_juxt_exprs.get(0);
3138                    TypeCheckerResult expr_result = expr.accept(this); // Is it bad to re-typecheck all args?
3139                    return TypeCheckerResult.compose(expr_result.ast(), expr_result.type(), subtypeChecker, expr_result,
3140                                                     TypeCheckerResult.compose(expr_result.ast(), subtypeChecker, new_juxt_results));
3141                }
3142                // (1) If any element that remains has type String, then it is a static error if any two adjacent elements are not of type String.
3143                // TODO: Separate pass?
3144                // (2) Treat the sequence that remains as a multifix application of the juxtaposition operator. The rules for multifix operators then apply:
3145                OpExpr multi_op_expr = ExprFactory.makeOpExpr(NodeUtil.getSpan(that),
3146                                                              NodeUtil.isParenthesized(that),
3147                                                              NodeUtil.getExprType(that),
3148                                                              that.getMultiJuxt(),
3149                                                              new_juxt_exprs);
3150                TypeCheckerResult multi_op_result = multi_op_expr.accept(this);
3151                if( multi_op_result.type().isSome() ) {
3152                    return TypeCheckerResult.compose(multi_op_result.ast(), multi_op_result.type(), subtypeChecker,
3153                                                     TypeCheckerResult.compose(multi_op_result.ast(), subtypeChecker, new_juxt_results));
3154                }
3155                // if an applicable method cannot be found for the entire expression, then it is left-associated.
3156                Iterator<Expr> expr_iter = new_juxt_exprs.iterator();
3157                Expr expr_1 = expr_iter.next(); // the fact that >= two items are here is guaranteed from above.
3158                Expr expr_2 = expr_iter.next();
3159                OpExpr cur_op_expr = ExprFactory.makeOpExpr(FortressUtil.spanTwo(expr_1,expr_2),
3160                                                            that.getInfixJuxt(),
3161                                                            expr_1, expr_2);
3162                while( expr_iter.hasNext() ) {
3163                    Expr next_expr = expr_iter.next();
3164                    cur_op_expr = ExprFactory.makeOpExpr(FortressUtil.spanTwo(cur_op_expr,next_expr),
3165                                                         that.getInfixJuxt(),
3166                                                         cur_op_expr, next_expr);
3167                }
3168                // typecheck this result instead
3169                TypeCheckerResult op_expr_result = cur_op_expr.accept(this); // Is it bad to re-typecheck all args?
3170                return TypeCheckerResult.compose(op_expr_result.ast(), op_expr_result.type(), subtypeChecker, op_expr_result,
3171                                                 TypeCheckerResult.compose(op_expr_result.ast(), subtypeChecker, new_juxt_results));
3172            } else {
3173                return super.forJuxtOnly(that, exprType_result,
3174                                         multiJuxt_result, infixJuxt_result,
3175                                         exprs_result);
3176            }
3177        }
3178
3179    // Math primary, which is the more general case, is going to be called for both TightJuxt and MathPrimary
3180    @Override
3181    public TypeCheckerResult forMathPrimary(MathPrimary that) {
3182
3183        // Base case of recursion: If there is no 'rest', return the Expr
3184        Expr front = that.getFront();
3185        if( that.getRest().isEmpty() ) {
3186            return front.accept(this);
3187        }
3188        // See if simple static errors exist
3189        Option<TypeCheckerResult> static_result = exponentiationStaticCheck(that, that.getRest());
3190        if( static_result.isSome() ) {
3191            return static_result.unwrap();
3192        }
3193
3194        // HANDLE THE FRONT ITEM
3195        {
3196            // Create a new list of MathItems that is a copy of the old one
3197            List<MathItem> new_items = new ArrayList<MathItem>(that.getRest());
3198            ListIterator<MathItem> item_iter = new_items.listIterator();
3199
3200            TypeCheckerResult front_result = front.accept(this);
3201            MathItem first_of_rest = item_iter.next();
3202            if( front_result.type().isNone() )
3203                return TypeCheckerResult.compose(that, subtypeChecker, front_result);
3204
3205            // If front is a fn followed by an expr, we reassociate
3206            if( TypesUtil.isArrows(front_result.type().unwrap()) && isExprMI(first_of_rest) ) {
3207                MathItem arg = first_of_rest;
3208                // It is a static error if either the argument is not parenthesized,
3209                Option<TypeCheckerResult> is_error = expectParenedExprItem(arg);
3210                if( is_error.isSome() ) {
3211                    return TypeCheckerResult.compose(that, subtypeChecker, is_error.unwrap());
3212                }
3213                item_iter.remove(); // remove arg from item list
3214                // static error if the argument is immediately followed by a non-expression element.
3215                if( item_iter.hasNext() ) {
3216                    Option<TypeCheckerResult> is_expr_error = expectExprMI(item_iter.next());
3217                    if( is_expr_error.isSome() ) {
3218                        return TypeCheckerResult.compose(that, subtypeChecker, is_expr_error.unwrap());
3219                    }
3220                }
3221                // Otherwise, make a new MathPrimary that is one element shorter, and recur
3222                _RewriteFnApp fn = ExprFactory.make_RewriteFnApp(front,
3223                                                                                 ((ExprMI)arg).getExpr());
3224                MathPrimary new_primary = ExprFactory.makeMathPrimary(NodeUtil.getSpan(that),
3225                                                                                      NodeUtil.isParenthesized(that),
3226                                                                                      NodeUtil.getExprType(that),
3227                                                                                      that.getMultiJuxt(),
3228                                                                                      that.getInfixJuxt(),
3229                                                                                      fn,
3230                                                                                      new_items);
3231                return new_primary.accept(this);
3232            }
3233        }
3234        // THE FRONT ITEM WAS NOT A FN FOLLOWED BY AN EXPR, REASSOCIATE REST
3235        {
3236
3237            Expr last_expr_ = front;
3238            boolean last_expr_is_front = true;
3239            // Create a new list of MathItems that is a copy of the old one
3240            List<MathItem> new_items = new ArrayList<MathItem>(that.getRest());
3241            ListIterator<MathItem> item_iter = new_items.listIterator();
3242
3243            while( item_iter.hasNext() ) {
3244                MathItem cur_item = item_iter.next();
3245                final Expr last_expr = last_expr_;
3246                // For each expression element determine whether it is a function immediately followed by an expr
3247                if( isExprMI(cur_item) ) {
3248                    TypeCheckerResult expr_result = ((ExprMI)cur_item).getExpr().accept(this);
3249                    if( expr_result.type().isNone() )
3250                        return TypeCheckerResult.compose(that, subtypeChecker, expr_result);
3251
3252                    Option<MathItem> next_item = item_iter.hasNext() ? Option.<MathItem>some(item_iter.next()) : Option.<MathItem>none();
3253                    if( TypesUtil.isArrows(expr_result.type().unwrap()) && next_item.isSome() && isExprMI(next_item.unwrap()) ) {
3254                        // Yes. We have a function followed by an expr
3255                        MathItem arg = next_item.unwrap();
3256                        // It is a static error if either the argument is not parenthesized,
3257                        Option<TypeCheckerResult> is_error = expectParenedExprItem(arg);
3258                        if( is_error.isSome() ) {
3259                            return TypeCheckerResult.compose(that, subtypeChecker, is_error.unwrap());
3260                        }
3261                        item_iter.remove(); // remove arg from item list
3262                        item_iter.previous();
3263                        item_iter.remove(); // remove fn from item list
3264                        // replace both with fn appication
3265                        _RewriteFnApp fn = ExprFactory.make_RewriteFnApp(front, ((ExprMI)arg).getExpr());
3266                        item_iter.add(ExprFactory.makeNonParenthesisDelimitedMI(NodeUtil.getSpan(fn),fn));
3267                        // static error if the argument is immediately followed by a non-expression element.
3268                        if( item_iter.hasNext() ) {
3269                            Option<TypeCheckerResult> is_expr_error = expectExprMI(item_iter.next());
3270                            if( is_expr_error.isSome() ) {
3271                                return TypeCheckerResult.compose(that, subtypeChecker, is_expr_error.unwrap());
3272                            }
3273                        }
3274                        // Otherwise, make a new MathPrimary that is one element shorter, and recur
3275                        MathPrimary new_primary = ExprFactory.makeMathPrimary(NodeUtil.getSpan(that),
3276                                                                                                      NodeUtil.isParenthesized(that),
3277                                                                                                      NodeUtil.getExprType(that),
3278                                                                                                      that.getMultiJuxt(),
3279                                                                                                      that.getInfixJuxt(),
3280                                                                                                      that.getFront(),
3281                                                                                                      new_items);
3282                        return new_primary.accept(this);
3283                    } else {
3284                        // Continues to next expression...
3285                        last_expr_ = ((ExprMI)cur_item).getExpr();
3286                        last_expr_is_front = false;
3287                    }
3288                }
3289                else {
3290                    // If there is any non-expression element then replace the first such element and the
3291                    // element immediately preceding it (which must be an expression) with a single element that does the appropriate
3292                    // operator application.
3293                    NodeDepthFirstVisitor<Expr> op_visitor = new NodeDepthFirstVisitor<Expr>() {
3294                        @Override
3295                        public Expr forExponentiationMI(ExponentiationMI that) {
3296                            // how could an expression not exist in an exponentiation?
3297                            return ExprFactory.makeOpExpr(that.getOp(), last_expr, that.getExpr().unwrap());
3298                        }
3299                        @Override
3300                        public Expr forSubscriptingMI(SubscriptingMI that) {
3301                            Span span = new Span(NodeUtil.getSpan(last_expr),NodeUtil.getSpan(that));
3302                            return ExprFactory.makeSubscriptExpr(span, last_expr, that.getExprs(), Option.some(that.getOp()), that.getStaticArgs());
3303                        }
3304                    };
3305                    Expr new_expr = cur_item.accept(op_visitor);
3306                    item_iter.remove(); // remove NonExprMI
3307                    if( last_expr_is_front ) {
3308                        // in our new MathPrimary, place this expression at the front
3309                        MathPrimary new_primary = ExprFactory.makeMathPrimary(NodeUtil.getSpan(that),
3310                                                                                                      NodeUtil.isParenthesized(that),
3311                                                                                                      NodeUtil.getExprType(that),
3312                                                                                                      that.getMultiJuxt(),
3313                                                                                                      that.getInfixJuxt(),
3314                                                                                                      new_expr, new_items);
3315                        return new_primary.accept(this);
3316                    }
3317                    else {
3318                        // in our new MathPrimary, place this expression where it is
3319                        item_iter.previous();
3320                        item_iter.remove(); // remove the previous expression in the list.
3321                        MathItem new_item = ExprFactory.makeParenthesisDelimitedMI(NodeUtil.getSpan(new_expr),new_expr);
3322                        item_iter.add(new_item); // replace both item with new item
3323                        MathPrimary new_primary = ExprFactory.makeMathPrimary(NodeUtil.getSpan(that),
3324                                                                                                      NodeUtil.isParenthesized(that),
3325                                                                                                      NodeUtil.getExprType(that),
3326                                                                                                      that.getMultiJuxt(),
3327                                                                                                      that.getInfixJuxt(),
3328                                                                                                      that.getFront(),
3329                                                                                                      new_items);
3330                        return new_primary.accept(this);
3331                    }
3332                }
3333            }
3334        }
3335
3336        // If you've made is this far, there are only expressions left in the math primary.
3337        // helper method handles the last two rules
3338        return juxtaposeMathPrimary(that);
3339    }
3340
3341
3342
3343
3344
3345    @Override
3346    public TypeCheckerResult forMethodInvocation(MethodInvocation that) {
3347        Option<TypeCheckerResult> exprType_result = recurOnOptionOfType(NodeUtil.getExprType(that));
3348        TypeCheckerResult obj_result = recur(that.getObj());
3349        TypeCheckerResult method_result = recur(that.getMethod());
3350        List<TypeCheckerResult> staticArgs_result = recurOnListOfStaticArg(that.getStaticArgs());
3351        TypeCheckerResult arg_result = recur(that.getArg());
3352
3353        // Use constraints from arguments to extend typechecker. This will allow for
3354        // better choices of overloadings during the first typechecking phase.
3355        Iterable<ConstraintFormula> arg_constraint = IterUtil.make(arg_result.getNodeConstraints());
3356        TypeChecker new_checker = this.extendWithConstraints(arg_constraint);
3357        return new_checker.forMethodInvocationOnly(that, exprType_result, obj_result, method_result, staticArgs_result, arg_result);
3358    }
3359
3360    public TypeCheckerResult forMethodInvocationOnly(MethodInvocation that, Option<TypeCheckerResult> exprType_result,
3361            TypeCheckerResult obj_result, TypeCheckerResult DONTUSE,
3362            List<TypeCheckerResult> staticArgs_result,
3363            TypeCheckerResult arg_result) {
3364
3365        // Did the arguments typecheck?
3366        if( arg_result.type().isNone() ) {
3367            return TypeCheckerResult.compose(that, subtypeChecker, obj_result, arg_result,
3368                    TypeCheckerResult.compose(that, subtypeChecker, staticArgs_result));
3369        }
3370        // We need the type of the receiver
3371        if( obj_result.type().isNone() ) {
3372            return TypeCheckerResult.compose(that, subtypeChecker, obj_result, arg_result,
3373                    TypeCheckerResult.compose(that, subtypeChecker, staticArgs_result));
3374        }
3375        // Check whether receiver can have methods
3376        Type recvr_type=obj_result.type().unwrap();
3377        List<TraitType> traits = traitTypesCallable(recvr_type);
3378        if( traits.isEmpty() ) {
3379            //error receiver not a trait
3380            String trait_err = "Target of a method invocation must have trait type, while this receiver has type " + recvr_type + ".";
3381            TypeCheckerResult trait_result = new TypeCheckerResult(that.getObj(), TypeError.make(trait_err, that.getObj()));
3382            return TypeCheckerResult.compose(that, subtypeChecker, obj_result, arg_result, trait_result,
3383                    TypeCheckerResult.compose(that, subtypeChecker, staticArgs_result));
3384        }
3385        else {
3386            Pair<List<Method>,List<TypeCheckerResult>> candidate_pair =
3387                findMethodsInTraitHierarchy(that.getMethod(), traits, arg_result.type().unwrap(),that.getStaticArgs(),that);
3388
3389            // Now we meet together the results, or return an error if there are no candidates.
3390            List<Method> candidates = candidate_pair.first();
3391            if(candidates.isEmpty()) {
3392                String err = "No candidate methods found for '" + that.getMethod() + "' with argument types (" + arg_result.type().unwrap() + ").";
3393                TypeCheckerResult no_methods = new TypeCheckerResult(that,TypeError.make(err,that));
3394
3395                return TypeCheckerResult.compose(that, subtypeChecker, obj_result, arg_result, no_methods,
3396                        TypeCheckerResult.compose(that, subtypeChecker, staticArgs_result));
3397            }
3398
3399            List<Type> ranges = CollectUtil.makeList(IterUtil.map(candidates, new Lambda<Method,Type>(){
3400                public Type value(Method arg0) {
3401                    return arg0.getReturnType();
3402                }}));
3403
3404            Type range = subtypeChecker.meet(ranges);
3405            range = subtypeChecker.normalize(range);
3406            MethodInvocation new_node = ExprFactory.makeMethodInvocation(NodeUtil.getSpan(that),
3407                                                                                     NodeUtil.isParenthesized(that),
3408                                                                                     Option.<Type>some(range),
3409                                                                                     (Expr)obj_result.ast(),
3410                                                                                     that.getMethod(),
3411                    (List<StaticArg>)TypeCheckerResult.astFromResults(staticArgs_result),
3412                    (Expr)arg_result.ast());
3413
3414            TypeCheckerResult result = TypeCheckerResult.compose(new_node, range, subtypeChecker, obj_result, arg_result,
3415                    TypeCheckerResult.compose(that, subtypeChecker, staticArgs_result),
3416                    TypeCheckerResult.compose(new_node, subtypeChecker, candidate_pair.second()));
3417
3418            // On the post-inference pass, an application could produce Inference vars
3419            TypeCheckerResult successful = new TypeCheckerResult(that);
3420            if( postInference && TypesUtil.containsInferenceVarTypes(new_node) ) {
3421                // close constraints
3422
3423                Pair<Boolean,Node> temp1 = TypesUtil.closeConstraints(new_node, subtypeChecker, result);
3424                new_node = (MethodInvocation)temp1.second();
3425                Pair<Boolean,Node> temp2 = TypesUtil.closeConstraints(range, subtypeChecker, result);
3426                range = (Type)temp2.second();
3427                if(!temp1.first() || !temp2.first()){
3428                    String err = "No candidate methods found for '" + that.getMethod() + "' with argument types (" + arg_result.type().unwrap() + ").";
3429                    successful = new TypeCheckerResult(that, TypeError.make(err,that));
3430                }
3431            }
3432
3433            return TypeCheckerResult.compose(new_node, range, subtypeChecker, result, successful);
3434        }
3435    }
3436
3437    @Override
3438    public TypeCheckerResult forMultiFixity(MultiFixity that) {
3439        // No checks needed to be performed on a MultiFixity.
3440        return new TypeCheckerResult(that);
3441    }
3442
3443    @Override
3444    public TypeCheckerResult forNoFixity(NoFixity that) {
3445        // No checks needed to be performed on a NoFixity.
3446        return new TypeCheckerResult(that);
3447    }
3448
3449    @Override
3450    public TypeCheckerResult forParam(Param that) {
3451        // No checks needed to be performed on a Param.
3452        return new TypeCheckerResult(that);
3453    }
3454
3455    @Override
3456    public TypeCheckerResult forIntBaseOnly(IntBase that,
3457                                                TypeCheckerResult info,
3458                                                TypeCheckerResult val_result) {
3459        return new TypeCheckerResult(that);
3460    }
3461
3462    @Override
3463    public TypeCheckerResult forObjectDecl(final ObjectDecl that) {
3464        TypeChecker checker_with_sparams = this.extend(NodeUtil.getStaticParams(that), NodeUtil.getParams(that), NodeUtil.getWhereClause(that));
3465        TypeCheckerResult nameResult = NodeUtil.getName(that).accept(checker_with_sparams);
3466        List<TypeCheckerResult> extendsClauseResult = checker_with_sparams.recurOnListOfTraitTypeWhere(NodeUtil.getExtendsClause(that));
3467        TypeCheckerResult whereResult;
3468        Option<WhereClause> where;
3469        if ( NodeUtil.getWhereClause(that).isSome() ) {
3470            whereResult = NodeUtil.getWhereCl