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

Revision 3292, 253.9 KB (checked in by EricAllen, 11 months ago)

Added a type normalizer to improve presentation of types in error messages.
Got the type checker working over more of our first 20 compiled programs.
Added corresponding tests to CompilerJUTests.

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