Package com.redhat.ceylon.compiler.typechecker.analyzer

Source Code of com.redhat.ceylon.compiler.typechecker.analyzer.TypeVisitor

package com.redhat.ceylon.compiler.typechecker.analyzer;

import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.declaredInPackage;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.getTypeArguments;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.getTypeDeclaration;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.getTypeMember;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.getTypedDeclaration;
import static com.redhat.ceylon.compiler.typechecker.model.SiteVariance.IN;
import static com.redhat.ceylon.compiler.typechecker.model.SiteVariance.OUT;
import static com.redhat.ceylon.compiler.typechecker.model.Util.getContainingClassOrInterface;
import static com.redhat.ceylon.compiler.typechecker.model.Util.intersectionOfSupertypes;
import static com.redhat.ceylon.compiler.typechecker.model.Util.isTypeUnknown;
import static com.redhat.ceylon.compiler.typechecker.model.Util.notOverloaded;
import static com.redhat.ceylon.compiler.typechecker.model.Util.producedType;
import static com.redhat.ceylon.compiler.typechecker.tree.Util.formatPath;
import static com.redhat.ceylon.compiler.typechecker.tree.Util.name;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.redhat.ceylon.compiler.typechecker.model.Class;
import com.redhat.ceylon.compiler.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.Generic;
import com.redhat.ceylon.compiler.typechecker.model.Import;
import com.redhat.ceylon.compiler.typechecker.model.ImportList;
import com.redhat.ceylon.compiler.typechecker.model.Interface;
import com.redhat.ceylon.compiler.typechecker.model.IntersectionType;
import com.redhat.ceylon.compiler.typechecker.model.Method;
import com.redhat.ceylon.compiler.typechecker.model.MethodOrValue;
import com.redhat.ceylon.compiler.typechecker.model.Module;
import com.redhat.ceylon.compiler.typechecker.model.ModuleImport;
import com.redhat.ceylon.compiler.typechecker.model.Package;
import com.redhat.ceylon.compiler.typechecker.model.Parameter;
import com.redhat.ceylon.compiler.typechecker.model.ProducedType;
import com.redhat.ceylon.compiler.typechecker.model.Scope;
import com.redhat.ceylon.compiler.typechecker.model.Specification;
import com.redhat.ceylon.compiler.typechecker.model.TypeAlias;
import com.redhat.ceylon.compiler.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.TypeParameter;
import com.redhat.ceylon.compiler.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.UnionType;
import com.redhat.ceylon.compiler.typechecker.model.Unit;
import com.redhat.ceylon.compiler.typechecker.model.UnknownType;
import com.redhat.ceylon.compiler.typechecker.model.Value;
import com.redhat.ceylon.compiler.typechecker.parser.CeylonLexer;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.BaseMemberExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.MemberLiteral;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.TypeVariance;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;

/**
* Second phase of type analysis.
* Scan the compilation unit looking for literal type
* declarations and maps them to the associated model
* objects. Also builds up a list of imports for the
* compilation unit. Finally, assigns types to the
* associated model objects of declarations declared
* using an explicit type (this must be done in
* this phase, since shared declarations may be used
* out of order in expressions).
*
* @author Gavin King
*
*/
public class TypeVisitor extends Visitor {
   
    private Unit unit;
   
    @Override public void visit(Tree.CompilationUnit that) {
        unit = that.getUnit();
        super.visit(that);
        HashSet<String> set = new HashSet<String>();
        for (Tree.Import im: that.getImportList().getImports()) {
            Tree.ImportPath ip = im.getImportPath();
            if (ip!=null) {
                String mp = formatPath(ip.getIdentifiers());
                if (!set.add(mp)) {
                    ip.addError("duplicate import: '" + mp + "'");
                }
            }
        }
    }
   
    @Override
    public void visit(Tree.Import that) {
        Package importedPackage = getPackage(that.getImportPath());
        if (importedPackage!=null) {
            that.getImportPath().setModel(importedPackage);
            Tree.ImportMemberOrTypeList imtl = that.getImportMemberOrTypeList();
            if (imtl!=null) {
                ImportList il = imtl.getImportList();
                il.setImportedScope(importedPackage);
                Set<String> names = new HashSet<String>();
                for (Tree.ImportMemberOrType member: imtl.getImportMemberOrTypes()) {
                    names.add(importMember(member, importedPackage, il));
                }
                if (imtl.getImportWildcard()!=null) {
                    importAllMembers(importedPackage, names, il);
                }
                else if (imtl.getImportMemberOrTypes().isEmpty()) {
                    imtl.addError("empty import list");
                }
            }
        }
    }
   
    private void importAllMembers(Package importedPackage,
            Set<String> ignoredMembers, ImportList il) {
        for (Declaration dec: importedPackage.getMembers()) {
            if (dec.isShared() && !dec.isAnonymous() &&
                    !ignoredMembers.contains(dec.getName()) &&
                    !isNonimportable(importedPackage, dec.getName())) {
                addWildcardImport(il, dec);
            }
        }
    }
   
    private void importAllMembers(TypeDeclaration importedType,
            Set<String> ignoredMembers, ImportList til) {
        for (Declaration dec: importedType.getMembers()) {
            if (dec.isShared() && dec.isStaticallyImportable() &&
                    !dec.isAnonymous() &&
                    !ignoredMembers.contains(dec.getName())) {
                addWildcardImport(til, dec);
            }
        }
    }
   
    private void addWildcardImport(ImportList il, Declaration dec) {
        if (!hidesToplevel(dec)) {
            Import i = new Import();
            i.setAlias(dec.getName());
            i.setDeclaration(dec);
            i.setWildcardImport(true);
            addWildcardImport(il, dec, i);
        }
    }
   
    private void addWildcardImport(ImportList il, Declaration dec, Import i) {
        if (notOverloaded(dec)) {
            String alias = i.getAlias();
            if (alias!=null) {
                Import o = unit.getImport(dec.getName());
                if (o!=null && o.isWildcardImport()) {
                    if (o.getDeclaration().equals(dec)) {
                        //this case only happens in the IDE,
                        //due to reuse of the Unit
                        unit.getImports().remove(o);
                        il.getImports().remove(o);
                    }
                    else {
                        i.setAmbiguous(true);
                        o.setAmbiguous(true);
                    }
                }
                unit.getImports().add(i);
                il.getImports().add(i);
            }
        }
    }
   
    public static Module getModule(Tree.ImportPath path) {
        if (path!=null && !path.getIdentifiers().isEmpty()) {
            String nameToImport = formatPath(path.getIdentifiers());
            Module module = path.getUnit().getPackage().getModule();
            Package pkg = module.getPackage(nameToImport);
            if (pkg != null) {
                Module mod = pkg.getModule();
                if (!pkg.getNameAsString().equals(mod.getNameAsString())) {
                    path.addError("not a module: '" + nameToImport + "'");
                    return null;
                }
                if (mod.equals(module)) {
                    return mod;
                }
                //check that the package really does belong to
                //an imported module, to work around bug where
                //default package thinks it can see stuff in
                //all modules in the same source dir
                Set<Module> visited = new HashSet<Module>();
                for (ModuleImport mi: module.getImports()) {
                    if (findModuleInTransitiveImports(mi.getModule(),
                            mod, visited)) {
                        return mod;
                    }
                }
            }
            path.addError("module not found in imported modules: '" +
                    nameToImport + "'", 7000);
        }
        return null;
    }
   
    public static Package getPackage(Tree.ImportPath path) {
        if (path!=null && !path.getIdentifiers().isEmpty()) {
            String nameToImport = formatPath(path.getIdentifiers());
            Module module = path.getUnit().getPackage().getModule();
            Package pkg = module.getPackage(nameToImport);
            if (pkg != null) {
                if (pkg.getModule().equals(module)) {
                    return pkg;
                }
                if (!pkg.isShared()) {
                    path.addError("imported package is not shared: '" +
                            nameToImport + "'");
                }
//                if (module.isDefault() &&
//                        !pkg.getModule().isDefault() &&
//                        !pkg.getModule().getNameAsString()
//                            .equals(Module.LANGUAGE_MODULE_NAME)) {
//                    path.addError("package belongs to a module and may not be imported by default module: " +
//                            nameToImport);
//                }
                //check that the package really does belong to
                //an imported module, to work around bug where
                //default package thinks it can see stuff in
                //all modules in the same source dir
                Set<Module> visited = new HashSet<Module>();
                for (ModuleImport mi: module.getImports()) {
                    if (findModuleInTransitiveImports(mi.getModule(),
                            pkg.getModule(), visited)) {
                        return pkg;
                    }
                }
            }
            String help;
            if(module.isDefault())
                help = " (define a module and add module import to its module descriptor)";
            else
                help = " (add module import to module descriptor of '" +
                        module.getNameAsString() + "')";
            path.addError("package not found in imported modules: '" +
                    nameToImport + "'" + help, 7000);
        }
        return null;
    }
   
//    private boolean hasName(List<Tree.Identifier> importPath, Package mp) {
//        if (mp.getName().size()==importPath.size()) {
//            for (int i=0; i<mp.getName().size(); i++) {
//                if (!mp.getName().get(i).equals(name(importPath.get(i)))) {
//                    return false;
//                }
//            }
//            return true;
//        }
//        else {
//            return false;
//        }
//    }
   
    private static boolean findModuleInTransitiveImports(Module moduleToVisit,
            Module moduleToFind, Set<Module> visited) {
        if (!visited.add(moduleToVisit))
            return false;
        if (moduleToVisit.equals(moduleToFind))
            return true;
        for (ModuleImport imp : moduleToVisit.getImports()) {
            // skip non-exported modules
            if (!imp.isExport())
                continue;
            if (findModuleInTransitiveImports(imp.getModule(), moduleToFind, visited))
                return true;
        }
        return false;
    }
   
    private boolean hidesToplevel(Declaration dec) {
        for (Declaration d: unit.getDeclarations()) {
            String n = d.getName();
            if (d.isToplevel() && n!=null &&
                    dec.getName().equals(n)) {
                return true;
            }
        }
        return false;
    }
   
    private boolean checkForHiddenToplevel(Tree.Identifier id, Import i, Tree.Alias alias) {
        for (Declaration d: unit.getDeclarations()) {
            String n = d.getName();
            if (d.isToplevel() && n!=null &&
                    i.getAlias().equals(n)) {
                if (alias==null) {
                    id.addError("toplevel declaration with this name declared in this unit: '" + n + "'");
                }
                else {
                    alias.addError("toplevel declaration with this name declared in this unit: '" + n + "'");
                }
                return true;
            }
        }
        return false;
    }
   
    private void importMembers(Tree.ImportMemberOrType member, Declaration d) {
        Tree.ImportMemberOrTypeList imtl = member.getImportMemberOrTypeList();
        if (imtl!=null) {
            if (d instanceof TypeDeclaration) {
                Set<String> names = new HashSet<String>();
                ImportList til = imtl.getImportList();
                TypeDeclaration td = (TypeDeclaration) d;
                til.setImportedScope(td);
                for (Tree.ImportMemberOrType submember: imtl.getImportMemberOrTypes()) {
                    names.add(importMember(submember, td, til));
                }
                if (imtl.getImportWildcard()!=null) {
                    importAllMembers(td, names, til);
                }
                else if (imtl.getImportMemberOrTypes().isEmpty()) {
                    imtl.addError("empty import list");
                }
            }
            else {
                imtl.addError("member alias list must follow a type");
            }
        }
    }
   
    private void checkAliasCase(Tree.Alias alias, Declaration d) {
        if (alias!=null) {
            Tree.Identifier id = alias.getIdentifier();
            int tt = id.getToken().getType();
            if (d instanceof TypeDeclaration &&
                    tt!=CeylonLexer.UIDENTIFIER) {
                id.addError("imported type should have uppercase alias: '" +
                        d.getName() + "'");
            }
            else if (d instanceof TypedDeclaration &&
                    tt!=CeylonLexer.LIDENTIFIER) {
                id.addError("imported member should have lowercase alias: '" +
                        d.getName() + "'");
            }
        }
    }
   
    private String importMember(Tree.ImportMemberOrType member,
            Package importedPackage, ImportList il) {
        Tree.Identifier id = member.getIdentifier();
        if (id==null) {
            return null;
        }
        Import i = new Import();
        member.setImportModel(i);
        Tree.Alias alias = member.getAlias();
        String name = name(id);
        if (alias==null) {
            i.setAlias(name);
        }
        else {
            i.setAlias(name(alias.getIdentifier()));
        }
        if (isNonimportable(importedPackage, name)) {
            id.addError("root type may not be imported");
            return name;
        }       
        Declaration d = importedPackage.getMember(name, null, false);
        if (d==null) {
            id.addError("imported declaration not found: '" +
                    name + "'", 100);
            unit.getUnresolvedReferences().add(id);
        }
        else {
            if (!declaredInPackage(d, unit)) {
                if (!d.isShared()) {
                    id.addError("imported declaration is not shared: '" +
                            name + "'", 400);
                }
                else if (d.isPackageVisibility()) {
                    id.addError("imported package private declaration is not visible: '" +
                            name + "'");
                }
                else if (d.isProtectedVisibility()) {
                    id.addError("imported protected declaration is not visible: '" +
                            name + "'");
                }
            }
            i.setDeclaration(d);
            member.setDeclarationModel(d);
            if (il.hasImport(d)) {
                id.addError("already imported: '" + name + "'");
            }
            else if (!checkForHiddenToplevel(id, i, alias)) {
                addImport(member, il, i);
            }
            checkAliasCase(alias, d);
        }
        importMembers(member, d);
        return name;
    }
   
    private String importMember(Tree.ImportMemberOrType member,
            TypeDeclaration td, ImportList il) {
        Tree.Identifier id = member.getIdentifier();
        if (id==null) {
            return null;
        }
        Import i = new Import();
        member.setImportModel(i);
        Tree.Alias alias = member.getAlias();
        String name = name(id);
        if (alias==null) {
            i.setAlias(name);
        }
        else {
            i.setAlias(name(alias.getIdentifier()));
        }
        Declaration m = td.getMember(name, null, false);
        if (m==null) {
            id.addError("imported declaration not found: '" +
                    name + "' of '" + td.getName() + "'", 100);
            unit.getUnresolvedReferences().add(id);
        }
        else {
            for (Declaration d: m.getContainer().getMembers()) {
                if (d.getName().equals(name) && !d.sameKind(m)) {
                    //crazy interop cases like isOpen() + open()
                    id.addError("ambiguous member declaration: '" +
                            name + "' of '" + td.getName() + "'");
                    return null;
                }
            }
            if (!m.isShared()) {
                id.addError("imported declaration is not shared: '" +
                        name + "' of '" + td.getName() + "'", 400);
            }
            else if (!declaredInPackage(m, unit)) {
                if (m.isPackageVisibility()) {
                    id.addError("imported package private declaration is not visible: '" +
                            name + "' of '" + td.getName() + "'");
                }
                else if (m.isProtectedVisibility()) {
                    id.addError("imported protected declaration is not visible: '" +
                            name + "' of '" + td.getName() + "'");
                }
            }
            i.setTypeDeclaration(td);
            if (!m.isStaticallyImportable()) {
                if (alias==null) {
                    member.addError("does not specify an alias");
                }
            }
            i.setDeclaration(m);
            member.setDeclarationModel(m);
            if (il.hasImport(m)) {
                id.addError("already imported: '" +
                        name + "' of '" + td.getName() + "'");
            }
            else {
                if (m.isStaticallyImportable()) {
                    if (!checkForHiddenToplevel(id, i, alias)) {
                        addImport(member, il, i);
                    }
                }
                else {
                    addMemberImport(member, il, i);
                }
            }
            checkAliasCase(alias, m);
        }
        importMembers(member, m);
        //imtl.addError("member aliases may not have member aliases");
        return name;
    }
   
    private void addImport(Tree.ImportMemberOrType member, ImportList il,
            Import i) {
        String alias = i.getAlias();
        if (alias!=null) {
            Map<String, String> mods = unit.getModifiers();
            if (mods.containsKey(alias) && mods.get(alias).equals(alias)) {
                //spec says you can't hide a language modifier
                //unless the modifier itself has an alias
                //(this is perhaps a little heavy-handed)
                member.addError("import hides a language modifier: '" + alias + "'");
            }
            else {
                Import o = unit.getImport(alias);
                if (o==null) {
                    unit.getImports().add(i);
                    il.getImports().add(i);
                }
                else if (o.isWildcardImport()) {
                    unit.getImports().remove(o);
                    il.getImports().remove(o);
                    unit.getImports().add(i);
                    il.getImports().add(i);
                }
                else {
                    member.addError("duplicate import alias: '" + alias + "'");
                }
            }
        }
    }
   
    private void addMemberImport(Tree.ImportMemberOrType member, ImportList il,
            Import i) {
        String alias = i.getAlias();
        if (alias!=null) {
            if (il.getImport(alias)==null) {
                unit.getImports().add(i);
                il.getImports().add(i);
            }
            else {
                member.addError("duplicate member import alias: '" + alias + "'");
            }
        }
    }
   
    private boolean isNonimportable(Package pkg, String name) {
        return pkg.getQualifiedNameString().equals("java.lang") &&
                ("Object".equals(name) ||
                 "Throwable".equals(name) ||
                 "Exception".equals(name));
    }
   
    public void visit(Tree.GroupedType that) {
        super.visit(that);
        Tree.StaticType type = that.getType();
        if (type!=null) {
            that.setTypeModel(type.getTypeModel());
        }
    }
   
    @Override
    public void visit(Tree.UnionType that) {
        super.visit(that);
        List<ProducedType> types =
                new ArrayList<ProducedType>(that.getStaticTypes().size());
        for (Tree.StaticType st: that.getStaticTypes()) {
            //addToUnion( types, st.getTypeModel() );
            ProducedType t = st.getTypeModel();
            if (t!=null) types.add(t);
        }
        UnionType ut = new UnionType(unit);
        ut.setCaseTypes(types);
        that.setTypeModel(ut.getType());
        //that.setTarget(pt);
    }
   
    @Override
    public void visit(Tree.IntersectionType that) {
        super.visit(that);
        List<ProducedType> types =
                new ArrayList<ProducedType>(that.getStaticTypes().size());
        for (Tree.StaticType st: that.getStaticTypes()) {
            //addToIntersection(types, st.getTypeModel(), unit);
            ProducedType t = st.getTypeModel();
            if (t!=null) types.add(t);
        }
        IntersectionType it = new IntersectionType(unit);
        it.setSatisfiedTypes(types);
        that.setTypeModel(it.getType());
        //that.setTarget(pt);
    }
   
    @Override
    public void visit(Tree.SequenceType that) {
        super.visit(that);
        Tree.StaticType elementType = that.getElementType();
        Tree.NaturalLiteral length = that.getLength();
        ProducedType et = elementType.getTypeModel();
        if (et!=null) {
            ProducedType t;
            if (length==null) {
                t = unit.getSequentialType(et);
            }
            else {
                final int len;
                try {
                    len = Integer.parseInt(length.getText());
                }
                catch (NumberFormatException nfe) {
                    length.addError("must be a positive decimal integer");
                    return;
                }
                if (len<1) {
                    length.addError("must be positive");
                    return;
                }
                if (len>100) {
                    length.addError("may not be greater than 100");
                    return;
                }
                Class td = unit.getTupleDeclaration();
                t = unit.getEmptyDeclaration().getType();
                for (int i=0; i<len; i++) {
                    t = producedType(td, et, et, t);
                }
            }
            that.setTypeModel(t);
        }
    }
   
    @Override
    public void visit(Tree.IterableType that) {
        super.visit(that);
        Tree.Type elem = that.getElementType();
        if (elem==null) {
            that.setTypeModel(unit.getIterableType(unit.getNothingDeclaration().getType()));
            that.addError("iterable type must have an element type");
        }
        else {
            if (elem instanceof Tree.SequencedType) {
                ProducedType et = ((Tree.SequencedType) elem).getType().getTypeModel();
                if (et!=null) {
                    if (((Tree.SequencedType) elem).getAtLeastOne()) {
                        that.setTypeModel(unit.getNonemptyIterableType(et));
                    }
                    else {
                        that.setTypeModel(unit.getIterableType(et));
                    }
                }
            }
            else {
                that.addError("malformed iterable type");
            }
        }
    }
   
    @Override
    public void visit(Tree.OptionalType that) {
        super.visit(that);
        List<ProducedType> types = new ArrayList<ProducedType>(2);
        types.add(unit.getType(unit.getNullDeclaration()));
        ProducedType dt = that.getDefiniteType().getTypeModel();
        if (dt!=null) types.add(dt);
        UnionType ut = new UnionType(unit);
        ut.setCaseTypes(types);
        that.setTypeModel(ut.getType());
    }
   
    @Override
    public void visit(Tree.EntryType that) {
        super.visit(that);
        ProducedType kt = that.getKeyType().getTypeModel();
        ProducedType vt = that.getValueType()==null ?
                new UnknownType(unit).getType() :
                that.getValueType().getTypeModel();
        that.setTypeModel(unit.getEntryType(kt, vt));
    }
   
    @Override
    public void visit(Tree.FunctionType that) {
        super.visit(that);
        that.setTypeModel(producedType(unit.getCallableDeclaration(),
                that.getReturnType().getTypeModel(),
                getTupleType(that.getArgumentTypes(), unit)));
    }
   
    @Override
    public void visit(Tree.TupleType that) {
        super.visit(that);
        that.setTypeModel(getTupleType(that.getElementTypes(), unit));
    }

    static ProducedType getTupleType(List<Tree.Type> ets, Unit unit) {
        List<ProducedType> args = new ArrayList<ProducedType>(ets.size());
        boolean sequenced = false;
        boolean atleastone = false;
        int firstDefaulted = -1;
        for (int i=0; i<ets.size(); i++) {
            Tree.Type st = ets.get(i);
            ProducedType arg = st==null ? null : st.getTypeModel();
            if (arg==null) {
                arg = new UnknownType(unit).getType();
            }
            else if (st instanceof Tree.SpreadType) {
                //currently we only allow a
                //single spread type, but in
                //future we should also allow
                //X, Y, *Zs
                return st.getTypeModel();
            }
            else if (st instanceof Tree.DefaultedType) {
                if (firstDefaulted==-1) {
                    firstDefaulted = i;
                }
            }
            else if (st instanceof Tree.SequencedType) {
                if (i!=ets.size()-1) {
                    st.addError("variant element must occur last in a tuple type");
                }
                else {
                    sequenced = true;
                    atleastone = ((Tree.SequencedType) st).getAtLeastOne();
                    arg = ((Tree.SequencedType) st).getType().getTypeModel();
                }
                if (firstDefaulted!=-1 && atleastone) {
                    st.addError("nonempty variadic element must occur after defaulted elements in a tuple type");
                }
            }
            else {
                if (firstDefaulted!=-1) {
                    st.addError("required element must occur after defaulted elements in a tuple type");
                }
            }
            args.add(arg);
        }
        return getTupleType(args, sequenced, atleastone, firstDefaulted, unit);
    }

    //TODO: big copy/paste from Unit.getTupleType(), to eliminate
    //      the canonicalization (since aliases are not yet
    //      resolvable in this phase)
    public static ProducedType getTupleType(List<ProducedType> elemTypes,
            boolean variadic, boolean atLeastOne, int firstDefaulted,
            Unit unit) {
        ProducedType result = unit.getType(unit.getEmptyDeclaration());
        ProducedType union = unit.getType(unit.getNothingDeclaration());
        int last = elemTypes.size()-1;
        for (int i=last; i>=0; i--) {
            ProducedType elemType = elemTypes.get(i);
            List<ProducedType> pair = new ArrayList<ProducedType>();
            pair.add(elemType);
            pair.add(union);
            UnionType ut = new UnionType(unit);
            ut.setCaseTypes(pair);
            union = ut.getType();
            if (variadic && i==last) {
                result = atLeastOne ?
                        unit.getSequenceType(elemType) :
                        unit.getSequentialType(elemType);
            }
            else {
                result = producedType(unit.getTupleDeclaration(),
                        union, elemType, result);
                if (firstDefaulted>=0 && i>=firstDefaulted) {
                    pair = new ArrayList<ProducedType>();
                    pair.add(unit.getType(unit.getEmptyDeclaration()));
                    pair.add(result);
                    ut = new UnionType(unit);
                    ut.setCaseTypes(pair);
                    result = ut.getType();
                }
            }
        }
        return result;
    }
   
    @Override
    public void visit(Tree.BaseType that) {
        super.visit(that);
        TypeDeclaration type = getTypeDeclaration(that.getScope(),
                name(that.getIdentifier()), null, false, that.getUnit());
        String name = name(that.getIdentifier());
        if (type==null) {
            that.addError("type declaration does not exist: '" + name + "'", 102);
            unit.getUnresolvedReferences().add(that.getIdentifier());
        }
        else {
            ProducedType outerType = that.getScope().getDeclaringType(type);
            visitSimpleType(that, outerType, type);
        }
    }
   
    public void visit(Tree.SuperType that) {
        //if (inExtendsClause) { //can't appear anywhere else in the tree!
            ClassOrInterface ci = getContainingClassOrInterface(that.getScope());
            if (ci!=null) {
                if (ci.isClassOrInterfaceMember()) {
                    ClassOrInterface oci = (ClassOrInterface) ci.getContainer();
                    that.setTypeModel(intersectionOfSupertypes(oci));
                }
                else {
                    that.addError("super appears in extends for non-member class");
                }
            }
        //}
    }
   
    @Override
    public void visit(MemberLiteral that) {
        super.visit(that);
        if (that.getType()!=null) {
            ProducedType pt = that.getType().getTypeModel();
            if (pt!=null) {
                if (that.getTypeArgumentList()!=null &&
                        isTypeUnknown(pt) && !pt.isUnknown()) {
                    that.getTypeArgumentList()
                            .addError("qualifying type does not fully-specify type arguments");
                }
            }
        }
    }
   
    @Override
    public void visit(Tree.QualifiedType that) {
        super.visit(that);
        ProducedType pt = that.getOuterType().getTypeModel();
        if (pt!=null) {
            if (that.getMetamodel() &&
                    that.getTypeArgumentList()!=null &&
                    isTypeUnknown(pt) && !pt.isUnknown()) {
                that.getTypeArgumentList()
                        .addError("qualifying type does not fully-specify type arguments");
            }
            TypeDeclaration d = pt.getDeclaration();
            String name = name(that.getIdentifier());
            TypeDeclaration type = getTypeMember(d, name, null, false, unit);
            if (type==null) {
                if (d.isMemberAmbiguous(name, unit, null, false)) {
                    that.addError("member type declaration is ambiguous: '" +
                            name + "' for type '" + d.getName() + "'");
                }
                else {
                    that.addError("member type declaration does not exist: '" +
                            name + "' in type '" + d.getName() + "'", 100);
                    unit.getUnresolvedReferences().add(that.getIdentifier());
                }
            }
            else {
                visitSimpleType(that, pt, type);
            }
        }
    }

    private void visitSimpleType(Tree.SimpleType that, ProducedType ot,
            TypeDeclaration dec) {
        Tree.TypeArgumentList tal = that.getTypeArgumentList();
        List<TypeParameter> params = dec.getTypeParameters();
        List<ProducedType> ta = getTypeArguments(tal, params, ot);
        //if (acceptsTypeArguments(dec, ta, tal, that)) {
        ProducedType pt = dec.getProducedType(ot, ta);
        //dec = pt.getDeclaration();
        if (tal!=null) {
            tal.setTypeModels(ta);
            //TODO: dupe of logic in ExpressionVisitor
            List<Tree.Type> args = tal.getTypes();
            for (int i = 0; i<args.size(); i++) {
                Tree.Type t = args.get(i);
                if (t instanceof Tree.StaticType) {
                    TypeVariance variance =
                            ((Tree.StaticType) t).getTypeVariance();
                    if (variance!=null) {
                        TypeParameter p = params.get(i);
                        if (p.isInvariant()) {
                            if (variance.getText().equals("out")) {
                                pt.setVariance(p, OUT);
                            }
                            else if (variance.getText().equals("in")) {
                                pt.setVariance(p, IN);
                            }
                        }
                    }
                }
            }
        }
        that.setTypeModel(pt);
        that.setDeclarationModel(dec);
    }
   
    @Override
    public void visit(Tree.VoidModifier that) {
        Class vtd = unit.getAnythingDeclaration();
        if (vtd!=null) {
            that.setTypeModel(vtd.getType());
        }
    }

    @Override
    public void visit(Tree.SequencedType that) {
        super.visit(that);
        ProducedType type = that.getType().getTypeModel();
        if (type!=null) {
            ProducedType et = that.getAtLeastOne() ?
                    unit.getSequenceType(type) :
                    unit.getSequentialType(type);
            that.setTypeModel(et);
        }
    }

    @Override
    public void visit(Tree.DefaultedType that) {
        super.visit(that);
        ProducedType type = that.getType().getTypeModel();
        if (type!=null) {
            that.setTypeModel(type);
        }
    }

    @Override
    public void visit(Tree.SpreadType that) {
        super.visit(that);
        Tree.Type t = that.getType();
        if (t!=null) {
            ProducedType type = t.getTypeModel();
            if (type!=null) {
                that.setTypeModel(type);
            }
        }
    }

    @Override
    public void visit(Tree.TypedDeclaration that) {
        super.visit(that);
        TypedDeclaration dec = that.getDeclarationModel();
        setType(that, that.getType(), dec);
        if (dec instanceof MethodOrValue) {
            if (dec.isLate() && ((MethodOrValue) dec).isParameter()) {
                that.addError("parameter may not be annotated late");
            }
        }
    }

    @Override
    public void visit(Tree.TypedArgument that) {
        super.visit(that);
        setType(that, that.getType(), that.getDeclarationModel());
    }
       
    /*@Override
    public void visit(Tree.FunctionArgument that) {
        super.visit(that);
        setType(that, that.getType(), that.getDeclarationModel());
    }*/
       
    private void setType(Node that, Tree.Type type, TypedDeclaration td) {
        if (type==null) {
            that.addError("missing type of declaration: '" + td.getName() + "'");
        }
        else if (!(type instanceof Tree.LocalModifier)) { //if the type declaration is missing, we do type inference later
            ProducedType t = type.getTypeModel();
            if (t!=null) {
                td.setType(t);
            }
        }
    }
   
    private void defaultSuperclass(Tree.ExtendedType et,
            TypeDeclaration cd) {
        if (et==null) {
            Class bd = unit.getBasicDeclaration();
            if (bd!=null) {
                cd.setExtendedType(bd.getType());
            }
        }
    }

    @Override
    public void visit(Tree.ObjectDefinition that) {
        Class o = that.getAnonymousClass();
        o.setExtendedType(null);
        o.getSatisfiedTypes().clear();
        defaultSuperclass(that.getExtendedType(), o);
        super.visit(that);
    }

    @Override
    public void visit(Tree.ObjectArgument that) {
        Class o = that.getAnonymousClass();
        o.setExtendedType(null);
        o.getSatisfiedTypes().clear();
        defaultSuperclass(that.getExtendedType(), o);
        super.visit(that);
    }

    @Override
    public void visit(Tree.ClassDefinition that) {
        Class cd = that.getDeclarationModel();
        cd.setExtendedType(null);
        cd.getSatisfiedTypes().clear();
        Class vd = unit.getAnythingDeclaration();
        if (vd != null && !vd.equals(cd)) {
            defaultSuperclass(that.getExtendedType(), cd);
        }
        super.visit(that);
    }

    @Override
    public void visit(Tree.InterfaceDefinition that) {
        Interface id = that.getDeclarationModel();
        id.setExtendedType(null);
        id.getSatisfiedTypes().clear();
        Class od = unit.getObjectDeclaration();
        if (od!=null) {
            id.setExtendedType(od.getType());
        }
        super.visit(that);
    }

    @Override
    public void visit(Tree.TypeParameterDeclaration that) {
        TypeParameter tpd = that.getDeclarationModel();
        tpd.setExtendedType(null);
        tpd.getSatisfiedTypes().clear();
        Class vd = unit.getAnythingDeclaration();
        if (vd!=null) {
            tpd.setExtendedType(vd.getType());
        }
        super.visit(that);
        Tree.TypeSpecifier ts = that.getTypeSpecifier();
        if (ts!=null) {
            Tree.StaticType type = ts.getType();
            if (type!=null) {
                ProducedType dta = type.getTypeModel();
                if (dta!=null && dta.containsDeclaration(tpd.getDeclaration())) {
                    type.addError("default type argument involves parameterized type");
                }
                /*else if (t.containsTypeParameters()) {
                    type.addError("default type argument involves type parameters: " +
                    t.getProducedTypeName());
                }*/
                else {
                    tpd.setDefaultTypeArgument(dta);
                }
            }
        }
    }
   
    @Override
    public void visit(Tree.TypeParameterList that) {
        super.visit(that);
        List<Tree.TypeParameterDeclaration> tpds = that.getTypeParameterDeclarations();
        List<TypeParameter> params = new ArrayList<TypeParameter>(tpds.size());
        for (int i=tpds.size()-1; i>=0; i--) {
            Tree.TypeParameterDeclaration tpd = tpds.get(i);
            if (tpd!=null) {
                TypeParameter tp = tpd.getDeclarationModel();
                if (tp.getDefaultTypeArgument()!=null) {
                    params.add(tp);
                    if (tp.getDefaultTypeArgument().containsTypeParameters(params)) {
                        tpd.getTypeSpecifier().addError("default type argument involves a type parameter not yet declared");
                    }
                }
            }
        }
    }
   
    @Override
    public void visit(Tree.ClassDeclaration that) {
        Class td = that.getDeclarationModel();
        td.setExtendedType(null);
        super.visit(that);
        Tree.ClassSpecifier cs = that.getClassSpecifier();
        if (cs==null) {
            that.addError("missing class body or aliased class reference");
        }
        else {
            if (that.getExtendedType()!=null) {
                that.getExtendedType().addError("class alias may not extend a type");
            }
            if (that.getSatisfiedTypes()!=null) {
                that.getSatisfiedTypes().addError("class alias may not satisfy a type");
            }
            if (that.getCaseTypes()!=null) {
                that.addError("class alias may not have cases or a self type");
            }
            Tree.SimpleType ct = cs.getType();
            if (ct==null) {
//                that.addError("malformed aliased class");
            }
            else if (!(ct instanceof Tree.StaticType)) {
                cs.addError("aliased type must be a class");
            }
            /*else if (ct instanceof Tree.QualifiedType) {
                cs.addError("aliased class may not be a qualified type");
            }*/
            else {
                ProducedType type = ct.getTypeModel();
                if (type!=null) {
                    /*if (type.containsTypeAliases()) {
                        et.addError("aliased type involves type aliases: " +
                                type.getProducedTypeName());
                    }
                    else*/ if (type.getDeclaration() instanceof Class) {
                        that.getDeclarationModel().setExtendedType(type);
                    }
                    else {
                        ct.addError("not a class: '" +
                                type.getDeclaration().getName(unit) + "'");
                    }
                    TypeDeclaration etd = ct.getDeclarationModel();
                    if (etd==td) {
                        //TODO: handle indirect circularities!
                        ct.addError("directly aliases itself: '" + td.getName() + "'");
                        return;
                    }
                }
            }
        }
    }
   
    @Override
    public void visit(Tree.InterfaceDeclaration that) {
        Interface id = that.getDeclarationModel();
        id.setExtendedType(null);
        super.visit(that);
        if (that.getTypeSpecifier()==null) {
            that.addError("missing interface body or aliased interface reference");
        }
        else {
            if (that.getSatisfiedTypes()!=null) {
                that.getSatisfiedTypes().addError("interface alias may not satisfy a type");
            }
            if (that.getCaseTypes()!=null) {
                that.addError("class alias may not have cases or a self type");
            }
            Tree.StaticType et = that.getTypeSpecifier().getType();
            if (et==null) {
//                that.addError("malformed aliased interface");
            }
            else if (!(et instanceof Tree.StaticType)) {
                that.getTypeSpecifier()
                        .addError("aliased type must be an interface");
            }
            else {
                ProducedType type = et.getTypeModel();
                if (type!=null) {
                    /*if (type.containsTypeAliases()) {
                        et.addError("aliased type involves type aliases: " +
                                type.getProducedTypeName());
                    }
                    else*/ if (type.getDeclaration() instanceof Interface) {
                        id.setExtendedType(type);
                    }
                    else {
                        et.addError("not an interface: '" +
                                type.getDeclaration().getName(unit) + "'");
                    }
                }
            }
        }
    }
   
    @Override
    public void visit(Tree.TypeAliasDeclaration that) {
        TypeAlias ta = that.getDeclarationModel();
        ta.setExtendedType(null);
        super.visit(that);
        if (that.getSatisfiedTypes()!=null) {
            that.getSatisfiedTypes().addError("type alias may not satisfy a type");
        }
        if (that.getTypeSpecifier()==null) {
            that.addError("missing aliased type");
        }
        else {
            Tree.StaticType et = that.getTypeSpecifier().getType();
            if (et==null) {
                that.addError("malformed aliased type");
            }
            else {
                ProducedType type = et.getTypeModel();
                if (type!=null) {
                    /*if (type.containsTypeAliases()) {
                        et.addError("aliased type involves type aliases: " +
                                type.getProducedTypeName());
                    }
                    else {*/
                        ta.setExtendedType(type);
                    //}
                }
            }
        }
    }
   
    @Override
    public void visit(Tree.MethodDeclaration that) {
        super.visit(that);
        Tree.SpecifierExpression sie = that.getSpecifierExpression();
        Method dec = that.getDeclarationModel();
        if (dec!=null && dec.isParameter() &&
                dec.getInitializerParameter().isHidden()) {
            if (sie!=null) {
                sie.addError("function is an initializer parameter and may not have an initial value: '" +
                        dec.getName() + "'");
            }
        }
    }
   
    @Override
    public void visit(Tree.MethodDefinition that) {
        super.visit(that);
        Method dec = that.getDeclarationModel();
        if (dec!=null && dec.isParameter() &&
                dec.getInitializerParameter().isHidden()) {
            that.getBlock().addError("function is an initializer parameter and may not have a body: '" +
                    dec.getName() + "'");
        }
    }
   
    @Override
    public void visit(Tree.AttributeDeclaration that) {
        super.visit(that);
        Tree.SpecifierOrInitializerExpression sie = that.getSpecifierOrInitializerExpression();
        Value dec = that.getDeclarationModel();
        if (dec!=null && dec.isParameter() &&
                dec.getInitializerParameter().isHidden()) {
            Parameter param = dec.getInitializerParameter();
            if (that.getType() instanceof Tree.SequencedType) {
                param.setSequenced(true);
                param.setAtLeastOne(((Tree.SequencedType)that.getType()).getAtLeastOne());
            }
            if (sie!=null) {
                sie.addError("value is an initializer parameter and may not have an initial value: '" +
                        dec.getName() + "'");
            }
        }
    }
   
    @Override
    public void visit(Tree.AttributeGetterDefinition that) {
        super.visit(that);
        Value dec = that.getDeclarationModel();
        if (dec!=null && dec.isParameter() &&
                dec.getInitializerParameter().isHidden()) {
            that.getBlock().addError("value is an initializer parameter and may not have a body: '" +
                    dec.getName() + "'");
        }
    }
   
    @Override
    public void visit(Tree.ExtendedType that) {
        super.visit(that);
        TypeDeclaration td = (TypeDeclaration) that.getScope();
        if (td.isAlias()) {
            return;
        }
        Tree.SimpleType et = that.getType();
        if (et==null) {
//            that.addError("malformed extended type");
        }
        else {
            /*if (et instanceof Tree.QualifiedType) {
                Tree.QualifiedType st = (Tree.QualifiedType) et;
                ProducedType pt = st.getOuterType().getTypeModel();
                if (pt!=null) {
                    TypeDeclaration superclass = (TypeDeclaration) getMemberDeclaration(pt.getDeclaration(), st.getIdentifier(), context);
                    if (superclass==null) {
                        that.addError("member type declaration not found: " +
                                st.getIdentifier().getText());
                    }
                    else {
                        visitExtendedType(st, pt, superclass);
                    }
                }
            }*/
            ProducedType type = et.getTypeModel();
            if (type!=null) {
                TypeDeclaration etd = et.getDeclarationModel();
                if (etd!=null) {
                    if (etd==td) {
                        //TODO: handle indirect circularities!
                        et.addError("directly extends itself: '" + td.getName() + "'");
                    }
                    else if (etd instanceof TypeParameter) {
                        et.addError("directly extends a type parameter: '" +
                                type.getDeclaration().getName(unit) + "'");
                    }
                    else if (etd instanceof Interface) {
                        et.addError("extends an interface: '" +
                                type.getDeclaration().getName(unit) + "'");
                    }
                    else if (etd instanceof TypeAlias) {
                        et.addError("extends a type alias: '" +
                                type.getDeclaration().getName(unit) + "'");
                    }
                    else {
                        td.setExtendedType(type);
                    }
                }
            }
        }
    }
   
    @Override
    public void visit(Tree.SatisfiedTypes that) {
        super.visit(that);
        TypeDeclaration td = (TypeDeclaration) that.getScope();
        if (td.isAlias()) {
            return;
        }
        List<ProducedType> list = new ArrayList<ProducedType>(that.getTypes().size());
        if ( that.getTypes().isEmpty() ) {
            that.addError("missing types in satisfies");
        }
        boolean foundTypeParam = false;
        boolean foundClass = false;
        boolean foundInterface = false;
        for (Tree.StaticType st: that.getTypes()) {
            ProducedType type = st.getTypeModel();
            if (type!=null) {
                TypeDeclaration std = type.getDeclaration();
                if (std!=null && !(std instanceof UnknownType)) {
                    if (std==td) {
                        //TODO: handle indirect circularities!
                        st.addError("directly extends itself: '" + td.getName() + "'");
                        continue;
                    }
                    if (std instanceof TypeAlias) {
                        st.addError("satisfies a type alias: '" +
                                type.getDeclaration().getName(unit) + "'");
                        continue;
                    }
                    if (td instanceof TypeParameter) {
                        if (foundTypeParam) {
                            st.addUnsupportedError("type parameter upper bounds are not yet supported in combination with other bounds");
                        }
                        else if (std instanceof TypeParameter) {
                            if (foundClass||foundInterface) {
                                st.addUnsupportedError("type parameter upper bounds are not yet supported in combination with other bounds");
                            }
                            foundTypeParam = true;
                        }
                        else if (std instanceof Class) {
                            if (foundClass) {
                                st.addUnsupportedError("multiple class upper bounds are not yet supported");
                            }
                            foundClass = true;
                        }
                        else if (std instanceof Interface) {
                            foundInterface = true;
                        }
                        else {
                            st.addError("upper bound must be a class, interface, or type parameter");
                            continue;
                        }
                    }
                    else {
                        if (std instanceof TypeParameter) {
                            st.addError("directly satisfies type parameter: '" + std.getName(unit) + "'");
                            continue;
                        }
                        else if (std instanceof Class) {
                            st.addError("satisfies a class: '" + std.getName(unit) + "'");
                            continue;
                        }
                        else if (std instanceof Interface) {
                            if (td.isDynamic() && !std.isDynamic()) {
                                st.addError("dynamic interface satisfies a non-dynamic interface: '" +
                                        std.getName(unit) + "'");
                            }
                        }
                        else {
                            st.addError("satisfied type must be an interface");
                            continue;
                        }
                    }
                    list.add(type);
                }
            }
        }
        td.setSatisfiedTypes(list);
    }
   
    /*@Override
    public void visit(Tree.TypeConstraint that) {
        super.visit(that);
        if (that.getSelfType()!=null) {
            TypeDeclaration td = (TypeDeclaration) that.getSelfType().getScope();
            TypeParameter tp = that.getDeclarationModel();
            td.setSelfType(tp.getType());
            if (tp.isSelfType()) {
                that.addError("type parameter may not act as self type for two different types");
            }
            else {
                tp.setSelfTypedDeclaration(td);
            }
        }
    }*/

    @Override
    public void visit(Tree.CaseTypes that) {
        super.visit(that);
        TypeDeclaration td = (TypeDeclaration) that.getScope();
        List<BaseMemberExpression> bmes = that.getBaseMemberExpressions();
        List<Tree.StaticType> cts = that.getTypes();
        List<ProducedType> list = new ArrayList<ProducedType>(bmes.size()+cts.size());
        if (td instanceof TypeParameter) {
            if (!bmes.isEmpty()) {
                that.addError("cases of type parameter must be a types");
            }
        }
        else {
            for (Tree.BaseMemberExpression bme: bmes) {
                //bmes have not yet been resolved
                TypedDeclaration od = getTypedDeclaration(bme.getScope(),
                        name(bme.getIdentifier()), null, false, bme.getUnit());
                if (od!=null) {
                    ProducedType type = od.getType();
                    if (type!=null) {
                        list.add(type);
                    }
                }
            }
        }
        for (Tree.StaticType st: cts) {
            ProducedType type = st.getTypeModel();
            if (type!=null) {
                TypeDeclaration ctd = type.getDeclaration();
                if (ctd!=null) {
                    if (ctd instanceof UnionType || ctd instanceof IntersectionType) {
                        //union/intersection types don't have equals()
                        if (td instanceof TypeParameter) {
                            st.addError("enumerated bound must be a class or interface type");
                        }
                        else {
                            st.addError("case type must be a class, interface, or self type");
                        }
                    }
                    else if (ctd.equals(td)) {
                        st.addError("directly enumerates itself: '" + td.getName() + "'");
                        continue;
                    }
                    else if (ctd instanceof TypeParameter) {
                        if (!(td instanceof TypeParameter)) {
                            TypeParameter tp = (TypeParameter) ctd;
                            td.setSelfType(type);
                            if (tp.isSelfType()) {
                                st.addError("type parameter may not act as self type for two different types");
                            }
                            else {
                                tp.setSelfTypedDeclaration(td);
                            }
                            if (cts.size()>1) {
                                st.addError("a type may not have more than one self type");
                            }
                        }
                        else {
                            //TODO: error?!
                        }
                    }
                    else if (ctd instanceof ClassOrInterface) {
                        //nothing special to do
                    }
                    else {
                        if (td instanceof TypeParameter) {
                            st.addError("enumerated bound must be a class or interface type");
                        }
                        else {
                            st.addError("case type must be a class, interface, or self type");
                        }
                        continue;
                    }
                    list.add(type);
                }
            }
        }
        if (!list.isEmpty()) {
            if (list.size() == 1 && list.get(0).getDeclaration().isSelfType()) {
                Scope s = list.get(0).getDeclaration().getContainer();
                if (s instanceof ClassOrInterface && !((ClassOrInterface) s).isAbstract()) {
                    that.addError("non-abstract class parameterized by self type: '" +
                            td.getName() + "'", 905);
                }
            }
            else {
                if (td instanceof ClassOrInterface && !((ClassOrInterface) td).isAbstract()) {
                    that.addError("non-abstract class has enumerated subtypes: '" +
                            td.getName() + "'", 905);
                }
            }
            td.setCaseTypes(list);
        }
    }

    @Override
    public void visit(Tree.InitializerParameter that) {
        super.visit(that);
        //i.e. an attribute initializer parameter
        Parameter p = that.getParameterModel();
        Declaration a = that.getScope().getDirectMember(p.getName(), null, false);
        if (a==null) {
            //Now done in ExpressionVisitor!
//            that.addError("parameter declaration does not exist: '" + p.getName() + "'");
        }
        else if (!isLegalParameter(a)) {
            that.addError("parameter is not a reference value or function: '" + p.getName() + "'");
        }
        else {
            if (a.isFormal()) {
                that.addError("parameter is a formal attribute: '" + p.getName() + "'", 320);
            }
            /*else if (a.isDefault()) {
                that.addError("initializer parameter refers to a default attribute: " +
                        d.getName());
            }*/
            MethodOrValue mov = (MethodOrValue) a;
            mov.setInitializerParameter(p);
            p.setModel(mov);
        }
        /*if (d.isHidden() && d.getDeclaration() instanceof Method) {
            if (a instanceof Method) {
                that.addWarning("initializer parameters for inner methods of methods not yet supported");
            }
            if (a instanceof Value && ((Value) a).isVariable()) {
                that.addWarning("initializer parameters for variables of methods not yet supported");
            }
        }*/
        if (a instanceof Generic && !((Generic) a).getTypeParameters().isEmpty()) {
            that.addError("parameter declaration has type parameters: '" +
                    p.getName() + "' may not declare type parameters");
        }
        if (p.isDefaulted()) {
            checkDefaultArg(that.getSpecifierExpression(), p);
        }
    }

    public boolean isLegalParameter(Declaration a) {
        if (a instanceof Value) {
            Value v = (Value) a;
            return !v.isTransient() &&
                    (v.getTypeDeclaration()==null ||
                    !v.getTypeDeclaration().isAnonymous());
        }
        else if (a instanceof Method) {
            return true;
        }
        else {
            return false;
        }
    }
   
    @Override
    public void visit(Tree.AnyAttribute that) {
        super.visit(that);
        if (that.getType() instanceof Tree.SequencedType) {
            Value v = (Value) that.getDeclarationModel();
            Parameter p = v.getInitializerParameter();
            if (p==null) {
                that.getType().addError("value is not a parameter, so may not be variadic: '" +
                        v.getName() + "'");
            }
            else {
                p.setSequenced(true);
            }
        }
    }

    @Override
    public void visit(Tree.AnyMethod that) {
        super.visit(that);
        if (that.getType() instanceof Tree.SequencedType) {
            that.getType().addError("function type may not be variadic");
        }
    }
   
    @Override public void visit(Tree.QualifiedMemberOrTypeExpression that) {
        Tree.Primary p = that.getPrimary();
        if (p instanceof Tree.MemberOrTypeExpression) {
            if (p instanceof Tree.BaseTypeExpression ||
                p instanceof Tree.QualifiedTypeExpression) {
                that.setStaticMethodReference(true);
                ((Tree.MemberOrTypeExpression) p).setStaticMethodReferencePrimary(true);
                if (p instanceof Tree.QualifiedTypeExpression) {
                    Tree.Primary pp = ((Tree.QualifiedTypeExpression) p).getPrimary();
                    if (!(pp instanceof Tree.BaseTypeExpression)
                            && !(pp instanceof Tree.QualifiedTypeExpression)) {
                        that.getPrimary().addError("non-static type expression in static member reference");
                    }
                }
            }
        }
        if (p instanceof Tree.Package) {
            ((Tree.Package) p).setQualifier(true);
        }
        super.visit(that);
    }

    @Override public void visit(Tree.InvocationExpression that) {
        super.visit(that);
        Tree.Term p = Util.unwrapExpressionUntilTerm(that.getPrimary());
        if (p instanceof Tree.MemberOrTypeExpression) {
            Tree.MemberOrTypeExpression mte = (Tree.MemberOrTypeExpression) p;
            mte.setDirectlyInvoked(true);
        }
    }

    private static Tree.SpecifierOrInitializerExpression getSpecifier(
            Tree.ParameterDeclaration that) {
        Tree.TypedDeclaration d = that.getTypedDeclaration();
        if (d instanceof Tree.AttributeDeclaration) {
            return ((Tree.AttributeDeclaration) d)
                    .getSpecifierOrInitializerExpression();
        }
        else if (d instanceof Tree.MethodDeclaration) {
            return ((Tree.MethodDeclaration) that.getTypedDeclaration())
                    .getSpecifierExpression();
        }
        else {
            return null;
        }
    }
   
    private void checkDefaultArg(Tree.SpecifierOrInitializerExpression se, Parameter p) {
        if (se!=null) {
            if (se.getScope() instanceof Specification) {
                se.addError("parameter of specification statement may not define default value");
            }
            else {
                Declaration d = p.getDeclaration();
                if (d.isActual()) {
                    se.addError("parameter of actual declaration may not define default value: parameter '" +
                            p.getName() + "' of '" + p.getDeclaration().getName() + "'");
                }
            }
            /*if (declaration instanceof Method &&
                !declaration.isToplevel() &&
                !(declaration.isClassOrInterfaceMember() &&
                        ((Declaration) declaration.getContainer()).isToplevel())) {
                se.addWarning("default arguments for parameters of inner methods not yet supported");
            }
            if (declaration instanceof Class &&
                    !declaration.isToplevel()) {
                se.addWarning("default arguments for parameters of inner classes not yet supported");
            }*/
                /*else {
                se.addWarning("parameter default values are not yet supported");
            }*/
        }
    }
   
    @Override public void visit(Tree.ParameterDeclaration that) {
        super.visit(that);
        Parameter p = that.getParameterModel();
        if (p.isDefaulted()) {
            if (p.getDeclaration().isParameter()) {
                getSpecifier(that).addError("parameter of callable parameter may not have default argument");
            }
            checkDefaultArg(getSpecifier(that), p);
        }
    }
}
TOP

Related Classes of com.redhat.ceylon.compiler.typechecker.analyzer.TypeVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.