Package com.redhat.ceylon.compiler.java.codegen

Source Code of com.redhat.ceylon.compiler.java.codegen.ClassTransformer$SatisfactionVisitor

/*
* Copyright Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the authors tag. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License version 2.
*
* This particular file is subject to the "Classpath" exception as provided in the
* LICENSE file that accompanied this code.
*
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE.  See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License,
* along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA  02110-1301, USA.
*/

package com.redhat.ceylon.compiler.java.codegen;

import static com.redhat.ceylon.compiler.java.codegen.Naming.DeclNameFlag.QUALIFIED;
import static com.sun.tools.javac.code.Flags.ABSTRACT;
import static com.sun.tools.javac.code.Flags.FINAL;
import static com.sun.tools.javac.code.Flags.INTERFACE;
import static com.sun.tools.javac.code.Flags.PRIVATE;
import static com.sun.tools.javac.code.Flags.PROTECTED;
import static com.sun.tools.javac.code.Flags.PUBLIC;
import static com.sun.tools.javac.code.Flags.STATIC;

import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.redhat.ceylon.compiler.java.codegen.Naming.Substitution;
import com.redhat.ceylon.compiler.java.codegen.Naming.Suffix;
import com.redhat.ceylon.compiler.java.codegen.Naming.SyntheticName;
import com.redhat.ceylon.compiler.java.codegen.Naming.Unfix;
import com.redhat.ceylon.compiler.java.codegen.StatementTransformer.DeferredSpecification;
import com.redhat.ceylon.compiler.java.codegen.recovery.Drop;
import com.redhat.ceylon.compiler.java.codegen.recovery.Errors;
import com.redhat.ceylon.compiler.java.codegen.recovery.HasErrorException;
import com.redhat.ceylon.compiler.java.codegen.recovery.TransformationPlan;
import com.redhat.ceylon.compiler.java.codegen.recovery.ThrowerMethod;
import com.redhat.ceylon.compiler.loader.model.LazyInterface;
import com.redhat.ceylon.compiler.typechecker.model.Class;
import com.redhat.ceylon.compiler.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.compiler.typechecker.model.ControlBlock;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.Functional;
import com.redhat.ceylon.compiler.typechecker.model.Generic;
import com.redhat.ceylon.compiler.typechecker.model.Interface;
import com.redhat.ceylon.compiler.typechecker.model.Method;
import com.redhat.ceylon.compiler.typechecker.model.MethodOrValue;
import com.redhat.ceylon.compiler.typechecker.model.Package;
import com.redhat.ceylon.compiler.typechecker.model.Parameter;
import com.redhat.ceylon.compiler.typechecker.model.ParameterList;
import com.redhat.ceylon.compiler.typechecker.model.ProducedReference;
import com.redhat.ceylon.compiler.typechecker.model.ProducedType;
import com.redhat.ceylon.compiler.typechecker.model.ProducedTypedReference;
import com.redhat.ceylon.compiler.typechecker.model.Scope;
import com.redhat.ceylon.compiler.typechecker.model.Setter;
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.Unit;
import com.redhat.ceylon.compiler.typechecker.model.UnknownType;
import com.redhat.ceylon.compiler.typechecker.model.Value;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AnnotationList;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AnyClass;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AttributeDeclaration;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AttributeGetterDefinition;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AttributeSetterDefinition;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.BaseMemberExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.LazySpecifierExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.MethodDeclaration;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SequencedArgument;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SpecifierExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SpecifierOrInitializerExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SpecifierStatement;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.TypeParameterDeclaration;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.TypeParameterList;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBinary;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCNewClass;
import com.sun.tools.javac.tree.JCTree.JCReturn;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCThrow;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;

/**
* This transformer deals with class/interface declarations
*/
public class ClassTransformer extends AbstractTransformer {

    public static ClassTransformer getInstance(Context context) {
        ClassTransformer trans = context.get(ClassTransformer.class);
        if (trans == null) {
            trans = new ClassTransformer(context);
            context.put(ClassTransformer.class, trans);
        }
        return trans;
    }

    private ClassTransformer(Context context) {
        super(context);
    }

    public List<JCTree> transform(final Tree.ClassOrInterface def) {
        final ClassOrInterface model = def.getDeclarationModel();
       
        // we only create types for aliases so they can be imported with the model loader
        // and since we can't import local declarations let's just not create those types
        // in that case
        if(model.isAlias()
                && Decl.isAncestorLocal(def))
            return List.nil();
       
        naming.clearSubstitutions(model);
        final String javaClassName;
        String ceylonClassName = def.getIdentifier().getText();
        if (def instanceof Tree.AnyInterface) {
            javaClassName = naming.makeTypeDeclarationName(model, QUALIFIED).replaceFirst(".*\\.", "");
        } else {
            javaClassName = Naming.quoteClassName(def.getIdentifier().getText());
        }
        ClassDefinitionBuilder instantiatorImplCb;
        ClassDefinitionBuilder instantiatorDeclCb;
        if (Decl.withinInterface(model)) {
            instantiatorImplCb = gen().current().getCompanionBuilder((Interface)model.getContainer());
            instantiatorDeclCb = gen().current();
        } else {
            instantiatorImplCb = gen().current();
            instantiatorDeclCb = null;
        }
        ClassDefinitionBuilder classBuilder = ClassDefinitionBuilder
                .klass(this, javaClassName, ceylonClassName, Decl.isLocal(model))
                .forDefinition(model);
        TypeParameterList typeParameterList = def.getTypeParameterList();

        // Very special case for Anything
        if ("ceylon.language::Anything".equals(model.getQualifiedNameString())) {
            classBuilder.extending(model.getType(), null);
        }
       
        if (def instanceof Tree.AnyClass) {
            Tree.ParameterList paramList = ((Tree.AnyClass)def).getParameterList();
            Class cls = ((Tree.AnyClass)def).getDeclarationModel();
            // Member classes need a instantiator method
            boolean generateInstantiator = Strategy.generateInstantiator(cls);
            if(generateInstantiator){
                generateInstantiators(cls, classBuilder, paramList, cls, instantiatorDeclCb, instantiatorImplCb, typeParameterList);
            }
            classBuilder.annotations(expressionGen().transform(def.getAnnotationList()));
            if(def instanceof Tree.ClassDefinition){
                transformClass((Tree.ClassDefinition)def, (Class)model, classBuilder, paramList, generateInstantiator, cls, instantiatorDeclCb, instantiatorImplCb, typeParameterList);
            }else{
                // class alias
                classBuilder.constructorModifiers(PRIVATE);
                transformClassAlias((Tree.AnyClass)def, classBuilder);
            }
           
            addMissingUnrefinedMembers(def, cls, classBuilder);
        }
       
        if (def instanceof Tree.AnyInterface) {
            classBuilder.annotations(expressionGen().transform(def.getAnnotationList()));
            if(def instanceof Tree.InterfaceDefinition){
                transformInterface(def, (Interface)model, classBuilder, typeParameterList);
            }else{
                // interface alias
                classBuilder.annotations(makeAtAlias(model.getExtendedType()));
                classBuilder.isAlias(true);
            }
            classBuilder.isDynamic(model.isDynamic());
        }

        // make sure we set the container in case we move it out
        addAtContainer(classBuilder, model);
       
        // Transform the class/interface members
        List<JCStatement> childDefs = visitClassOrInterfaceDefinition(def, classBuilder);

        // If it's a Class without initializer parameters...
        if (Strategy.generateMain(def)) {
            // ... then add a main() method
            classBuilder.method(makeMainForClass(model));
        }
       
        classBuilder
            .modelAnnotations(model.getAnnotations())
            .modifiers(transformClassDeclFlags(def))
            .satisfies(model.getSatisfiedTypes())
            .caseTypes(model.getCaseTypes(), model.getSelfType())
            .init(childDefs);
       
        // aliases don't need a $getType method
        if(!model.isAlias()){
            // only classes get a $getType method
            if(model instanceof Class)
                classBuilder.addGetTypeMethod(model.getType());
            if(supportsReifiedAlias(model))
                classBuilder.reifiedAlias(model.getType());
        }
       
        List<JCTree> result;
        if (Decl.isAnnotationClass(def)) {
            ListBuffer<JCTree> trees = ListBuffer.lb();
            trees.addAll(transformAnnotationClass((Tree.AnyClass)def));
            transformAnnotationClassConstructor((Tree.AnyClass)def, classBuilder);
            trees.addAll(classBuilder.build());
            result = trees.toList();
        } else {
            result = classBuilder.build();
        }
       
        return result;
    }

    /**
     * Recover from members not being refined in the class hierarchy
     * by generating a stub method that throws.
     */
    private void addMissingUnrefinedMembers(
            Node def,
            Class classModel,
            ClassDefinitionBuilder classBuilder) {
       
        for (ProducedReference unrefined : classModel.getUnimplementedFormals()) {
            Declaration formalMember = unrefined.getDeclaration();//classModel.getMember(memberName, null, false);
            String errorMessage  = "formal member '"+formalMember.getName()+"' of '"+((TypeDeclaration)formalMember.getContainer()).getName()+"' not implemented in class hierarchy";
            java.util.List<ProducedType> params = new java.util.ArrayList<ProducedType>();
            if (formalMember instanceof Generic) {
                for (TypeParameter tp: ((Generic) formalMember).getTypeParameters()) {
                    params.add(tp.getType());
                }
            }
            if (formalMember instanceof Value) {
                addRefinedThrowerAttribute(classBuilder, errorMessage, classModel,
                        (Value)formalMember);
            } else if (formalMember instanceof Method) {
                addRefinedThrowerMethod(classBuilder, errorMessage, classModel,
                        (Method)formalMember);
            } else if (formalMember instanceof Class
                    && formalMember.isClassMember()) {
                addRefinedThrowerInstantiatorMethod(classBuilder, errorMessage, classModel,
                        (Class)formalMember, unrefined);
            }
            // formal member class of interface handled in
            // makeDelegateToCompanion()
        }
   
    }

    private void addRefinedThrowerInstantiatorMethod(ClassDefinitionBuilder classBuilder,
            String message, ClassOrInterface classModel, Class formalClass, ProducedReference unrefined) {
        MethodDefinitionBuilder mdb = MethodDefinitionBuilder.systemMethod(this,
                naming.getInstantiatorMethodName(formalClass));
        mdb.modifiers(transformClassDeclFlags(formalClass) &~ABSTRACT);
        for (TypeParameter tp: formalClass.getTypeParameters()) {
            mdb.typeParameter(tp);
            mdb.reifiedTypeParameter(tp);
        }
        for (Parameter formalP : formalClass.getParameterList().getParameters()) {
            ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.systemParameter(this, formalP.getName());
            pdb.sequenced(formalP.isSequenced());
            pdb.defaulted(formalP.isDefaulted());
            pdb.type(makeJavaType(unrefined.getTypedParameter(formalP).getType()), null);
            mdb.parameter(pdb);
        }
        mdb.resultType(makeJavaType(unrefined.getType()), null);
        mdb.body(makeThrowUnresolvedCompilationError(message));
        classBuilder.method(mdb);
    }

    private Iterable<java.util.List<Parameter>> overloads(Functional f) {
        java.util.List<java.util.List<Parameter>> result = new ArrayList<java.util.List<Parameter>>();
        result.add(f.getParameterLists().get(0).getParameters());
        int ii = 0;
        for (Parameter p : f.getParameterLists().get(0).getParameters()) {
            if (p.isDefaulted()
                    || (p.isSequenced() && !p.isAtLeastOne())) {
                result.add(f.getParameterLists().get(0).getParameters().subList(0, ii));
            }
            ii++;
        }
        return result;
    }
   
    private void addRefinedThrowerMethod(ClassDefinitionBuilder classBuilder,
            String error, ClassOrInterface classModel,
            Method formalMethod) {
        Method refined = refineMethod(classModel,
                classModel.getType().getTypedMember(formalMethod, Collections.<ProducedType>emptyList()),
                classModel, formalMethod, classModel.getUnit());
        // TODO When the method in inherited from an interface and is defaulted
        // we need to generate a DPM as well, otherwise the class is missing
        // the DPM and javac barfs.
        for (java.util.List<Parameter> parameterList : overloads(refined)) {
            MethodDefinitionBuilder mdb = MethodDefinitionBuilder.method(this, refined);
            mdb.isOverride(true);
            mdb.modifiers(transformMethodDeclFlags(refined));
            for (TypeParameter tp: formalMethod.getTypeParameters()) {
                mdb.typeParameter(tp);
                mdb.reifiedTypeParameter(tp);
            }
            for (Parameter param : parameterList) {
                mdb.parameter(param, null, 0, false);
            }
            mdb.resultType(refined, 0);
            mdb.body(makeThrowUnresolvedCompilationError(error));
            classBuilder.method(mdb);
        }
    }

    private Class refineClass(
            Scope container,
            ProducedReference pr,
            ClassOrInterface classModel,
            Class formalClass,
            Unit unit) {
        Class refined = new Class();
        refined.setActual(true);
        refined.setShared(formalClass.isShared());
        refined.setContainer(container);
        refined.setExtendedType(formalClass.getType());
        refined.setDeprecated(formalClass.isDeprecated());
        refined.setName(formalClass.getName());
        refined.setRefinedDeclaration(formalClass.getRefinedDeclaration());
        refined.setScope(container);
        //refined.setType(pr.getType());
        refined.setUnit(unit);
        for (ParameterList formalPl : formalClass.getParameterLists()) {
            ParameterList refinedPl = new ParameterList();
            for (Parameter formalP : formalPl.getParameters()){
                Parameter refinedP = new Parameter();
                refinedP.setAtLeastOne(formalP.isAtLeastOne());
                refinedP.setDeclaration(refined);
                refinedP.setDefaulted(formalP.isDefaulted());
                refinedP.setDeclaredAnything(formalP.isDeclaredAnything());
                refinedP.setHidden(formalP.isHidden());
                refinedP.setSequenced(formalP.isSequenced());
                refinedP.setName(formalP.getName());
                final ProducedTypedReference typedParameter = pr.getTypedParameter(formalP);
                MethodOrValue paramModel;
                if (formalP.getModel() instanceof Value) {
                    Value paramValueModel = refineValue((Value)formalP.getModel(), typedParameter, refined, classModel.getUnit());
                    paramValueModel.setInitializerParameter(refinedP);
                    paramModel = paramValueModel;
                } else {
                    Method paramFunctionModel = refineMethod(refined, typedParameter, classModel, (Method)formalP.getModel(), unit);
                    paramFunctionModel.setInitializerParameter(refinedP);
                    paramModel = paramFunctionModel;
                }
                refinedP.setModel(paramModel);
                refinedPl.getParameters().add(refinedP);
            }
            refined.addParameterList(refinedPl);
        }
        return refined;
    }
   
    private Method refineMethod(
            Scope container,
            ProducedTypedReference pr,
            ClassOrInterface classModel,
            Method formalMethod,
            Unit unit) {
        Method refined = new Method();
        refined.setActual(true);
        refined.setShared(formalMethod.isShared());
        refined.setContainer(container);
        refined.setDefault(true);// in case there are subclasses
        refined.setDeferred(false);
        refined.setDeprecated(formalMethod.isDeprecated());
        refined.setName(formalMethod.getName());
        refined.setRefinedDeclaration(formalMethod.getRefinedDeclaration());
        refined.setScope(container);
        refined.setType(pr.getType());
        refined.setUnit(unit);
        refined.setUnboxed(formalMethod.getUnboxed());
        refined.setUntrustedType(formalMethod.getUntrustedType());
        refined.setTypeErased(formalMethod.getTypeErased());
        ArrayList<TypeParameter> refinedTp = new ArrayList<TypeParameter>();;
        for (TypeParameter formalTp : formalMethod.getTypeParameters()) {
            refinedTp.add(formalTp);
        }
        refined.setTypeParameters(refinedTp);
        for (ParameterList formalPl : formalMethod.getParameterLists()) {
            ParameterList refinedPl = new ParameterList();
            for (Parameter formalP : formalPl.getParameters()){
                Parameter refinedP = new Parameter();
                refinedP.setAtLeastOne(formalP.isAtLeastOne());
                refinedP.setDeclaration(refined);
                refinedP.setDefaulted(formalP.isDefaulted());
                refinedP.setDeclaredAnything(formalP.isDeclaredAnything());
                refinedP.setHidden(formalP.isHidden());
                refinedP.setSequenced(formalP.isSequenced());
                refinedP.setName(formalP.getName());
                final ProducedTypedReference typedParameter = pr.getTypedParameter(formalP);
                MethodOrValue paramModel;
                if (formalP.getModel() instanceof Value) {
                    Value paramValueModel = refineValue((Value)formalP.getModel(), typedParameter, refined, classModel.getUnit());
                    paramValueModel.setInitializerParameter(refinedP);
                    paramModel = paramValueModel;
                } else {
                    Method paramFunctionModel = refineMethod(refined, typedParameter, classModel, (Method)formalP.getModel(), unit);
                    paramFunctionModel.setInitializerParameter(refinedP);
                    paramModel = paramFunctionModel;
                }
                refinedP.setModel(paramModel);
                refinedPl.getParameters().add(refinedP);
            }
            refined.addParameterList(refinedPl);
        }
        return refined;
    }

    /**
     * Adds a getter (and possibly a setter) to {@code classBuilder}
     * which throws
     * @param classBuilder The class builder to add the method to
     * @param error The message
     * @param classModel The class which doesn't refine {@code formalAttribute}
     * @param formalAttribute The formal attribute that hasn't been refined in {@code classModel}
     */
    private void addRefinedThrowerAttribute(
            ClassDefinitionBuilder classBuilder, String error,
            ClassOrInterface classModel, Value formalAttribute) {
        Value refined = refineValue(formalAttribute, formalAttribute.getProducedTypedReference(null, null), classModel, classModel.getUnit());
        AttributeDefinitionBuilder getterBuilder = AttributeDefinitionBuilder.getter(this, refined.getName(), refined);
        getterBuilder.skipField();
        getterBuilder.modifiers(transformAttributeGetSetDeclFlags(refined, false));
        getterBuilder.getterBlock(make().Block(0, List.<JCStatement>of(makeThrowUnresolvedCompilationError(error))));
        classBuilder.attribute(getterBuilder);
        if (formalAttribute.isVariable()) {
            AttributeDefinitionBuilder setterBuilder = AttributeDefinitionBuilder.setter(this, refined.getName(), refined);
            setterBuilder.skipField();
            setterBuilder.modifiers(transformAttributeGetSetDeclFlags(refined, false));
            setterBuilder.setterBlock(make().Block(0, List.<JCStatement>of(makeThrowUnresolvedCompilationError(error))));
            classBuilder.attribute(setterBuilder);
        }
    }

    private Value refineValue(Value formalAttribute,
            ProducedTypedReference producedValue,
            Scope container,
            Unit unit) {
        Value refined = new Value();
        refined.setActual(true);
        refined.setContainer(container);
        refined.setName(formalAttribute.getName());
        refined.setRefinedDeclaration(formalAttribute.getRefinedDeclaration());
        refined.setScope(container);
        refined.setVariable(formalAttribute.isVariable());
       
        refined.setShared(formalAttribute.isShared());
        refined.setTransient(formalAttribute.isTransient());
        refined.setType(producedValue.getType());// TODO
        refined.setTypeErased(formalAttribute.getTypeErased());
        refined.setUnboxed(formalAttribute.getUnboxed());
        refined.setUntrustedType(formalAttribute.getUntrustedType());
        refined.setUnit(unit);
        return refined;
    }

   

    private void transformClassAlias(final Tree.AnyClass def,
            ClassDefinitionBuilder classBuilder) {
        Class model = def.getDeclarationModel();
        ProducedType aliasedClass = model.getExtendedType();
        classBuilder.annotations(makeAtAlias(aliasedClass));
        classBuilder.isAlias(true);
        MethodDefinitionBuilder instantiator = transformClassAliasInstantiator(
                def, model, aliasedClass);
       
        ClassDefinitionBuilder cbInstantiator = null;
        switch (Strategy.defaultParameterMethodOwner(model)) {
        case STATIC:
            cbInstantiator = classBuilder;
            break;
        case OUTER:
            cbInstantiator =  classBuilder.getContainingClassBuilder();
            break;
        case OUTER_COMPANION:
            cbInstantiator = classBuilder.getContainingClassBuilder().getCompanionBuilder(Decl.getClassOrInterfaceContainer(model, true));
            break;
        default:
            throw BugException.unhandledEnumCase(Strategy.defaultParameterMethodOwner(model));
        }
       
        cbInstantiator.method(instantiator);
    }

    /**
     * Builds the instantiator method for a class aliases. In 1.0 you can't
     * actually invoke these, they exist just so there's somewhere to put the
     * class alias annotations. In 1.2 (when we fix #1295) the
     * instantiators will actually do something.
     */
    private MethodDefinitionBuilder transformClassAliasInstantiator(
            final Tree.AnyClass def, Class model, ProducedType aliasedClass) {
        MethodDefinitionBuilder instantiator = MethodDefinitionBuilder.systemMethod(this, naming.getAliasInstantiatorMethodName(model));
        int f = 0;
        if (Strategy.defaultParameterMethodStatic(def.getDeclarationModel())) {
            f = STATIC;
        }
        instantiator.modifiers((transformClassDeclFlags(def) & ~FINAL)| f);
        for (TypeParameter tp : typeParametersOfAllContainers(model, true)) {
            instantiator.typeParameter(tp);
        }
       
        instantiator.resultType(null, makeJavaType(aliasedClass));
        instantiator.annotationFlags(Annotations.MODEL_AND_USER | Annotations.IGNORE);
        // We need to reify the parameters, at least so they have reified annotations
       
       
        for (final Tree.Parameter param : def.getParameterList().getParameters()) {
            // Overloaded instantiators
            Parameter paramModel = param.getParameterModel();
            at(param);

            List<JCAnnotation> annotations = expressionGen().transform(Decl.getAnnotations(def, param));
            transformParameter(instantiator, paramModel, annotations);
        }
        instantiator.body(make().Throw(makeNewClass(makeJavaType(typeFact().getExceptionDeclaration().getType(), JT_CLASS_NEW))));
        return instantiator;
    }

    /**
     * Generates a constructor for an annotation class which takes the
     * annotation type as parameter.
     * @param classBuilder
     */
    private void transformAnnotationClassConstructor(
            Tree.AnyClass def,
            ClassDefinitionBuilder classBuilder) {
        Class klass = def.getDeclarationModel();
        MethodDefinitionBuilder annoCtor = classBuilder.addConstructor();
        annoCtor.ignoreModelAnnotations();
        // constructors are never final
        annoCtor.modifiers(transformClassDeclFlags(klass) & ~FINAL);
        ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.systemParameter(this, "anno");
        pdb.type(makeJavaType(klass.getType(), JT_ANNOTATION), null);
        annoCtor.parameter(pdb);
       
        // It's up to the caller to invoke value() on the Java annotation for a sequenced
        // annotation
       
        ListBuffer<JCExpression> args = ListBuffer.lb();
        for (Tree.Parameter parameter : def.getParameterList().getParameters()) {
            at(parameter);
            Parameter parameterModel = parameter.getParameterModel();
            JCExpression annoAttr = make().Apply(null, naming.makeQuotedQualIdent(naming.makeUnquotedIdent("anno"),
                    parameter.getParameterModel().getName()),
                    List.<JCExpression>nil());
            ProducedType parameterType = parameterModel.getType();
            JCExpression argExpr;
            if (typeFact().isIterableType(parameterType)
                    && !isCeylonString(parameterType)) {
                // Convert from array to Sequential
                ProducedType iteratedType = typeFact().getIteratedType(parameterType);
                if (isCeylonBasicType(iteratedType)) {
                    argExpr = utilInvocation().sequentialWrapperBoxed(annoAttr);
                } else if (Decl.isAnnotationClass(iteratedType.getDeclaration())) {
                    // Can't use Util.sequentialAnnotation becase we need to 'box'
                    // the Java annotations in their Ceylon annotation class
                    argExpr = make().Apply(null, naming.makeUnquotedIdent(naming.getAnnotationSequenceMethodName()), List.of(annoAttr));
                    ListBuffer<JCStatement> stmts = ListBuffer.lb();
                    SyntheticName array = naming.synthetic(Unfix.$array$);
                    SyntheticName sb = naming.synthetic(Unfix.$sb$);
                    SyntheticName index = naming.synthetic(Unfix.$index$);
                    SyntheticName element = naming.synthetic(Unfix.$element$);
                    stmts.append(makeVar(FINAL, sb,
                            make().TypeArray(make().Type(syms().objectType)),
                            make().NewArray(make().Type(syms().objectType), List.of(naming.makeQualIdent(array.makeIdent(), "length")), null)));
                    stmts.append(makeVar(index,
                            make().Type(syms().intType),
                            make().Literal(0)));
                    stmts.append(make().ForeachLoop(
                            makeVar(element, makeJavaType(iteratedType, JT_ANNOTATION), null),
                            array.makeIdent(),
                            make().Exec(make().Assign(
                                    make().Indexed(sb.makeIdent(),
                                            make().Unary(JCTree.POSTINC, index.makeIdent())),
                                    instantiateAnnotationClass(iteratedType, element.makeIdent())))));
                    stmts.append(make().Return(
                            make().NewClass(null,
                                    null,
                                    make().QualIdent(syms().ceylonTupleType.tsym),
                                    List.of(makeReifiedTypeArgument(iteratedType),
                                            sb.makeIdent(),
                                            makeEmpty(),
                                            make().Literal(false)),
                                    null)));
                    classBuilder.method(
                            MethodDefinitionBuilder.systemMethod(this, naming.getAnnotationSequenceMethodName())
                                .ignoreModelAnnotations()
                                .modifiers(PRIVATE | STATIC)
                                .resultType(null, makeJavaType(typeFact().getSequentialType(iteratedType)))
                                .parameter(ParameterDefinitionBuilder.systemParameter(this, array.getName())
                                        .type(make().TypeArray(makeJavaType(iteratedType, JT_ANNOTATION)), null))
                                .body(stmts.toList()));
                } else if (isCeylonMetamodelDeclaration(iteratedType)) {
                    argExpr = makeMetamodelInvocation("parseMetamodelReferences",
                            List.<JCExpression>of(makeReifiedTypeArgument(iteratedType), annoAttr),
                            List.<JCExpression>of(makeJavaType(iteratedType, JT_TYPE_ARGUMENT)));
                } else if (Decl.isEnumeratedTypeWithAnonCases(iteratedType)) {
                    argExpr = makeMetamodelInvocation("parseEnumerationReferences",
                            List.<JCExpression>of(makeReifiedTypeArgument(iteratedType), annoAttr),
                            List.<JCExpression>of(makeJavaType(iteratedType, JT_TYPE_ARGUMENT)));
                } else {
                    argExpr = makeErroneous(parameter, "compiler bug");
                }
            } else if (Decl.isAnnotationClass(parameterType.getDeclaration())) {
                argExpr = instantiateAnnotationClass(parameterType, annoAttr);
            } else if (isCeylonMetamodelDeclaration(parameterType)) {
                argExpr = makeMetamodelInvocation("parseMetamodelReference",
                            List.<JCExpression>of(annoAttr),
                            List.<JCExpression>of(makeJavaType(parameterType, JT_TYPE_ARGUMENT)));
            } else if (Decl.isEnumeratedTypeWithAnonCases(parameterType)) {
                argExpr = makeMetamodelInvocation("parseEnumerationReference",
                        List.<JCExpression>of(annoAttr),
                        null);
            } else {
                argExpr = annoAttr;
                argExpr = expressionGen().applyErasureAndBoxing(annoAttr, parameterType.withoutUnderlyingType(), false, BoxingStrategy.UNBOXED, parameterType);
            }
            args.add(argExpr);
        }
        annoCtor.body(at(def).Exec(
                make().Apply(null,  naming.makeThis(), args.toList())));
    }

    private JCNewClass instantiateAnnotationClass(
            ProducedType annotationClass,
            JCExpression javaAnnotationInstance) {
        return make().NewClass(null, null, makeJavaType(annotationClass),
                List.<JCExpression>of(javaAnnotationInstance), null);
    }

    /**
     * Transforms an annotation class into a Java annotation type.
     * <pre>
     * annotation class Foo(String s, Integer i=1) {}
     * </pre>
     * is transformed into
     * <pre>
     * @Retention(RetentionPolicy.RUNTIME)
     * @interface Foo$annotation$ {
     *     String s();
     *     long i() default 1;
     * }
     * </pre>
     * If the annotation class is a subtype of SequencedAnnotation a wrapper
     * annotation is also generated:
     * <pre>
     * @Retention(RetentionPolicy.RUNTIME)
     * @interface Foo$annotations${
     *     Foo$annotation$[] value();
     * }
     * </pre>
     */
    private List<JCTree> transformAnnotationClass(Tree.AnyClass def) {
        Class klass = (Class)def.getDeclarationModel();
        String annotationName = Naming.suffixName(Suffix.$annotation$, klass.getName());
        ClassDefinitionBuilder annoBuilder = ClassDefinitionBuilder.klass(this, annotationName, null, false);
       
        // annotations are never explicitly final in Java
        annoBuilder.modifiers(Flags.ANNOTATION | Flags.INTERFACE | (transformClassDeclFlags(def) & ~FINAL));
        annoBuilder.annotations(makeAtRetention(RetentionPolicy.RUNTIME));
        annoBuilder.annotations(makeAtIgnore());
       
        for (Tree.Parameter p : def.getParameterList().getParameters()) {
            Parameter parameterModel = p.getParameterModel();
            annoBuilder.method(makeAnnotationMethod(p));
        }
        List<JCTree> result;
        if (isSequencedAnnotation(klass)) {
            result = annoBuilder.annotations(makeAtAnnotationTarget()).build();
            String wrapperName = Naming.suffixName(Suffix.$annotations$, klass.getName());
            ClassDefinitionBuilder sequencedBuilder = ClassDefinitionBuilder.klass(this, wrapperName, null, false);
            // annotations are never explicitely final in Java
            sequencedBuilder.modifiers(Flags.ANNOTATION | Flags.INTERFACE | (transformClassDeclFlags(def) & ~FINAL));
            sequencedBuilder.annotations(makeAtRetention(RetentionPolicy.RUNTIME));
            MethodDefinitionBuilder mdb = MethodDefinitionBuilder.systemMethod(this, naming.getSequencedAnnotationMethodName());
            mdb.annotationFlags(Annotations.MODEL_AND_USER);
            mdb.modifiers(PUBLIC | ABSTRACT);
            mdb.resultType(null, make().TypeArray(makeJavaType(klass.getType(), JT_ANNOTATION)));
            mdb.noBody();
            ClassDefinitionBuilder sequencedAnnotation = sequencedBuilder.method(mdb);
            sequencedAnnotation.annotations(transformAnnotationConstraints(klass));
            sequencedAnnotation.annotations(makeAtIgnore());
            result = result.appendList(sequencedAnnotation.build());
           
        } else {
            result = annoBuilder.annotations(transformAnnotationConstraints(klass)).build();
        }
       
        return result;
    }
   
    private List<JCAnnotation> makeAtRetention(RetentionPolicy retentionPolicy) {
        return List.of(
                make().Annotation(
                        make().Type(syms().retentionType),
                        List.of(naming.makeQuotedQualIdent(make().Type(syms().retentionPolicyType), retentionPolicy.name()))));
    }
   
    /**
     * Makes {@code @java.lang.annotation.Target(types)}
     * where types are the given element types.
     */
    private List<JCAnnotation> makeAtAnnotationTarget(ElementType... types) {
        List<JCExpression> typeExprs = List.<JCExpression>nil();
        for (ElementType type : types) {
            typeExprs = typeExprs.prepend(naming.makeQuotedQualIdent(make().Type(syms().elementTypeType), type.name()));
        }
        return List.of(
                make().Annotation(
                        make().Type(syms().targetType),
                        List.<JCExpression>of(make().NewArray(null, null, typeExprs))));
    }
   
    private List<JCAnnotation> transformAnnotationConstraints(Class klass) {
        TypeDeclaration meta = (TypeDeclaration)typeFact().getLanguageModuleDeclaration("ConstrainedAnnotation");
        ProducedType constrainedType = klass.getType().getSupertype(meta);
        if (constrainedType != null) {
            ProducedType programElement = constrainedType.getTypeArgumentList().get(2);
            if (programElement.isSubtypeOf(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("ClassOrInterfaceDeclaration")).getType())
                    || programElement.isSubtypeOf(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("Package")).getType())
                    || programElement.isSubtypeOf(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("Module")).getType())) {
                return makeAtAnnotationTarget(ElementType.TYPE);
            } else if (programElement.isSubtypeOf(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("ValueDeclaration")).getType())
                    || programElement.isSubtypeOf(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("FunctionDeclaration")).getType())) {
                return makeAtAnnotationTarget(ElementType.METHOD, ElementType.PARAMETER);
            } else if (programElement.isSubtypeOf(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("Import")).getType())) {
                return makeAtAnnotationTarget(ElementType.FIELD);
            }
        }
        return List.<JCAnnotation>nil();
    }

    private JCExpression transformAnnotationParameterDefault(Tree.Parameter p) {
        Tree.SpecifierOrInitializerExpression defaultArgument = Decl.getDefaultArgument(p);
        Tree.Expression defaultExpression = defaultArgument.getExpression();
        Tree.Term term = defaultExpression.getTerm();
        JCExpression defaultLiteral = null;
        if (term instanceof Tree.Literal
                && !(term instanceof Tree.QuotedLiteral)) {
            defaultLiteral = expressionGen().transform((Tree.Literal)term);
        } else if (term instanceof Tree.BaseMemberExpression) {
            Tree.BaseMemberExpression bme = (Tree.BaseMemberExpression)term;
            Declaration decl = bme.getDeclaration();
            if (isBooleanTrue(decl)) {
                defaultLiteral = makeBoolean(true);
            } else if (isBooleanFalse(decl)) {
                defaultLiteral = makeBoolean(false);
            } else if (typeFact().isEmptyType(bme.getTypeModel())) {
                defaultLiteral = make().NewArray(null, null, List.<JCExpression>nil());
            } else if (Decl.isAnonCaseOfEnumeratedType(bme)) {
                defaultLiteral = makeClassLiteral(bme.getTypeModel());
            } else {
                defaultLiteral = make().Literal(bme.getDeclaration().getQualifiedNameString());
            }
        } else if (term instanceof Tree.MemberOrTypeExpression) {
            Tree.MemberOrTypeExpression mte = (Tree.MemberOrTypeExpression)term;
            defaultLiteral = make().Literal(mte.getDeclaration().getQualifiedNameString());
        } else if (term instanceof Tree.SequenceEnumeration) {
            Tree.SequenceEnumeration seq = (Tree.SequenceEnumeration)term;
            SequencedArgument sequencedArgument = seq.getSequencedArgument();
            defaultLiteral = makeArrayInitializer(sequencedArgument);
        } else if (term instanceof Tree.Tuple) {
            Tree.Tuple seq = (Tree.Tuple)term;
            SequencedArgument sequencedArgument = seq.getSequencedArgument();
            defaultLiteral = makeArrayInitializer(sequencedArgument);
        } else if (term instanceof Tree.InvocationExpression) {
            // Allow invocations of annotation constructors, so long as they're
            // themselves being invoked with permitted arguments
            Tree.InvocationExpression invocation = (Tree.InvocationExpression)term;
            try {
                defaultLiteral = AnnotationInvocationVisitor.transform(expressionGen(), invocation);
            } catch (BugException e) {
                defaultLiteral = e.makeErroneous(this, invocation);
            }
        } else if (term instanceof Tree.MemberLiteral) {
            defaultLiteral = expressionGen().makeMetaLiteralStringLiteralForAnnotation((Tree.MemberLiteral) term);
        } else if (term instanceof Tree.TypeLiteral) {
            defaultLiteral = expressionGen().makeMetaLiteralStringLiteralForAnnotation((Tree.TypeLiteral) term);
        }
        if (defaultLiteral == null) {
            defaultLiteral = makeErroneous(p, "compiler bug: " + p.getParameterModel().getName() + " has an unsupported defaulted parameter expression");
        }
        return defaultLiteral;
    }

    private JCExpression transformAnnotationMethodType(Tree.Parameter parameter) {
        ProducedType parameterType = parameter.getParameterModel().getType();
        JCExpression type = null;
        if (isScalarAnnotationParameter(parameterType)) {
            type = makeJavaType(parameterType.withoutUnderlyingType(), JT_ANNOTATION);
        } else if (isMetamodelReference(parameterType)) {
            type = make().Type(syms().stringType);
        } else if (Decl.isEnumeratedTypeWithAnonCases(parameterType)) {
            type = makeJavaClassTypeBounded(parameterType);
        } else if (typeFact().isIterableType(parameterType)) {
            ProducedType iteratedType = typeFact().getIteratedType(parameterType);
            if (isScalarAnnotationParameter(iteratedType)) {
                JCExpression scalarType = makeJavaType(iteratedType, JT_ANNOTATION);
                type = make().TypeArray(scalarType);
            } else if (isMetamodelReference(iteratedType)) {
                JCExpression scalarType = make().Type(syms().stringType);
                type = make().TypeArray(scalarType);
            } else if (Decl.isEnumeratedTypeWithAnonCases(iteratedType)) {
                JCExpression scalarType = makeJavaClassTypeBounded(iteratedType);
                type = make().TypeArray(scalarType);
            }
        }
        if (type == null) {
            type = makeErroneous(parameter, "compiler bug: " + parameter.getParameterModel().getName() + " has an unsupported annotation parameter type");
        }
        return type;
    }

    private boolean isMetamodelReference(ProducedType parameterType) {
        return isCeylonMetamodelDeclaration(parameterType);
    }
    /**
     * Makes a new Array expression suitable for use in initializing a Java array
     * using as elements the positional arguments
     * of the given {@link Tree.SequencedArgument} (which must
     * be {@link Tree.ListedArgument}s).
     *
     * <pre>
     *     Whatever[] w = <strong>{ listedArg1, listedArg2, ... }</strong>
     *     //             ^---------- this bit ---------^
     * </pre>
     *
     * @param sequencedArgument
     * @return The array initializer expression
     */
    JCExpression makeArrayInitializer(
            Tree.SequencedArgument sequencedArgument) {
        JCExpression defaultLiteral;
        ListBuffer<JCExpression> elements = ListBuffer.<JCTree.JCExpression>lb();
        if (sequencedArgument != null) {
            for (Tree.PositionalArgument arg : sequencedArgument.getPositionalArguments()) {
                if (arg instanceof Tree.ListedArgument) {
                    Tree.ListedArgument la = (Tree.ListedArgument)arg;
                    elements.append(expressionGen().transformExpression(la.getExpression().getTerm(), BoxingStrategy.UNBOXED, la.getExpression().getTypeModel()));
                } else {
                    elements = null;
                    break;
                }
            }
        }
        defaultLiteral = elements == null ? null :
            make().NewArray(null, List.<JCExpression>nil(), elements.toList());
        return defaultLiteral;
    }

    private boolean isScalarAnnotationParameter(ProducedType parameterType) {
        return isCeylonBasicType(parameterType)
                || Decl.isAnnotationClass(parameterType.getDeclaration());
    }

    private List<JCStatement> visitClassOrInterfaceDefinition(Node def, ClassDefinitionBuilder classBuilder) {
        // Transform the class/interface members
        CeylonVisitor visitor = gen().visitor;
       
        final ListBuffer<JCTree> prevDefs = visitor.defs;
        final boolean prevInInitializer = visitor.inInitializer;
        final ClassDefinitionBuilder prevClassBuilder = visitor.classBuilder;
        try {
            visitor.defs = new ListBuffer<JCTree>();
            visitor.inInitializer = true;
            visitor.classBuilder = classBuilder;
            def.visitChildren(visitor);
            return (List<JCStatement>)visitor.getResult().toList();
        } finally {
            visitor.classBuilder = prevClassBuilder;
            visitor.inInitializer = prevInInitializer;
            visitor.defs = prevDefs;
            naming.closeScopedSubstitutions(def.getScope());
        }
    }

    private void generateInstantiators(Class model, ClassDefinitionBuilder classBuilder, Tree.ParameterList paramList,
            Class cls, ClassDefinitionBuilder instantiatorDeclCb, ClassDefinitionBuilder instantiatorImplCb, TypeParameterList typeParameterList) {
        // TODO Instantiators on companion classes
        classBuilder.constructorModifiers(PROTECTED);
       
        if (Decl.withinInterface(cls)) {
            MethodDefinitionBuilder instBuilder = MethodDefinitionBuilder.systemMethod(this, naming.getInstantiatorMethodName(cls));
            DefaultedArgumentOverload overloaded = new DefaultedArgumentInstantiator(daoAbstract, model);
            overloaded.makeOverload(
                    instBuilder,
                    paramList.getModel(),
                    null,
                    typeParameterListModel(typeParameterList));
            instantiatorDeclCb.method(instBuilder);
        }
        if (!Decl.withinInterface(cls)
                || !model.isFormal()) {
            MethodDefinitionBuilder instBuilder = MethodDefinitionBuilder.systemMethod(this, naming.getInstantiatorMethodName(cls));
            DefaultedArgumentOverload overloaded = new DefaultedArgumentInstantiator(!cls.isFormal() ? daoThis : daoAbstract, model);
            overloaded.makeOverload(
                    instBuilder,
                    paramList.getModel(),
                    null,
                    typeParameterListModel(typeParameterList));
            instantiatorImplCb.method(instBuilder);
        }
    }

    private void makeAttributeForValueParameter(ClassDefinitionBuilder classBuilder, Tree.Parameter parameterTree, List<JCAnnotation> annotations) {
        Parameter decl = parameterTree.getParameterModel();
        if (!(decl.getModel() instanceof Value)) {
            return;
        }       
        final Value value = (Value)decl.getModel();
        if (parameterTree instanceof Tree.ValueParameterDeclaration
                && (value.isShared() || value.isCaptured())) {
            makeFieldForParameter(classBuilder, decl);
            AttributeDefinitionBuilder adb = AttributeDefinitionBuilder.getter(this, decl.getName(), decl.getModel());
            adb.modifiers(classGen().transformAttributeGetSetDeclFlags(decl.getModel(), false));
            adb.userAnnotations(annotations);
            classBuilder.attribute(adb);
            if (value.isVariable()) {
                AttributeDefinitionBuilder setter = AttributeDefinitionBuilder.setter(this, decl.getName(), decl.getModel());
                setter.modifiers(classGen().transformAttributeGetSetDeclFlags(decl.getModel(), false));
                setter.userAnnotations(annotations);
                classBuilder.attribute(setter);
            }
        } else if (decl.isHidden()
                        // TODO Isn't this always true here? We know this is a parameter to a Class
                        && (decl.getDeclaration() instanceof TypeDeclaration)) {
            Declaration member = CodegenUtil.findMethodOrValueForParam(decl);
            if (Strategy.createField(decl, (Value)member)) {
                // The field itself is created by when we transform the AttributeDeclaration
                // but it has to be initialized here so all the fields are initialized in parameter order
               
                JCExpression parameterExpr = makeUnquotedIdent(Naming.getAliasedParameterName(decl));
                ProducedTypedReference typedRef = getTypedReference(value);
                ProducedTypedReference nonWideningTypedRef = nonWideningTypeDecl(typedRef);
                ProducedType paramType = nonWideningType(typedRef, nonWideningTypedRef);
                if (!paramType.isExactly(decl.getType())) {
                    // The parameter type follows normal erasure rules, not affected by inheritance
                    // but the attribute respects non-widening rules, so we may need to cast
                    // the parameter to the field type (see #1728)
                    parameterExpr = make().TypeCast(classGen().transformClassParameterType(decl), parameterExpr);
                }
                classBuilder.init(make().Exec(
                        make().Assign(naming.makeQualifiedName(naming.makeThis(), value, Naming.NA_IDENT),
                                parameterExpr)));
            }
        }
    }

    private int transformClassParameterDeclFlags(Parameter param) {
        return param.getModel().isVariable() ? 0 : FINAL;
    }
   
    private void makeFieldForParameter(ClassDefinitionBuilder classBuilder,
            Parameter decl) {
        MethodOrValue model = decl.getModel();
        classBuilder.defs(make().VarDef(make().Modifiers(transformClassParameterDeclFlags(decl) | PRIVATE, makeAtIgnore()),
                names().fromString(Naming.quoteFieldName(decl.getName())),
                classGen().transformClassParameterType(decl), null));
       
        classBuilder.init(make().Exec(make().Assign(
                naming.makeQualifiedName(naming.makeThis(), model, Naming.NA_IDENT),
                naming.makeName(model, Naming.NA_IDENT_PARAMETER_ALIASED))));
    }
   

    private void transformParameter(ParameterizedBuilder classBuilder, Parameter param, List<JCAnnotation> annotations) {

        JCExpression type = makeJavaType(param.getModel(), param.getType(), 0);
        ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.explicitParameter(this, param);
        pdb.aliasName(Naming.getAliasedParameterName(param));
        pdb.sequenced(param.isSequenced());
        pdb.defaulted(param.isDefaulted());
        pdb.type(type, makeJavaTypeAnnotations(param.getModel()));
        pdb.modifiers(transformClassParameterDeclFlags(param));
        if (!(param.getModel().isShared() || param.getModel().isCaptured())) {
            // We load the model for shared parameters from the corresponding member
            pdb.modelAnnotations(param.getModel().getAnnotations());
            pdb.userAnnotations(annotations);
        }

        if (classBuilder instanceof ClassDefinitionBuilder
                && pdb.requiresBoxedVariableDecl()) {
            ((ClassDefinitionBuilder)classBuilder).init(pdb.buildBoxedVariableDecl());
        }
        classBuilder.parameter(pdb);
    }
   
    private void transformClass(com.redhat.ceylon.compiler.typechecker.tree.Tree.AnyClass def, Class model, ClassDefinitionBuilder classBuilder,
            com.redhat.ceylon.compiler.typechecker.tree.Tree.ParameterList paramList, boolean generateInstantiator,
            Class cls, ClassDefinitionBuilder instantiatorDeclCb, ClassDefinitionBuilder instantiatorImplCb, TypeParameterList typeParameterList) {
        // do reified type params first
        if(typeParameterList != null)
            classBuilder.reifiedTypeParameters(typeParameterList);
       
        for (final Tree.Parameter param : paramList.getParameters()) {
            // Overloaded instantiators
           
            Parameter paramModel = param.getParameterModel();
            Parameter refinedParam = CodegenUtil.findParamForDecl(
                    (TypedDeclaration)CodegenUtil.getTopmostRefinedDeclaration(param.getParameterModel().getModel()));
            at(param);

            List<JCAnnotation> annotations = expressionGen().transform(Decl.getAnnotations(def, param));
            transformParameter(classBuilder, paramModel, annotations);
            makeAttributeForValueParameter(classBuilder, param, annotations);
            makeMethodForFunctionalParameter(classBuilder, def, param, annotations);
           
            if (Strategy.hasDefaultParameterValueMethod(paramModel)
                    || Strategy.hasDefaultParameterOverload(paramModel)
                    || (generateInstantiator
                            && refinedParam != null
                            && (Strategy.hasDefaultParameterValueMethod(refinedParam)
                                    || Strategy.hasDefaultParameterOverload(refinedParam)))) {
                ClassDefinitionBuilder cbForDevaultValues;
                ClassDefinitionBuilder cbForDevaultValuesDecls = null;
                switch (Strategy.defaultParameterMethodOwner(model)) {
                case STATIC:
                    cbForDevaultValues = classBuilder;
                    break;
                case OUTER:
                    cbForDevaultValues = classBuilder.getContainingClassBuilder();
                    break;
                case OUTER_COMPANION:
                    cbForDevaultValues = classBuilder.getContainingClassBuilder().getCompanionBuilder(Decl.getClassOrInterfaceContainer(model, true));
                    cbForDevaultValuesDecls = classBuilder.getContainingClassBuilder();
                    break;
                default:
                    cbForDevaultValues = classBuilder.getCompanionBuilder(model);
                }
                if ((Strategy.hasDefaultParameterValueMethod(paramModel)
                            || (refinedParam != null && Strategy.hasDefaultParameterValueMethod(refinedParam)))) {
                    if (!generateInstantiator || Decl.equal(refinedParam, paramModel)) {
                        cbForDevaultValues.method(makeParamDefaultValueMethod(false, def.getDeclarationModel(), paramList, param, typeParameterList));
                        if (cbForDevaultValuesDecls != null) {
                            cbForDevaultValuesDecls.method(makeParamDefaultValueMethod(true, def.getDeclarationModel(), paramList, param, typeParameterList));
                        }
                    } else if (Strategy.hasDelegatedDpm(cls)) {
                        java.util.List<Parameter> parameters = paramList.getModel().getParameters();
                        MethodDefinitionBuilder mdb =
                        makeDelegateToCompanion((Interface)cls.getRefinedDeclaration().getContainer(),
                                paramModel.getModel().getProducedTypedReference(cls.getType(), null),
                                ((TypeDeclaration)cls.getContainer()).getType(),
                                FINAL | transformClassDeclFlags(cls),
                                List.<TypeParameter>nil(), Collections.<java.util.List<ProducedType>>emptyList(),
                                paramModel.getType(),
                                Naming.getDefaultedParamMethodName(cls, paramModel),
                                parameters.subList(0, parameters.indexOf(paramModel)),
                                false,
                                Naming.getDefaultedParamMethodName(cls, paramModel));
                        cbForDevaultValues.method(mdb);
                    }
                }
                boolean addOverloadedConstructor = false;
                if (generateInstantiator) {
                    if (Decl.withinInterface(cls)) {
                        MethodDefinitionBuilder instBuilder = MethodDefinitionBuilder.systemMethod(this, naming.getInstantiatorMethodName(cls));
                        new DefaultedArgumentInstantiator(daoAbstract, model).makeOverload(instBuilder,
                                paramList.getModel(),
                                param.getParameterModel(),
                                typeParameterListModel(typeParameterList));
                        instantiatorDeclCb.method(instBuilder);
                    }
                    if (!Decl.withinInterface(cls) || !cls.isFormal()) {
                        MethodDefinitionBuilder instBuilder = MethodDefinitionBuilder.systemMethod(this, naming.getInstantiatorMethodName(cls));
                        new DefaultedArgumentInstantiator(daoThis, model).makeOverload(instBuilder,
                                paramList.getModel(),
                                param.getParameterModel(),
                                typeParameterListModel(typeParameterList));
                        instantiatorImplCb.method(instBuilder);
                    } else {
                        addOverloadedConstructor  = true;
                    }
                } else {
                    addOverloadedConstructor = true;
                }
                if (addOverloadedConstructor) {
                    // Add overloaded constructors for defaulted parameter
                    MethodDefinitionBuilder overloadBuilder = classBuilder.addConstructor();
                    new DefaultedArgumentConstructor(daoThis, model).makeOverload(
                            overloadBuilder,
                            paramList.getModel(),
                            param.getParameterModel(),
                            typeParameterListModel(typeParameterList));
                }
            }
        }
        satisfaction(def.getSatisfiedTypes(), model, classBuilder);
        serialization(model, classBuilder);
        at(def);
       
        // Generate the inner members list for model loading
        addAtMembers(classBuilder, model, def);
        addAtLocalDeclarations(classBuilder, def);
       
        // Make sure top types satisfy reified type
        addReifiedTypeInterface(classBuilder, model);
    }

    /**
     * Add extra constructor and methods required for serialization
     */
    private void serialization(Class model, ClassDefinitionBuilder classBuilder) {
        if (!model.isSerializable()) {
            return;
        }
        at(null);
        classBuilder.serializable();
        serializationConstructor(model, classBuilder);
        serializationSerialize(model, classBuilder);
        serializationDeserialize(model, classBuilder);
    }
   
    private boolean hasField(Declaration member) {
        if (member instanceof Value) {
            Value value = (Value)member;
            if (!value.isTransient()
                    && !value.isFormal()
                    && (value.isShared() || value.isCaptured())) {
                return true;
            }
        }
        return false;
    }
   
    /**
     * <p>Generates the serialization constructor
     * with signature {@code ($Serialization$)} which:</p>
     * <ul>
     * <li>invokes {@code super()}, if the super class is also
     *     serializable,</li>
     * <li>initializes all companion instance fields to a
     *     newly instantiated companion instance,</li>
     * <li>initializes all reified type argument fields to null,</li>
     * <li>initializes all reference attribute fields to null,</li>
     * <li>initializesall primitive attribute fields to a default
     *     value (basically some kind of 0)</li>
     * </ul>
     */
    private void serializationConstructor(Class model, ClassDefinitionBuilder classBuilder) {
        MethodDefinitionBuilder ctor = classBuilder.addConstructor();
        ctor.ignoreModelAnnotations();
        ctor.modifiers(PUBLIC);
        ParameterDefinitionBuilder serializationPdb = ParameterDefinitionBuilder.systemParameter(this, "ignored");
        serializationPdb.modifiers(FINAL);
        serializationPdb.type(make().Type(syms().ceylonSerializationType), null);
        ctor.parameter(serializationPdb);
       
        for (TypeParameter tp : model.getTypeParameters()) {
            ctor.reifiedTypeParameter(tp);
        }
       
        final ListBuffer<JCStatement> stmts = ListBuffer.lb();

        if (extendsSerializable(model)) {
            // invoke super
            ListBuffer<JCExpression> superArgs = ListBuffer.<JCExpression>lb();
            superArgs.add(naming.makeUnquotedIdent("ignored"));
            for (JCExpression ta : makeReifiedTypeArguments(model.getExtendedType())) {
                superArgs.add(ta);
            }
            stmts.add(make().Exec(make().Apply(null,
                    naming.makeSuper(),
                    superArgs.toList())));
        }

        // initialize reified type arguments to according to parameters
        for (TypeParameter tp : model.getTypeParameters()) {
            stmts.add(makeReifiedTypeParameterAssignment(tp));
        }
       
        // initialize companion instances to a new companion instance
        if (!model.getSatisfiedTypeDeclarations().isEmpty()) {
            SatisfactionVisitor visitor = new SatisfactionVisitor() {
                @Override
                public void visit(Class model, Interface iface) {
                    if (hasImpl(iface)) {
                        stmts.add(makeCompanionInstanceAssignment(model, iface, model.getType().getSupertype(iface)));
                    }
                   
                }
            };
            HashSet<Interface> satisfiedInterfaces = new HashSet<Interface>();
            for (ProducedType satisfiedType : model.getSatisfiedTypes()) {
                TypeDeclaration decl = satisfiedType.getDeclaration();
                if (!(decl instanceof Interface)) {
                    continue;
                }
                // make sure we get the right instantiation of the interface
                satisfiedType = model.getType().getSupertype(decl);
                walkSatisfiedInterfaces(model, visitor, satisfiedType, satisfiedInterfaces);
            }
        }
       
        // initialize attribute fields to null or a zero
        for (Declaration member : model.getMembers()) {
            if (hasField(member)) {
                Value value = (Value)member;
                if (value.isLate()) {
                    continue;
                }
                // initialize all reference fields to null and all primitive
                // fields to a default value.
                JCExpression nullOrZero;
                if (CodegenUtil.isUnBoxed(value)) {
                    Object literal;
                    ProducedType type = value.getType();
                    if (isCeylonBoolean(type)) {
                        literal = false;
                    } else if (isCeylonByte(type)) {
                        literal = (byte)0;
                    } else if (isCeylonInteger(type)) {
                        literal = 0L;
                    } else if (isCeylonCharacter(type)) {
                        literal = 0;
                    } else if (isCeylonFloat(type)) {
                        literal = 0.0;
                    } else if (isCeylonString(type)) {
                        literal = "";
                    } else {
                        throw BugException.unhandledCase(type);
                    }
                    nullOrZero = make().Literal(literal);
                } else {
                    nullOrZero = makeNull();
                }
                stmts.add(make().Exec(make().Assign(
                        naming.makeQualifiedName(naming.makeThis(), value, Naming.NA_IDENT),
                        nullOrZero)));
            }
        }
        ctor.body(stmts.toList());
    }
   
    static interface SatisfactionVisitor {
        void visit(Class model, Interface iface);
    }
   
    private void walkSatisfiedInterfaces(final Class model,
            SatisfactionVisitor visitor,
            ProducedType satisfiedType, Set<Interface> satisfiedInterfaces) {
        satisfiedType = satisfiedType.resolveAliases();
        Interface iface = (Interface)satisfiedType.getDeclaration();
       
        if (satisfiedInterfaces.contains(iface)
                || iface.getType().isExactly(typeFact().getIdentifiableDeclaration().getType())) {
            return;
        }
       
        visitor.visit(model, iface);
       
        satisfiedInterfaces.add(iface);
        // recurse up the hierarchy
        for (ProducedType sat : iface.getSatisfiedTypes()) {
            sat = model.getType().getSupertype(sat.getDeclaration());
            walkSatisfiedInterfaces(model, visitor, sat, satisfiedInterfaces);
        }
    }

    private boolean extendsSerializable(Class model) {
        return !typeFact().getObjectDeclaration().getType().isExactly(model.getExtendedType())
                && !typeFact().getBasicDeclaration().getType().isExactly(model.getExtendedType());
    }
   
    /**
     * <p>Generates the {@code $serialize$()} method to serialize the classes state
     * which:</p>
     * <ul>
     * <li>invokes {@code super.$serialize$()}, if the super class is also
     *     serializable,</li>
     * <li>invokes {@code dtor.putTypeArgument()} for each type argument in the
     *     class,</li>
     * <li>invokes {@code dtor.putValue()} for each attribute in the class
     *     whose state is held in a field.</li>
     * </ul>
     */
    private void serializationSerialize(Class model,
            ClassDefinitionBuilder classBuilder) {
        MethodDefinitionBuilder mdb = MethodDefinitionBuilder.systemMethod(this, Unfix.$serialize$.toString());
        mdb.isOverride(true);
        mdb.ignoreModelAnnotations();
        mdb.modifiers(PUBLIC);
        ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.systemParameter(this, "dtorFactory");
        pdb.modifiers(FINAL);
        pdb.type(makeJavaType(
               
                typeFact().getCallableType(
                       
                    typeFact().getDeconstructorType())), null);
        mdb.parameter(pdb);
       
        ListBuffer<JCStatement> stmts = ListBuffer.lb();
        if (extendsSerializable(model)) {
            // invoke super.$serialize$()
            stmts.add(make().Exec(make().Apply(null,
                    naming.makeQualIdent(naming.makeSuper(), Unfix.$serialize$.toString()),
                    List.<JCExpression>of(naming.makeUnquotedIdent("dtorFactory")))));
        }
       
        stmts.add(makeVar(FINAL, Unfix.deconstructor.toString(),
                makeJavaType(typeFact().getDeconstructorType()),
                make().Apply(null,
                        naming.makeQualIdent(naming.makeUnquotedIdent("dtorFactory"),
                                naming.getCallableMethodName()),
                        List.<JCExpression>of(expressionGen().makeTypeLiteralCall(model.getType(), false, model.getType())))));
       
       
       
        // get reified type arguments
        for (TypeParameter tp : model.getTypeParameters()) {
            JCExpression reifiedType = makeMetamodelInvocation("getAppliedMetamodel",
                    List.<JCExpression>of(naming.makeUnquotedIdent(naming.getTypeArgumentDescriptorName(tp))),
                    null);
            stmts.add(make().Exec(make().Apply(
                    null,
                    naming.makeQualIdent(naming.makeUnquotedIdent(Unfix.deconstructor.toString()), "putTypeArgument"),
                    List.<JCExpression>of(
                            expressionGen().makeTypeParameterDeclaration(null, tp),
                            reifiedType))));
        }
       
        // Get the outer instance, if any
        if (model.getContainer() instanceof ClassOrInterface) {
            ClassOrInterface outerInstanceModel = (ClassOrInterface)model.getContainer();
            ProducedType outerInstanceType = outerInstanceModel.getType();
            stmts.add(make().Exec(make().Apply(
                    List.of(makeJavaType(outerInstanceType, JT_TYPE_ARGUMENT)),
                    naming.makeQualIdent(naming.makeUnquotedIdent(Unfix.deconstructor.toString()), "putOuterInstance"),
                    List.of(makeReifiedTypeArgument(outerInstanceType),
                            expressionGen().makeOuterExpr(outerInstanceType)))));
        }
       
        // get state from fields
        for (Declaration member : model.getMembers()) {
            if (hasField(member)) {
                Value value = (Value)member;
                final ProducedType serializedValueType;
                JCExpression serializedValue;
                //if (Decl.isValueTypeDecl(simplifyType(value.getType()))) {
               
                if (value.isLate()) {
                    stmts.add(make().If(
                            make().Unary(JCTree.NOT,
                                    naming.makeUnquotedIdent(naming.getInitializationFieldName(value.getName()))),
                            makeThrowAssertionException(make().Literal("instance cannot be serialized: " + member.getQualifiedNameString() + " has not been initialized")),
                            null));
                }
               
                serializedValueType = value.getType();
                if (value.isToplevel() || value.isLate()) {
                    serializedValue = make().Apply(null, naming.makeQualifiedName(naming.makeThis(), value, Naming.NA_MEMBER), List.<JCExpression>nil());
                } else {
                    serializedValue = naming.makeQualifiedName(naming.makeThis(), value, Naming.NA_IDENT);
                }
                serializedValue = expressionGen().applyErasureAndBoxing(serializedValue,
                        value.getType(),
                        !CodegenUtil.isUnBoxed(value),
                        BoxingStrategy.BOXED,
                        value.getType());
                stmts.add(make().Exec(make().Apply(
                        List.of(makeJavaType(serializedValueType, JT_TYPE_ARGUMENT)),
                        naming.makeQualIdent(naming.makeUnquotedIdent(Unfix.deconstructor.toString()), "putValue"),
                        List.of(makeReifiedTypeArgument(serializedValueType),
                                makeValueDeclaration(value),
                                serializedValue))));
            }
        }
       
        mdb.body(stmts.toList());
        classBuilder.method(mdb);
    }
    /**
     * <p>Generates the {@code $deserialize$()} method to deserialize
     * the classes state, which:</p>
     * <ul>
     * <li>invokes {@code super.$deserialize$()}, if the super class is also
     *     serializable,</li>
     * <li>assigns each reified type argument in the
     *     class by invoking {@code dted.getTypeArgument()},</li>
     * <li>assigns each field in the
     *     class by invoking {@code dted.getValue()}.</li>
     * </ul>
     */
    private void serializationDeserialize(Class model,
            ClassDefinitionBuilder classBuilder) {
        MethodDefinitionBuilder mdb = MethodDefinitionBuilder.systemMethod(this, Unfix.$deserialize$.toString());
        mdb.isOverride(true);
        mdb.ignoreModelAnnotations();
        mdb.modifiers(PUBLIC);
        ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.systemParameter(this, Unfix.deconstructed.toString());
        pdb.modifiers(FINAL);
        pdb.type(make().Type(syms().ceylonDeconstructedType), null);
        mdb.parameter(pdb);
       
        ListBuffer<JCStatement> stmts = ListBuffer.lb();
        boolean requiredLookup = false;
       
        // assign reified type arguments
        /*for (TypeParameter tp : model.getTypeParameters()) {
            requiredLookup = true;
            String descriptorName = naming.getTypeArgumentDescriptorName(tp);
            // Get the c.l.m.m.Type
            JCExpression reifiedType = make().Apply(null,
                    naming.makeQualIdent(naming.makeUnquotedIdent(Unfix.deconstructed.toString()), "getTypeArgument"),
                    List.of(expressionGen().makeTypeParameterDeclaration(null, tp)));
            // Now convert it to a TypeDescriptor, which is what we actually need
            JCExpression reifiedTypeDescriptor = makeMetamodelInvocation("getTypeDescriptor", List.of(reifiedType), null);
            stmts.add(makeReassignFinalField(makeTypeDescriptorType(), descriptorName, reifiedTypeDescriptor));
        }*/
       
        // assign fields
        for (Declaration member : model.getMembers()) {
            if (hasField(member)) {
                requiredLookup |= makeDeserializationAssignment(stmts, (Value)member);
            }
        }
        if (requiredLookup) {
            // if we needed to use a lookup object to reset final fields,
            // prepend that variable
            stmts.prepend(makeVar(FINAL,
                    "lookup",
                    naming.makeQualIdent(make().Type(syms().methodHandlesType), "Lookup"),
                    make().Apply(null, naming.makeQuotedFQIdent("java.lang.invoke.MethodHandles.lookup"), List.<JCExpression>nil())));
        }
       
        if (extendsSerializable(model)) {
            // prepend the invocation of super.$serialize$()
            stmts.prepend(make().Exec(make().Apply(null,
                    naming.makeQualIdent(naming.makeSuper(), Unfix.$deserialize$.toString()),
                    List.<JCExpression>of(naming.makeUnquotedIdent(Unfix.deconstructed.toString())))));
        }
        mdb.body(stmts.toList());
        classBuilder.method(mdb);
    }
   
    private boolean makeDeserializationAssignment(ListBuffer<JCStatement> stmts, Value value) {
        ProducedType typeOrReferenceType;
        boolean isValueType = Decl.isValueTypeDecl(simplifyType(value.getType()));
        //if (isValueType) {
        typeOrReferenceType = value.getType();
        //} else {
          //  typeOrReferenceType = typeFact().getReferenceType(value.getType());
        //}
           
        Naming.SyntheticName n = naming.alias("valueOrRef");
        JCExpression newValue = make().Apply(List.of(makeJavaType(typeOrReferenceType, JT_TYPE_ARGUMENT)),
                naming.makeQualIdent(naming.makeUnquotedIdent(Unfix.deconstructed.toString()), "getValue"),
                List.<JCExpression>of(
                        makeReifiedTypeArgument(typeOrReferenceType),
                        makeValueDeclaration(value)));
        // let (
        // Object valueOrRef = ^^;
        // valueOrRef instanceof Reference ? (($InstanceLeaker$)valueOrRef).$leakInstance$() : (Type)valueOrRef;
        // )
        newValue = make().LetExpr(makeVar(0, n, make().Type(syms().objectType), newValue),
                make().Conditional(
                        make().TypeTest(n.makeIdent(),
                                makeJavaType(typeFact().getReferenceType(value.getType()), JT_RAW)),
                        make().Apply(null,
                                naming.makeQualIdent(make().TypeCast(
                                        make().TypeApply(make().QualIdent(syms().ceylonInstanceLeakerType.tsym), List.of(makeJavaType(value.getType(), JT_TYPE_ARGUMENT))), n.makeIdent()), "$leakInstance$"),
                                List.<JCExpression>nil()),
                        make().TypeCast(makeJavaType(typeOrReferenceType, JT_NO_PRIMITIVES), n.makeIdent())));

        if (isValueType) {
            newValue = expressionGen().applyErasureAndBoxing(newValue,
                    value.getType(), true, CodegenUtil.getBoxingStrategy(value),
                    value.getType());
        } else {
            // We need to obtain the instance from the reference
            // but we don't need the instance to be fully deserialized
        }
        boolean requiredLookup = false;
        final JCStatement assignment;
        if (value.isToplevel() || value.isLate()) {// XXX duplicates logic in AttributeDefinitionBuilder
            // We use the setter for late values, since that will allocate
            // the array if needed.
            assignment = make().Exec(make().Apply(null,
                    naming.makeQualifiedName(naming.makeThis(), value, Naming.NA_MEMBER | Naming.NA_SETTER),
                    List.of(newValue)));
        } else {
            // We bypass the setter
            if (value.isVariable()) {
                assignment = make().Exec(make().Assign(
                        naming.makeQualifiedName(naming.makeThis(), value, Naming.NA_IDENT),
                        newValue));
            } else {
                // The field will have final modifier, so we need some
                // jiggery pokery to reset it.
                requiredLookup = true;
                String fieldName = value.getName();
                JCExpression fieldType = makeJavaType(value.getType());//TODO probably wrong
                assignment = makeReassignFinalField(fieldType, fieldName, newValue);
            }
        }
        stmts.add(assignment);
        return requiredLookup;
    }

    private JCStatement makeReassignFinalField(JCExpression fieldType,
            String fieldName, JCExpression newValue) {
        final JCStatement assignment;
        JCExpression mhExpr = utilInvocation().setter(
                naming.makeUnquotedIdent("lookup"),
                //naming.makeQualIdent(makeJavaType(((Class)value.getContainer()).getType(), JT_NO_PRIMITIVES), "class"),
                make().Literal(fieldName)// TODO field name should encapsulated
                );
       
        JCExpression expr = make().Apply(null,
                naming.makeQualIdent(mhExpr, "invokeExact"),
                List.of(naming.makeThis(),
                        make().TypeCast(fieldType, newValue)));// We always typecast here, due to method handle
        assignment = make().Exec(expr);
        return assignment;
    }
   
    private JCExpression makeValueDeclaration(Value value) {
        return expressionGen().makeMemberValueOrFunctionDeclarationLiteral(null, value, false);
    }
   
    /**
     * Generate a method for a shared FunctionalParameter which delegates to the Callable
     * @param klass
     * @param annotations */
    private void makeMethodForFunctionalParameter(
            ClassDefinitionBuilder classBuilder, AnyClass klass, Tree.Parameter paramTree, List<JCAnnotation> annotations) {
        Parameter paramModel = paramTree.getParameterModel();

        if (Strategy.createMethod(paramModel)) {
            Tree.MethodDeclaration methodDecl = (Tree.MethodDeclaration)Decl.getMemberDeclaration(klass, paramTree);
            makeFieldForParameter(classBuilder, paramModel);
            Method method = (Method)paramModel.getModel();

            java.util.List<Parameter> parameters = method.getParameterLists().get(0).getParameters();
            CallBuilder callBuilder = CallBuilder.instance(this).invoke(
                    naming.makeQualIdent(naming.makeName(method, Naming.NA_IDENT),
                            Naming.getCallableMethodName(method)));
            for (Parameter parameter : parameters) {
                JCExpression parameterExpr = naming.makeName(parameter.getModel(), Naming.NA_IDENT);
                parameterExpr = expressionGen().applyErasureAndBoxing(parameterExpr, parameter.getType(),
                        !CodegenUtil.isUnBoxed(parameter.getModel()), BoxingStrategy.BOXED,
                        parameter.getType());
                callBuilder.argument(parameterExpr);
            }
            JCExpression expr = callBuilder.build();
            JCStatement body;
            if (isVoid(methodDecl) && Decl.isUnboxedVoid(method) && !Strategy.useBoxedVoid(method)) {
                body = make().Exec(expr);
            } else {
                expr = expressionGen().applyErasureAndBoxing(expr, paramModel.getType(), true, CodegenUtil.getBoxingStrategy(method), paramModel.getType());
                body = make().Return(expr);
            }
            classBuilder.methods(transformMethod(method, null, methodDecl.getParameterLists(),
                    methodDecl.getAnnotationList(),
                    true, method.isActual(), true,
                    List.of(body), daoThis, false));
        }
    }
   
    private void addReifiedTypeInterface(ClassDefinitionBuilder classBuilder, ClassOrInterface model) {
        if(model.getExtendedType() == null || willEraseToObject(model.getExtendedType()) || !Decl.isCeylon(model.getExtendedTypeDeclaration()))
            classBuilder.reifiedType();
    }

    /**
     * Transforms the type of the given class parameter
     * @param decl
     * @return
     */
    JCExpression transformClassParameterType(Parameter parameter) {
        MethodOrValue decl = parameter.getModel();
        if (!(decl.getContainer() instanceof Class)) {
            throw new BugException("expected parameter of Class");
        }
        JCExpression type;
        MethodOrValue attr = decl;
        if (!Decl.isTransient(attr)) {
            ProducedTypedReference typedRef = getTypedReference(attr);
            ProducedTypedReference nonWideningTypedRef = nonWideningTypeDecl(typedRef);
            ProducedType paramType = nonWideningType(typedRef, nonWideningTypedRef);
            type = makeJavaType(nonWideningTypedRef.getDeclaration(), paramType, 0);
        } else {
            ProducedType paramType = decl.getType();
            type = makeJavaType(decl, paramType, 0);
        }
        return type;
    }

    private void transformInterface(com.redhat.ceylon.compiler.typechecker.tree.Tree.ClassOrInterface def, Interface model, ClassDefinitionBuilder classBuilder, TypeParameterList typeParameterList) {
        //  Copy all the qualifying type's type parameters into the interface
        java.util.List<TypeParameter> typeParameters = typeParametersOfAllContainers(model, false);
        for(TypeParameter tp : typeParameters){
            classBuilder.typeParameter(tp, false);
        }
       
        if(model.isCompanionClassNeeded()){
            classBuilder.method(makeCompanionAccessor(model, model.getType(), null, false));
            // Build the companion class
            buildCompanion(def, (Interface)model, classBuilder, typeParameterList);
        }
       
        // Generate the inner members list for model loading
        addAtMembers(classBuilder, model, def);
        addAtLocalDeclarations(classBuilder, def);
    }

    private void addAtMembers(ClassDefinitionBuilder classBuilder, ClassOrInterface model, com.redhat.ceylon.compiler.typechecker.tree.Tree.ClassOrInterface def) {
        List<JCExpression> members = List.nil();
        for(Declaration member : model.getMembers()){
            if(member instanceof ClassOrInterface == false
                    && member instanceof TypeAlias == false){
                continue;
            }
            TypeDeclaration innerType = (TypeDeclaration) member;
            Tree.Declaration innerTypeTree = findInnerType(def, innerType.getName());
            if(innerTypeTree != null) {
                TransformationPlan plan = errors().hasDeclarationAndMarkBrokenness(innerTypeTree);
                if (plan instanceof Drop) {
                    continue;
                }
            }
            JCAnnotation atMember = makeAtMember(innerType.getType());
            members = members.prepend(atMember);
        }
        classBuilder.annotations(makeAtMembers(members));
    }

    private Tree.Declaration findInnerType(Tree.ClassOrInterface def, String name) {
        Tree.Body body;
        if(def instanceof Tree.ClassDefinition)
            body = ((Tree.ClassDefinition) def).getClassBody();
        else if(def instanceof Tree.InterfaceDefinition)
            body = ((Tree.InterfaceDefinition) def).getInterfaceBody();
        else
            return null;
        for(Node node : body.getStatements()){
            if(node instanceof Tree.Declaration
                    && ((Tree.Declaration) node).getIdentifier() != null
                    && ((Tree.Declaration) node).getIdentifier().getText().equals(name))
                return (Tree.Declaration) node;
        }
        return null;
    }

    private void addAtLocalDeclarations(ClassDefinitionBuilder classBuilder, Tree.ClassOrInterface tree) {
        classBuilder.annotations(makeAtLocalDeclarations(tree));
    }

    private void addAtContainer(ClassDefinitionBuilder classBuilder, TypeDeclaration model) {
        Scope scope = model.getContainer();
        if(scope == null || scope instanceof Package)
            return;
        if(scope instanceof ClassOrInterface){
            ClassOrInterface container = (ClassOrInterface) scope;
            List<JCAnnotation> atContainer = makeAtContainer(container.getType());
            classBuilder.annotations(atContainer);
        }else{
            if(model instanceof Interface)
                classBuilder.annotations(makeLocalContainerPath((Interface) model));
            Declaration declarationContainer = getDeclarationContainer(model);
            classBuilder.annotations(makeAtLocalDeclaration(model.getQualifier(), declarationContainer == null));
        }
       
    }

    private void satisfaction(Tree.SatisfiedTypes satisfied, final Class model, ClassDefinitionBuilder classBuilder) {
        final java.util.List<ProducedType> satisfiedTypes = model.getSatisfiedTypes();
        Set<Interface> satisfiedInterfaces = new HashSet<Interface>();
        // start by saying that we already satisfied each interface from superclasses
        Class superClass = model.getExtendedTypeDeclaration();
        while(superClass != null){
            for(TypeDeclaration interfaceDecl : superClass.getSatisfiedTypeDeclarations()){
                collectInterfaces((Interface) interfaceDecl, satisfiedInterfaces);
            }
            superClass = superClass.getExtendedTypeDeclaration();
        }
        // now satisfy each new interface
        if (satisfied != null) {
            for (Tree.StaticType type : satisfied.getTypes()) {
                try {
                    ProducedType satisfiedType = type.getTypeModel();
                    TypeDeclaration decl = satisfiedType.getDeclaration();
                    if (!(decl instanceof Interface)) {
                        continue;
                    }
                    // make sure we get the right instantiation of the interface
                    satisfiedType = model.getType().getSupertype(decl);
                    concreteMembersFromSuperinterfaces(model, classBuilder, satisfiedType, satisfiedInterfaces);
                } catch (BugException e) {
                    e.addError(type);
                }
            }
        }
        // now find the set of interfaces we implemented twice with more refined type parameters
        if(model.getExtendedTypeDeclaration() != null){
            // reuse that Set
            satisfiedInterfaces.clear();
            for(TypeDeclaration interfaceDecl : model.getSatisfiedTypeDeclarations()){
                collectInterfaces((Interface) interfaceDecl, satisfiedInterfaces);
            }
            // now see if we refined them
            for(Interface iface : satisfiedInterfaces){
                // skip those we can't do anything about
                if(!supportsReified(iface) || !CodegenUtil.isCompanionClassNeeded(iface))
                    continue;
                ProducedType thisType = model.getType().getSupertype(iface);
                ProducedType superClassType = model.getExtendedType().getSupertype(iface);
                if(thisType != null
                        && superClassType != null
                        && !thisType.isExactly(superClassType)
                        && thisType.isSubtypeOf(superClassType)){
                    // we're refining it
                    classBuilder.refineReifiedType(thisType);
                }
            }
        }
    }

    private void collectInterfaces(Interface interfaceDecl, Set<Interface> satisfiedInterfaces) {
        if(satisfiedInterfaces.add(interfaceDecl)){
            for(TypeDeclaration newInterfaceDecl : interfaceDecl.getSatisfiedTypeDeclarations()){
                if (!(newInterfaceDecl instanceof UnknownType)) {
                    collectInterfaces((Interface) newInterfaceDecl, satisfiedInterfaces);
                }
            }
        }
    }

    /**
     * Generates companion fields ($Foo$impl) and methods
     */
    private void concreteMembersFromSuperinterfaces(final Class model,
            ClassDefinitionBuilder classBuilder,
            ProducedType satisfiedType, Set<Interface> satisfiedInterfaces) {
        satisfiedType = satisfiedType.resolveAliases();
        Interface iface = (Interface)satisfiedType.getDeclaration();
        if (satisfiedInterfaces.contains(iface)
                || iface.getType().isExactly(typeFact().getIdentifiableDeclaration().getType())) {
            return;
        }
    
        // If there is no $impl (e.g. implementing a Java interface)
        // then don't instantiate it...
        if (hasImpl(iface)) {
            // ... otherwise for each satisfied interface,
            // instantiate an instance of the
            // companion class in the constructor and assign it to a
            // $Interface$impl field
            transformInstantiateCompanions(classBuilder,
                    model, iface, satisfiedType);
        }
       
        if(!Decl.isCeylon(iface)){
            // let's not try to implement CMI for Java interfaces
            return;
        }
       
        // For each super interface
        for (Declaration member : iface.getMembers()) {
           
            if (member instanceof Class
                    && Strategy.generateInstantiator(member)
                    && !model.isAbstract() && !model.isFormal()
                    && model.getDirectMember(member.getName(), null, false) == null) {
                // instantiator method implementation
                Class klass = (Class)member;
                generateInstantiatorDelegate(classBuilder, satisfiedType,
                        iface, klass, model.getType());
            }
           
            // type aliases are on the $impl class
            if(member instanceof TypeAlias)
                continue;
           
            if (Strategy.onlyOnCompanion(member)) {
                // non-shared interface methods don't need implementing
                // (they're just private methods on the $impl)
                continue;
            }
            if (member instanceof Method) {
                Method method = (Method)member;
                final ProducedTypedReference typedMember = satisfiedType.getTypedMember(method, Collections.<ProducedType>emptyList());
                Declaration sub = (Declaration)model.getMember(method.getName(), null, false);
                if (sub instanceof Method) {
                    Method subMethod = (Method)sub;
                    if (subMethod.getParameterLists().isEmpty()) {
                        continue;
                    }
                    java.util.List<java.util.List<ProducedType>> producedTypeParameterBounds = producedTypeParameterBounds(
                            typedMember, subMethod);
                    final ProducedTypedReference refinedTypedMember = model.getType().getTypedMember(subMethod, Collections.<ProducedType>emptyList());
                    final java.util.List<TypeParameter> typeParameters = subMethod.getTypeParameters();
                    final java.util.List<Parameter> parameters = subMethod.getParameterLists().get(0).getParameters();
                    boolean hasOverloads = false;
                    if (!satisfiedInterfaces.contains((Interface)method.getContainer())) {

                        for (Parameter param : parameters) {
                            if (Strategy.hasDefaultParameterValueMethod(param)
                                    && CodegenUtil.getTopmostRefinedDeclaration(param.getModel()).getContainer().equals(member)) {
                                final ProducedTypedReference typedParameter = refinedTypedMember.getTypedParameter(param);
                                // If that method has a defaulted parameter,
                                // we need to generate a default value method
                                // which also delegates to the $impl
                                final MethodDefinitionBuilder defaultValueDelegate = makeDelegateToCompanion(iface,
                                        typedParameter,
                                        model.getType(),
                                        PUBLIC | FINAL,
                                        typeParameters, producedTypeParameterBounds,
                                        typedParameter.getType(),
                                        Naming.getDefaultedParamMethodName(method, param),
                                        parameters.subList(0, parameters.indexOf(param)),
                                        param.getModel().getTypeErased(),
                                        null);
                                classBuilder.method(defaultValueDelegate);
                            }

                            if (Strategy.hasDefaultParameterOverload(param)) {
                                if ((method.isDefault() || method.isShared() && !method.isFormal())
                                        && Decl.equal(method, subMethod)) {
                                    MethodDefinitionBuilder overloadBuilder = MethodDefinitionBuilder.method(this, subMethod);
                                    MethodDefinitionBuilder overload = new DefaultedArgumentMethodTyped(daoThis, typedMember)
                                        .makeOverload(
                                            overloadBuilder,
                                            subMethod.getParameterLists().get(0),
                                            param,
                                            typeParameters);
                                    classBuilder.method(overload);
                                }

                                hasOverloads = true;
                            }
                        }
                    }
                    // if it has the *most refined* default concrete member,
                    // then generate a method on the class
                    // delegating to the $impl instance
                    if (needsCompanionDelegate(model, member)) {

                        final MethodDefinitionBuilder concreteMemberDelegate = makeDelegateToCompanion(iface,
                                typedMember,
                                model.getType(),
                                PUBLIC | (method.isDefault() ? 0 : FINAL),
                                typeParameters,
                                producedTypeParameterBounds,
                                typedMember.getType(),
                                naming.selector(method),
                                method.getParameterLists().get(0).getParameters(),
                                ((Method) member).getTypeErased(),
                                null);
                        classBuilder.method(concreteMemberDelegate);
                    }

                    if (hasOverloads
                            && (method.isDefault() || method.isShared() && !method.isFormal())
                            && Decl.equal(method, subMethod)) {
                        final MethodDefinitionBuilder canonicalMethod = makeDelegateToCompanion(iface,
                                typedMember,
                                model.getType(),
                                PRIVATE,
                                subMethod.getTypeParameters(),
                                producedTypeParameterBounds,
                                typedMember.getType(),
                                Naming.selector(method, Naming.NA_CANONICAL_METHOD),
                                method.getParameterLists().get(0).getParameters(),
                                ((Method) member).getTypeErased(),
                                naming.selector(method));
                        classBuilder.method(canonicalMethod);
                    }
                }
            } else if (member instanceof Value
                    || member instanceof Setter) {
                TypedDeclaration attr = (TypedDeclaration)member;
                final ProducedTypedReference typedMember = satisfiedType.getTypedMember(attr, null);
                if (needsCompanionDelegate(model, member)) {
                    if (member instanceof Value) {
                        final MethodDefinitionBuilder getterDelegate = makeDelegateToCompanion(iface,
                                typedMember,
                                model.getType(),
                                PUBLIC | (attr.isDefault() ? 0 : FINAL),
                                Collections.<TypeParameter>emptyList(),
                                Collections.<java.util.List<ProducedType>>emptyList(),
                                typedMember.getType(),
                                Naming.getGetterName(attr),
                                Collections.<Parameter>emptyList(),
                                attr.getTypeErased(),
                                null);
                        classBuilder.method(getterDelegate);
                    }
                    if (member instanceof Setter) {
                        final MethodDefinitionBuilder setterDelegate = makeDelegateToCompanion(iface,
                                typedMember,
                                model.getType(),
                                PUBLIC | (((Setter)member).getGetter().isDefault() ? 0 : FINAL),
                                Collections.<TypeParameter>emptyList(),
                                Collections.<java.util.List<ProducedType>>emptyList(),
                                typeFact().getAnythingDeclaration().getType(),
                                Naming.getSetterName(attr),
                                Collections.<Parameter>singletonList(((Setter)member).getParameter()),
                                ((Setter) member).getTypeErased(),
                                null);
                        classBuilder.method(setterDelegate);
                    }
                    if (Decl.isValue(member)
                            && ((Value)attr).isVariable()) {
                        // I don't *think* this can happen because although a
                        // variable Value can be declared on an interface it
                        // will need to we refined as a Getter+Setter on a
                        // subinterface in order for there to be a method in a
                        // $impl to delegate to
                        throw new BugException("assertion failed: " + member.getQualifiedNameString() + " was unexpectedly a variable value");
                    }
                }
            } else if (needsCompanionDelegate(model, member)) {
                throw new BugException("unhandled concrete interface member " + member.getQualifiedNameString() + " " + member.getClass());
            }
        }
       
        // Add $impl instances for the whole interface hierarchy
        satisfiedInterfaces.add(iface);
        for (ProducedType sat : iface.getSatisfiedTypes()) {
            sat = model.getType().getSupertype(sat.getDeclaration());
            concreteMembersFromSuperinterfaces(model, classBuilder, sat, satisfiedInterfaces);
        }
       
    }

    private java.util.List<java.util.List<ProducedType>> producedTypeParameterBounds(
            final ProducedReference typedMember, Generic subMethod) {
        java.util.List<java.util.List<ProducedType>> producedTypeParameterBounds = new ArrayList<java.util.List<ProducedType>>(subMethod.getTypeParameters().size());
        for (TypeParameter tp : subMethod.getTypeParameters()) {
            java.util.List<ProducedType> satisfiedTypes = tp.getType().getSatisfiedTypes();
            ArrayList<ProducedType> bounds = new ArrayList<>(satisfiedTypes.size());
            for (ProducedType bound : satisfiedTypes) {
                bounds.add(bound.getType().substitute(typedMember.getTypeArguments()));
            }
            producedTypeParameterBounds.add(bounds);
        }
        return producedTypeParameterBounds;
    }

    private void generateInstantiatorDelegate(
            ClassDefinitionBuilder classBuilder, ProducedType satisfiedType,
            Interface iface, Class klass, ProducedType currentType) {
        ProducedType typeMember = satisfiedType.getTypeMember(klass, Collections.<ProducedType>emptyList());
        java.util.List<TypeParameter> typeParameters = klass.getTypeParameters();
        java.util.List<Parameter> parameters = klass.getParameterLists().get(0).getParameters();
       
        String instantiatorMethodName = naming.getInstantiatorMethodName(klass);
        for (Parameter param : parameters) {
            if (Strategy.hasDefaultParameterValueMethod(param)) {
                final ProducedTypedReference typedParameter = typeMember.getTypedParameter(param);
                // If that method has a defaulted parameter,
                // we need to generate a default value method
                // which also delegates to the $impl
                final MethodDefinitionBuilder defaultValueDelegate = makeDelegateToCompanion(iface,
                        typedParameter,
                        currentType,
                        PUBLIC | FINAL,
                        typeParameters,
                        producedTypeParameterBounds(typeMember, klass),
                        typedParameter.getType(),
                        Naming.getDefaultedParamMethodName(klass, param),
                        parameters.subList(0, parameters.indexOf(param)),
                        param.getModel().getTypeErased(),
                        null);
                classBuilder.method(defaultValueDelegate);
            }
            if (Strategy.hasDefaultParameterOverload(param)) {
                final MethodDefinitionBuilder overload = makeDelegateToCompanion(iface,
                        typeMember,
                        currentType,
                        PUBLIC | FINAL,
                        typeParameters,
                        producedTypeParameterBounds(typeMember, klass),
                        typeMember.getType(),
                        instantiatorMethodName,
                        parameters.subList(0, parameters.indexOf(param)),
                        false,
                        null);
                classBuilder.method(overload);
            }
        }
        final MethodDefinitionBuilder overload = makeDelegateToCompanion(iface,
                typeMember,
                currentType,
                PUBLIC | FINAL,
                typeParameters,
                producedTypeParameterBounds(typeMember, klass),
                typeMember.getType(),
                instantiatorMethodName,
                parameters,
                false,
                null);
        classBuilder.method(overload);
    }

    private boolean needsCompanionDelegate(final Class model, Declaration member) {
        final boolean mostRefined;
        Declaration m = model.getMember(member.getName(), null, false);
        if (member instanceof Setter && Decl.isGetter(m)) {
            mostRefined = member.equals(((Value)m).getSetter());
        } else {
            mostRefined = member.equals(m);
        }
        return mostRefined
                && (member.isDefault() || !member.isFormal());
    }

    /**
     * Generates a method which delegates to the companion instance $Foo$impl
     */
    private MethodDefinitionBuilder makeDelegateToCompanion(Interface iface,
            ProducedReference typedMember,
            ProducedType currentType,
            final long mods,
            final java.util.List<TypeParameter> typeParameters,
            final java.util.List<java.util.List<ProducedType>> producedTypeParameterBounds,
            final ProducedType methodType,
            final String methodName,
            final java.util.List<Parameter> parameters,
            boolean typeErased,
            final String targetMethodName) {
        final MethodDefinitionBuilder concreteWrapper = MethodDefinitionBuilder.systemMethod(gen(), methodName);
        concreteWrapper.modifiers(mods);
        concreteWrapper.ignoreModelAnnotations();
        if ((mods & PRIVATE) == 0) {
            concreteWrapper.isOverride(true);
        }
        if(typeParameters != null) {
            concreteWrapper.reifiedTypeParametersFromModel(typeParameters);
        }
        Iterator<java.util.List<ProducedType>> iterator = producedTypeParameterBounds.iterator();
        if(typeParameters != null) {
            for (TypeParameter tp : typeParameters) {
                concreteWrapper.typeParameter(tp, iterator.next());
            }
        }
        boolean explicitReturn = false;
        Declaration member = typedMember.getDeclaration();
        ProducedType returnType = null;
        if (!isAnything(methodType)
                || ((member instanceof Method || member instanceof Value) && !Decl.isUnboxedVoid(member))
                || (member instanceof Method && Strategy.useBoxedVoid((Method)member))) {
            explicitReturn = true;
            if(CodegenUtil.isHashAttribute(member)){
                // delegates for hash attributes are int
                concreteWrapper.resultType(null, make().Type(syms().intType));
                returnType = typedMember.getType();
            }else if (typedMember instanceof ProducedTypedReference) {
                ProducedTypedReference typedRef = (ProducedTypedReference) typedMember;
                // This is very much like for method refinement: if the supertype is erased -> go raw.
                // Except for some reason we only need to do it with multiple inheritance with different type
                // arguments, so let's not go overboard
                int flags = 0;
                if(CodegenUtil.hasTypeErased((TypedDeclaration)member.getRefinedDeclaration()) ||
                        CodegenUtil.hasTypeErased((TypedDeclaration)member)
                        && isInheritedTwiceWithDifferentTypeArguments(currentType, iface)){
                    flags |= AbstractTransformer.JT_RAW;
                }
                concreteWrapper.resultTypeNonWidening(currentType, typedRef, typedMember.getType(), flags);
                // FIXME: this is redundant with what we computed in the previous line in concreteWrapper.resultTypeNonWidening
                ProducedTypedReference nonWideningTypedRef = gen().nonWideningTypeDecl(typedRef, currentType);
                returnType = gen().nonWideningType(typedRef, nonWideningTypedRef);
            } else {
                concreteWrapper.resultType(null, makeJavaType((ProducedType)typedMember));
                returnType = (ProducedType) typedMember;
            }
        }
       
        ListBuffer<JCExpression> arguments = ListBuffer.<JCExpression>lb();
        if(typeParameters != null){
            for(TypeParameter tp : typeParameters){
                arguments.add(naming.makeUnquotedIdent(naming.getTypeArgumentDescriptorName(tp)));
            }
        }
        for (Parameter param : parameters) {
            final ProducedTypedReference typedParameter = typedMember.getTypedParameter(param);
            ProducedType type;
            // if the supertype method itself got erased to Object, we can't do better than this
            if(gen().willEraseToObject(param.getType()) && !gen().willEraseToBestBounds(param))
                type = typeFact().getObjectDeclaration().getType();
            else
                type = typedParameter.getType();
            concreteWrapper.parameter(param, type, FINAL, 0, true);
            arguments.add(naming.makeName(param.getModel(), Naming.NA_MEMBER));
        }
        JCExpression qualifierThis = makeUnquotedIdent(getCompanionFieldName(iface));
        // if the best satisfied type is not the one we think we implement, we may need to cast
        // our impl accessor to get the expected bounds of the qualifying type
        if(explicitReturn){
            ProducedType javaType = getBestSatisfiedType(currentType, iface);
            ProducedType ceylonType = typedMember.getQualifyingType();
            // don't even bother if the impl accessor is turned to raw because casting it to raw doesn't help
            if(!isTurnedToRaw(ceylonType)
                    // if it's exactly the same we don't need any cast
                    && !javaType.isExactly(ceylonType))
                // this will add the proper cast to the impl accessor
                qualifierThis = expressionGen().applyErasureAndBoxing(qualifierThis, currentType,
                        false, true, BoxingStrategy.BOXED, ceylonType,
                        ExpressionTransformer.EXPR_WANTS_COMPANION);
        }
        JCExpression expr = make().Apply(
                null,  // TODO Type args
                makeSelect(qualifierThis, (targetMethodName != null) ? targetMethodName : methodName),
                arguments.toList());
        if (isUnimplementedMemberClass(currentType, typedMember)) {
            concreteWrapper.body(makeThrowUnresolvedCompilationError(
                    // TODO encapsulate the error message
                    "formal member '"+typedMember.getDeclaration().getName()+"' of '"+iface.getName()+"' not implemented in class hierarchy"));
            current().broken();
        } else if (!explicitReturn) {
            concreteWrapper.body(gen().make().Exec(expr));
        } else {
            // deal with erasure and stuff
            BoxingStrategy boxingStrategy;
            boolean exprBoxed;
            if(member instanceof TypedDeclaration){
                TypedDeclaration typedDecl = (TypedDeclaration) member;
                exprBoxed = !CodegenUtil.isUnBoxed(typedDecl);
                boxingStrategy = CodegenUtil.getBoxingStrategy(typedDecl);
            }else{
                // must be a class or interface
                exprBoxed = true;
                boxingStrategy = BoxingStrategy.UNBOXED;
            }
            // if our interface impl is turned to raw, the whole call will be seen as raw by javac, so we may need
            // to force an additional cast
            if(isTurnedToRaw(typedMember.getQualifyingType())
                    // see note in BoxingVisitor.visit(QualifiedMemberExpression) about mixin super calls and variant type args
                    // in invariant locations
                    || needsRawCastForMixinSuperCall(iface, methodType))
                typeErased = true;
            expr = gen().expressionGen().applyErasureAndBoxing(expr, methodType, typeErased,
                                                               exprBoxed, boxingStrategy,
                                                               returnType, 0);
            concreteWrapper.body(gen().make().Return(expr));
        }
        return concreteWrapper;
    }

    private boolean isUnimplementedMemberClass(ProducedType currentType, ProducedReference typedMember) {
        if (typedMember instanceof ProducedType
                && currentType.getDeclaration() instanceof Class) {// member class
            for (ProducedReference formal : ((Class)currentType.getDeclaration()).getUnimplementedFormals()) {
                if (formal.getDeclaration().equals(typedMember.getDeclaration())) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isInheritedTwiceWithDifferentTypeArguments(ProducedType currentType, Interface iface) {
        ProducedType firstSatisfiedType = getFirstSatisfiedType(currentType, iface);
        ProducedType supertype = currentType.getSupertype(iface);
        return !supertype.isExactly(firstSatisfiedType);
    }

    private Boolean hasImpl(Interface iface) {
        if (gen().willEraseToObject(iface.getType())) {
            return false;
        }
        // Java interfaces never have companion classes
        if (iface instanceof LazyInterface
            && !((LazyInterface)iface).isCeylon()){
            return false;
        }
        return CodegenUtil.isCompanionClassNeeded(iface);
    }

    private void transformInstantiateCompanions(
            ClassDefinitionBuilder classBuilder,
            Class model,
            Interface iface,
            ProducedType satisfiedType) {
        at(null);
       
        // make sure we get the first type that java will find when it looks up
        final ProducedType bestSatisfiedType = getBestSatisfiedType(model.getType(), iface);
       
        classBuilder.init(makeCompanionInstanceAssignment(model, iface, satisfiedType));
       
        classBuilder.field(PROTECTED | FINAL, getCompanionFieldName(iface),
                makeJavaType(bestSatisfiedType, AbstractTransformer.JT_COMPANION | JT_SATISFIES), null, false,
                makeAtIgnore());

        classBuilder.method(makeCompanionAccessor(iface, bestSatisfiedType, model, true));
    }

    /**
     * Returns the companion instances assignment expression used in the constructor,
     * e.g.
     * <pre>
     * this.$ceylon$language$Enumerable$this$ = new .ceylon.language.Enumerable$impl<.com.redhat.ceylon.compiler.java.test.structure.klass.SerializableEnumerable>(.com.redhat.ceylon.compiler.java.test.structure.klass.SerializableEnumerable.$TypeDescriptor$, this);
     * </pre>
     */
    private JCExpressionStatement makeCompanionInstanceAssignment(Class model,
            Interface iface, ProducedType satisfiedType) {
       
        final ProducedType bestSatisfiedType = getBestSatisfiedType(model.getType(), iface);
       
        JCExpression containerInstance = null;
        if(!Decl.isToplevel(iface) && !Decl.isLocal(iface)){
            // if it's a member type we need to qualify the new instance with its $impl container
            ClassOrInterface interfaceContainer = Decl.getClassOrInterfaceContainer(iface, false);
            if(interfaceContainer instanceof Interface){
                ClassOrInterface modelContainer = model;
                while((modelContainer = Decl.getClassOrInterfaceContainer(modelContainer, false)) != null
                        && modelContainer.getType().getSupertype(interfaceContainer) == null){
                    // keep searching
                }
                if (modelContainer == null) {
                    throw new BugException("Could not find container that satisfies interface "
                        + iface.getQualifiedNameString() + " to find qualifying instance for companion instance for "
                        + model.getQualifiedNameString());
                }
                // if it's an interface we just qualify it properly
                if(modelContainer instanceof Interface){
                    JCExpression containerType = makeJavaType(modelContainer.getType(), JT_COMPANION | JT_SATISFIES | JT_RAW);
                    containerInstance = makeSelect(containerType, "this");
                }else{
                    // it's a class: find the right field used for the interface container impl
                    String containerFieldName = getCompanionFieldName((Interface)interfaceContainer);
                    JCExpression containerType = makeJavaType(modelContainer.getType(), JT_SATISFIES);
                    containerInstance = makeSelect(makeSelect(containerType, "this"), containerFieldName);
                }
            }
        }
       
        List<JCExpression> state = List.nil();
       
        // pass all reified type info to the constructor
        for(JCExpression t : makeReifiedTypeArguments(satisfiedType)){
            state = state.append(t);
        }
        // pass the instance of this
        state = state.append( expressionGen().applyErasureAndBoxing(naming.makeThis(),
                model.getType(), false, true, BoxingStrategy.BOXED,
                bestSatisfiedType, ExpressionTransformer.EXPR_FOR_COMPANION));
       
        final JCExpression ifaceImplType;
        if(!Decl.isToplevel(iface) && !Decl.isLocal(iface)
                && Decl.getClassOrInterfaceContainer(iface, false) instanceof Interface){
            ifaceImplType = makeJavaType(bestSatisfiedType, JT_COMPANION | JT_SATISFIES | JT_NON_QUALIFIED);
        } else {
            ifaceImplType = makeJavaType(bestSatisfiedType, JT_COMPANION | JT_SATISFIES);
        }
       
        JCExpression newInstance = make().NewClass(containerInstance,
                null,
                ifaceImplType,
                state,
                null);
       
        JCExpressionStatement companionInstanceAssign = make().Exec(make().Assign(
                makeSelect("this", getCompanionFieldName(iface)),// TODO Use qualified name for quoting?
                newInstance));
        return companionInstanceAssign;
    }
   
    private MethodDefinitionBuilder makeCompanionAccessor(Interface iface, ProducedType satisfiedType,
            Class currentType, boolean forImplementor) {
        MethodDefinitionBuilder thisMethod = MethodDefinitionBuilder.systemMethod(
                this, naming.getCompanionAccessorName(iface));
        thisMethod.noModelAnnotations();
        if (!forImplementor && Decl.isAncestorLocal(iface)) {
            // For a local interface the return type cannot be a local
            // companion class, because that won't be visible at the
            // top level, so use Object instead
            thisMethod.resultType(null, make().Type(syms().objectType));
        } else {
            thisMethod.resultType(null, makeJavaType(satisfiedType, JT_COMPANION));
        }
        if (forImplementor) {
            thisMethod.isOverride(true);
        } else {
            thisMethod.ignoreModelAnnotations();
        }
        thisMethod.modifiers(PUBLIC);
        if (forImplementor) {
            thisMethod.body(make().Return(naming.makeCompanionFieldName(iface)));
        } else {
            thisMethod.noBody();
        }
        return thisMethod;
    }

    private ProducedType getBestSatisfiedType(ProducedType currentType, Interface iface) {
        ProducedType refinedSuperType = currentType.getSupertype(iface);
        ProducedType firstSatisfiedType = getFirstSatisfiedType(currentType, iface);
        // in the very special case of the first satisfied type having type arguments erased to Object and
        // the most refined one having free type parameters, we prefer the one with free type parameters
        // because Java prefers it and it's in range with what nonWideningType does
        Map<TypeParameter, ProducedType> refinedTAs = refinedSuperType.getTypeArguments();
        Map<TypeParameter, ProducedType> firstTAs = firstSatisfiedType.getTypeArguments();
        for(TypeParameter tp : iface.getTypeParameters()){
            ProducedType refinedTA = refinedTAs.get(tp);
            ProducedType firstTA = firstTAs.get(tp);
            if(willEraseToObject(firstTA) && isTypeParameter(refinedTA))
                return refinedSuperType;
        }
        return firstSatisfiedType;
    }

    private ProducedType getFirstSatisfiedType(ProducedType currentType, Interface iface) {
        ProducedType found = null;
        TypeDeclaration currentDecl = currentType.getDeclaration();
        if (Decl.equal(currentDecl, iface)) {
            return currentType;
        }
        if(currentDecl.getExtendedType() != null){
            ProducedType supertype = currentType.getSupertype(currentDecl.getExtendedTypeDeclaration());
            found = getFirstSatisfiedType(supertype, iface);
            if(found != null)
                return found;
        }
        for(ProducedType superInterfaceType : currentType.getSatisfiedTypes()){
            found = getFirstSatisfiedType(superInterfaceType, iface);
            if(found != null)
                return found;
        }
        return null;
    }

    private void buildCompanion(final Tree.ClassOrInterface def,
            final Interface model, ClassDefinitionBuilder classBuilder,
            TypeParameterList typeParameterList) {
        at(def);
        // Give the $impl companion a $this field...
        classBuilder.getCompanionBuilder(model, typeParameterList);
    }

    private List<JCAnnotation> makeLocalContainerPath(Interface model) {
        List<String> path = List.nil();
        Scope container = model.getContainer();
        while(container != null
                && container instanceof Package == false){
            if(container instanceof Declaration)
                path = path.prepend(((Declaration) container).getPrefixedName());
            container = container.getContainer();
        }
        return makeAtLocalContainer(path, model.isCompanionClassNeeded() ? model.getJavaCompanionClassName() : null);
    }

    public List<JCStatement> transformRefinementSpecifierStatement(SpecifierStatement op, ClassDefinitionBuilder classBuilder) {
        List<JCStatement> result = List.<JCStatement>nil();
        // Check if this is a shortcut form of formal attribute refinement
        if (op.getRefinement()) {
            Tree.Term baseMemberTerm = op.getBaseMemberExpression();
            if(baseMemberTerm instanceof Tree.ParameterizedExpression)
                baseMemberTerm = ((Tree.ParameterizedExpression)baseMemberTerm).getPrimary();
           
            Tree.BaseMemberExpression expr = (BaseMemberExpression) baseMemberTerm;
            Declaration decl = expr.getDeclaration();
           
            if (Decl.isValue(decl) || Decl.isGetter(decl)) {
                // Now build a "fake" declaration for the attribute
                Tree.AttributeDeclaration attrDecl = new Tree.AttributeDeclaration(null);
                attrDecl.setDeclarationModel((Value)decl);
                attrDecl.setIdentifier(expr.getIdentifier());
                attrDecl.setScope(op.getScope());
                attrDecl.setSpecifierOrInitializerExpression(op.getSpecifierExpression());
                attrDecl.setAnnotationList(makeShortcutRefinementAnnotationTrees());
               
                // Make sure the boxing information is set correctly
                BoxingDeclarationVisitor v = new CompilerBoxingDeclarationVisitor(this);
                v.visit(attrDecl);
               
                // Generate the attribute
                transform(attrDecl, classBuilder);
            } else if (decl instanceof Method) {
                // Now build a "fake" declaration for the method
                Tree.MethodDeclaration methDecl = new Tree.MethodDeclaration(null);
                Method m = (Method)decl;
                methDecl.setDeclarationModel(m);
                methDecl.setIdentifier(expr.getIdentifier());
                methDecl.setScope(op.getScope());
                methDecl.setAnnotationList(makeShortcutRefinementAnnotationTrees());
               
                Tree.SpecifierExpression specifierExpression = op.getSpecifierExpression();
                methDecl.setSpecifierExpression(specifierExpression);
               
                if(specifierExpression instanceof Tree.LazySpecifierExpression == false){
                    Tree.Expression expression = specifierExpression.getExpression();
                    Tree.Term expressionTerm = Decl.unwrapExpressionsUntilTerm(expression);
                    // we can optimise lambdas and static method calls
                    if(!CodegenUtil.canOptimiseMethodSpecifier(expressionTerm, m)){
                        // we need a field to save the callable value
                        String name = naming.getMethodSpecifierAttributeName(m);
                        JCExpression specifierType = makeJavaType(expression.getTypeModel());
                        JCExpression specifier = expressionGen().transformExpression(expression);
                        classBuilder.field(PRIVATE | FINAL, name, specifierType, specifier, false);
                    }
                }

                // copy from formal declaration
                for (ParameterList pl : m.getParameterLists()) {
                    Tree.ParameterList tpl = new Tree.ParameterList(null);
                    tpl.setModel(pl);
                    for (Parameter p : pl.getParameters()) {
                        Tree.Parameter tp = null;
                        if (p.getModel() instanceof Value) {
                            Tree.ValueParameterDeclaration tvpd = new Tree.ValueParameterDeclaration(null);
                            tvpd.setParameterModel(p);
                            tp = tvpd;
                        } else if (p.getModel() instanceof Method) {
                            Tree.FunctionalParameterDeclaration tfpd = new Tree.FunctionalParameterDeclaration(null);
                            tfpd.setParameterModel(p);
                            tp = tfpd;
                        } else {
                            throw BugException.unhandledDeclarationCase(p.getModel());
                        }
                        tp.setScope(p.getDeclaration().getContainer());
                        //tp.setIdentifier(makeIdentifier(p.getName()));
                        tpl.addParameter(tp);
                    }
                    methDecl.addParameterList(tpl);
                }
               
                // Make sure the boxing information is set correctly
                BoxingDeclarationVisitor v = new CompilerBoxingDeclarationVisitor(this);
                v.visit(methDecl);
               
                // Generate the method
                classBuilder.method(methDecl, Errors.GENERATE);
            }
        } else {
            // Normal case, just generate the specifier statement
            result = result.append(expressionGen().transform(op));
        }
       
        Tree.Term term = op.getBaseMemberExpression();
        if (term instanceof Tree.BaseMemberExpression) {
            Tree.BaseMemberExpression bme = (Tree.BaseMemberExpression)term;
            DeferredSpecification ds = statementGen().getDeferredSpecification(bme.getDeclaration());
            if (ds != null && needsInnerSubstitution(term.getScope(), bme.getDeclaration())){
                result = result.append(ds.openInnerSubstitution());
            }
        }
        return result;
    }

    private AnnotationList makeShortcutRefinementAnnotationTrees() {
        AnnotationList annotationList = new AnnotationList(null);
       
        Tree.Annotation shared = new Tree.Annotation(null);
        Tree.BaseMemberExpression sharedPrimary = new Tree.BaseMemberExpression(null);
        sharedPrimary.setDeclaration(typeFact().getLanguageModuleDeclaration("shared"));
        shared.setPrimary(sharedPrimary);
        annotationList.addAnnotation(shared);
       
        Tree.Annotation actual = new Tree.Annotation(null);
        Tree.BaseMemberExpression actualPrimary = new Tree.BaseMemberExpression(null);
        actualPrimary.setDeclaration(typeFact().getLanguageModuleDeclaration("actual"));
        actual.setPrimary(actualPrimary);
        annotationList.addAnnotation(actual);
       
        return annotationList;
    }

    /**
     * We only need an inner substitution if we're within that substitution's scope
     */
    private boolean needsInnerSubstitution(Scope scope, Declaration declaration) {
        while(scope != null && scope instanceof Package == false){
            if(scope instanceof ControlBlock){
                Set<Value> specifiedValues = ((ControlBlock) scope).getSpecifiedValues();
                if(specifiedValues != null && specifiedValues.contains(declaration))
                    return true;
            }
            scope = scope.getScope();
        }
        return false;
    }

    public void transform(AttributeDeclaration decl, ClassDefinitionBuilder classBuilder) {
        final Value model = decl.getDeclarationModel();
        boolean lazy = decl.getSpecifierOrInitializerExpression() instanceof LazySpecifierExpression;
        boolean useField = Strategy.useField(model) && !lazy;
        String attrName = decl.getIdentifier().getText();

        // Only a non-formal or a concrete-non-lazy attribute has a corresponding field
        // and if a captured class parameter exists with the same name we skip this part as well
        Parameter parameter = CodegenUtil.findParamForDecl(decl);
        boolean createField = Strategy.createField(parameter, model) && !lazy;
        boolean concrete = Decl.withinInterface(decl)
                && decl.getSpecifierOrInitializerExpression() != null;
        if (!lazy &&
                (concrete ||
                        (!Decl.isFormal(decl)
                                && createField))) {
            ProducedTypedReference typedRef = getTypedReference(model);
            ProducedTypedReference nonWideningTypedRef = nonWideningTypeDecl(typedRef);
            ProducedType nonWideningType = nonWideningType(typedRef, nonWideningTypedRef);
           
            if (Decl.isIndirect(decl)) {
                attrName = Naming.getAttrClassName(model, 0);
                nonWideningType = getGetterInterfaceType(model);
            }
           
            JCExpression initialValue = null;
            if (decl.getSpecifierOrInitializerExpression() != null) {
                Value declarationModel = model;
                initialValue = expressionGen().transformExpression(decl.getSpecifierOrInitializerExpression().getExpression(),
                        CodegenUtil.getBoxingStrategy(declarationModel),
                        nonWideningType);
            }

            int flags = 0;
           
            if (!CodegenUtil.isUnBoxed(nonWideningTypedRef.getDeclaration())) {
                flags |= JT_NO_PRIMITIVES;
            }
            JCExpression type = makeJavaType(nonWideningType, flags);
           
            int modifiers = (useField) ? transformAttributeFieldDeclFlags(decl) : transformLocalDeclFlags(decl);
           
            // If the attribute is really from a parameter then don't generate a field
            // (makeAttributeForValueParameter() or makeMethodForFunctionalParameter()
            //  does it in those cases)
            if (parameter == null
                    || parameter.isHidden()) {
                if (concrete) {
                    classBuilder.getCompanionBuilder((TypeDeclaration)model.getContainer()).field(modifiers, attrName, type, initialValue, !useField);
                } else {
                    // fields should be ignored, they are accessed by the getters
                    classBuilder.field(modifiers, attrName, type, initialValue, !useField, makeAtIgnore());
                    if (model.isLate()) {
                        classBuilder.field(PRIVATE | Flags.VOLATILE, Naming.getInitializationFieldName(attrName),
                                make().Type(syms().booleanType),
                                make().Literal(false), false, makeAtIgnore());
                    }
                }       
            }
           
            // A shared attribute might be initialized in a for statement, so
            // we might need a def-assignment subst for it
            List<JCAnnotation> annots = makeJavaTypeAnnotations(decl.getDeclarationModel());
            JCStatement outerSubs = statementGen().openOuterSubstitutionIfNeeded(
                    decl.getDeclarationModel(), model.getType(), annots, 0);
            if (outerSubs != null) {
                classBuilder.init(outerSubs);
            }
        }

        boolean withinInterface = Decl.withinInterface(decl);
        if (useField || withinInterface || lazy) {
            if (!withinInterface || model.isShared()) {
                // Generate getter in main class or interface (when shared)
                classBuilder.attribute(makeGetter(decl, false, lazy));
            }
            if (withinInterface && lazy) {
                // Generate getter in companion class
                classBuilder.getCompanionBuilder((Interface)decl.getDeclarationModel().getContainer()).attribute(makeGetter(decl, true, lazy));
            }
            if (Decl.isVariable(decl) || Decl.isLate(decl)) {
                if (!withinInterface || model.isShared()) {
                    // Generate setter in main class or interface (when shared)
                    classBuilder.attribute(makeSetter(decl, false, lazy));
                }
                if (withinInterface && lazy) {
                    // Generate setter in companion class
                    classBuilder.getCompanionBuilder((Interface)decl.getDeclarationModel().getContainer()).attribute(makeSetter(decl, true, lazy));
                }
            }
        }
    }

  public AttributeDefinitionBuilder transform(AttributeSetterDefinition decl, boolean forCompanion) {
      if (Strategy.onlyOnCompanion(decl.getDeclarationModel()) && !forCompanion) {
          return null;
      }
        String name = decl.getIdentifier().getText();
        final AttributeDefinitionBuilder builder = AttributeDefinitionBuilder
                /*
                 * We use the getter as TypedDeclaration here because this is the same type but has a refined
                 * declaration we can use to make sure we're not widening the attribute type.
                 */
            .setter(this, name, decl.getDeclarationModel().getGetter())
            .modifiers(transformAttributeGetSetDeclFlags(decl.getDeclarationModel(), forCompanion));
       
        // companion class members are never actual no matter what the Declaration says
        if(forCompanion)
            builder.notActual();
       
        if (Decl.withinClass(decl) || forCompanion) {
            JCBlock setterBlock = makeSetterBlock(decl.getDeclarationModel(), decl.getBlock(), decl.getSpecifierExpression());
            builder.setterBlock(setterBlock);
        } else {
            builder.isFormal(true);
        }
        builder.userAnnotationsSetter(expressionGen().transform(decl.getAnnotationList()));
        return builder;
    }

    public AttributeDefinitionBuilder transform(AttributeGetterDefinition decl, boolean forCompanion) {
        if (Strategy.onlyOnCompanion(decl.getDeclarationModel()) && !forCompanion) {
            return null;
        }
        String name = decl.getIdentifier().getText();
        //expressionGen().transform(decl.getAnnotationList());
        final AttributeDefinitionBuilder builder = AttributeDefinitionBuilder
            .getter(this, name, decl.getDeclarationModel())
            .modifiers(transformAttributeGetSetDeclFlags(decl.getDeclarationModel(), forCompanion));
       
        // companion class members are never actual no matter what the Declaration says
        if(forCompanion)
            builder.notActual();
       
        if (Decl.withinClass(decl) || forCompanion) {
            JCBlock body = statementGen().transform(decl.getBlock());
            builder.getterBlock(body);
        } else {
            builder.isFormal(true);
        }
        builder.userAnnotations(expressionGen().transform(decl.getAnnotationList()));
        return builder;   
    }

    private int transformDeclarationSharedFlags(Declaration decl){
        return Decl.isShared(decl) && !Decl.isAncestorLocal(decl) ? PUBLIC : 0;
    }
   
    private int transformClassDeclFlags(ClassOrInterface cdecl) {
        int result = 0;

        result |= transformDeclarationSharedFlags(cdecl);
        // aliases cannot be abstract, especially since they're just placeholders
        result |= (cdecl instanceof Class) && (cdecl.isAbstract() || cdecl.isFormal()) && !cdecl.isAlias() ? ABSTRACT : 0;
        result |= (cdecl instanceof Interface) ? INTERFACE : 0;
        // aliases are always final placeholders, final classes are also final
        result |= (cdecl instanceof Class) && (cdecl.isAlias() || cdecl.isFinal())  ? FINAL : 0;

        return result;
    }

    private int transformTypeAliasDeclFlags(TypeAlias decl) {
        int result = 0;

        result |= transformDeclarationSharedFlags(decl);
        result |= FINAL;

        return result;
    }

    private int transformClassDeclFlags(Tree.ClassOrInterface cdecl) {
        return transformClassDeclFlags(cdecl.getDeclarationModel());
    }
   
    private int transformMethodDeclFlags(Method def) {
        int result = 0;

        if (def.isToplevel()) {
            result |= def.isShared() ? PUBLIC : 0;
            result |= STATIC;
        } else if (Decl.isLocalNotInitializer(def)) {
            result |= def.isShared() ? PUBLIC : 0;
        } else {
            result |= def.isShared() ? PUBLIC : PRIVATE;
            result |= def.isFormal() && !def.isDefault() ? ABSTRACT : 0;
            result |= !(def.isFormal() || def.isDefault() || def.getContainer() instanceof Interface) ? FINAL : 0;
        }

        return result;
    }
   
    private int transformAttributeFieldDeclFlags(Tree.AttributeDeclaration cdecl) {
        int result = 0;

        result |= Decl.isVariable(cdecl) || Decl.isLate(cdecl) ? 0 : FINAL;
        result |= PRIVATE;

        return result;
    }

    private int transformLocalDeclFlags(Tree.AttributeDeclaration cdecl) {
        int result = 0;

        result |= Decl.isVariable(cdecl) ? 0 : FINAL;

        return result;
    }

    /**
     * Returns the modifier flags to be used for the getter & setter for the
     * given attribute-like declaration. 
     * @param tdecl attribute-like declaration (Value, Getter, Parameter etc)
     * @param forCompanion Whether the getter/setter is on a companion type
     * @return The modifier flags.
     */
    int transformAttributeGetSetDeclFlags(TypedDeclaration tdecl, boolean forCompanion) {
        if (tdecl instanceof Setter) {
            // Spec says: A setter may not be annotated shared, default or
            // actual. The visibility and refinement modifiers of an attribute
            // with a setter are specified by annotating the matching getter.
            tdecl = ((Setter)tdecl).getGetter();
        }
       
        int result = 0;

        result |= tdecl.isShared() ? PUBLIC : PRIVATE;
        result |= ((tdecl.isFormal() && !tdecl.isDefault()) && !forCompanion) ? ABSTRACT : 0;
        result |= !(tdecl.isFormal() || tdecl.isDefault() || Decl.withinInterface(tdecl)) || forCompanion ? FINAL : 0;

        return result;
    }

    private int transformObjectDeclFlags(Value cdecl) {
        int result = 0;

        result |= FINAL;
        result |= !Decl.isAncestorLocal(cdecl) && Decl.isShared(cdecl) ? PUBLIC : 0;

        return result;
    }

    private AttributeDefinitionBuilder makeGetterOrSetter(Tree.AttributeDeclaration decl, boolean forCompanion, boolean lazy,
                                                          AttributeDefinitionBuilder builder, boolean isGetter) {
        at(decl);
        if (forCompanion || lazy) {
            SpecifierOrInitializerExpression specOrInit = decl.getSpecifierOrInitializerExpression();
            if (specOrInit != null) {
                HasErrorException error = errors().getFirstExpressionErrorAndMarkBrokenness(specOrInit.getExpression());
                if (error != null) {
                    builder.getterBlock(make().Block(0, List.<JCStatement>of(this.makeThrowUnresolvedCompilationError(error))));
                } else {
                    Value declarationModel = decl.getDeclarationModel();
                    ProducedTypedReference typedRef = getTypedReference(declarationModel);
                    ProducedTypedReference nonWideningTypedRef = nonWideningTypeDecl(typedRef);
                    ProducedType nonWideningType = nonWideningType(typedRef, nonWideningTypedRef);
                   
                    JCExpression expr = expressionGen().transformExpression(specOrInit.getExpression(),
                            CodegenUtil.getBoxingStrategy(declarationModel),
                            nonWideningType);
                    expr = convertToIntIfHashAttribute(declarationModel, expr);
                    builder.getterBlock(make().Block(0, List.<JCStatement>of(make().Return(expr))));
                }
            } else {
                JCExpression accessor = naming.makeQualifiedName(
                        naming.makeQuotedThis(),
                        decl.getDeclarationModel(),
                        Naming.NA_MEMBER | (isGetter ? Naming.NA_GETTER : Naming.NA_SETTER));
               
                if (isGetter) {
                    builder.getterBlock(make().Block(0, List.<JCStatement>of(make().Return(
                            make().Apply(
                                    null,
                                    accessor,
                                    List.<JCExpression>nil())))));
                } else {
                    List<JCExpression> args = List.<JCExpression>of(naming.makeName(decl.getDeclarationModel(), Naming.NA_MEMBER | Naming.NA_IDENT));
                    builder.setterBlock(make().Block(0, List.<JCStatement>of(make().Exec(
                            make().Apply(
                                    null,
                                    accessor,
                                    args)))));
                }
               
            }
        }
        if(forCompanion)
            builder.notActual();
        return builder
            .modifiers(transformAttributeGetSetDeclFlags(decl.getDeclarationModel(), forCompanion))
            .isFormal((Decl.isFormal(decl) || Decl.withinInterface(decl)) && !forCompanion);
    }
   
    private AttributeDefinitionBuilder makeGetter(Tree.AttributeDeclaration decl, boolean forCompanion, boolean lazy) {
        at(decl);
        String attrName = decl.getIdentifier().getText();
        AttributeDefinitionBuilder getter = AttributeDefinitionBuilder
            .getter(this, attrName, decl.getDeclarationModel());
        if(!forCompanion)
            getter.userAnnotations(expressionGen().transform(decl.getAnnotationList()));
        else
            getter.ignoreAnnotations();
       
        if (Decl.isIndirect(decl)) {
            getter.getterBlock(generateIndirectGetterBlock(decl.getDeclarationModel()));
        }
       
        return makeGetterOrSetter(decl, forCompanion, lazy, getter, true);
    }

    private JCTree.JCBlock generateIndirectGetterBlock(Value v) {
        JCTree.JCExpression returnExpr;
        returnExpr = naming.makeQualIdent(naming.makeName(v, Naming.NA_WRAPPER), "get_");
        returnExpr = make().Apply(null, returnExpr, List.<JCExpression>nil());
        JCReturn returnValue = make().Return(returnExpr);
        List<JCStatement> stmts = List.<JCTree.JCStatement>of(returnValue);  
        JCTree.JCBlock block = make().Block(0L, stmts);
        return block;
    }

    private AttributeDefinitionBuilder makeSetter(Tree.AttributeDeclaration decl, boolean forCompanion, boolean lazy) {
        at(decl);
        String attrName = decl.getIdentifier().getText();
        AttributeDefinitionBuilder setter = AttributeDefinitionBuilder.setter(this, attrName, decl.getDeclarationModel());
        return makeGetterOrSetter(decl, forCompanion, lazy, setter, false);
    }

    public List<JCTree> transformWrappedMethod(Tree.AnyMethod def, TransformationPlan plan) {
        final Method model = def.getDeclarationModel();
        if (model.isParameter()) {
            return List.nil();
        }
        naming.clearSubstitutions(model);
        // Generate a wrapper class for the method
        String name = def.getIdentifier().getText();
        ClassDefinitionBuilder builder = ClassDefinitionBuilder.methodWrapper(this, name, Decl.isShared(def));
       
        if (Decl.isAnnotationConstructor(def)) {
            AnnotationInvocation ai = ((AnnotationInvocation)def.getDeclarationModel().getAnnotationConstructor());
            if (ai != null) {
                builder.annotations(List.of(makeAtAnnotationInstantiation(ai)));
                builder.annotations(makeExprAnnotations(def, ai));
            }
        }
       
        builder.methods(classGen().transform(def, plan, builder));
       
        // Toplevel method
        if (Strategy.generateMain(def)) {
            // Add a main() method
            builder.method(makeMainForFunction(model));
        }
       
        if(Decl.isLocal(model) || Decl.isToplevel(model)){
            builder.annotations(makeAtLocalDeclarations(def));
        }
        if(Decl.isLocal(model)){
            builder.annotations(makeAtLocalDeclaration(model.getQualifier(), false));
        }
       
        List<JCTree> result = builder.build();
       
        if (Decl.isLocal(def)) {
            // Inner method
            JCVariableDecl call = at(def).VarDef(
                    make().Modifiers(FINAL),
                    naming.getSyntheticInstanceName(model),
                    naming.makeSyntheticClassname(model),
                    makeSyntheticInstance(model));
            result = result.append(call);
        }
       
        //if (Decl.isAnnotationConstructor(def)) {
            //result = result.prependList(transformAnnotationConstructorType(def));
        //}
        return result;
    }

    /**
     * Make the {@code @*Exprs} annotations to hold the literal arguments
     * to the invocation.
     */
    private List<JCAnnotation> makeExprAnnotations(Tree.AnyMethod def,
            AnnotationInvocation ai) {
        AnnotationInvocation ctor = (AnnotationInvocation)def.getDeclarationModel().getAnnotationConstructor();
        return ai.makeExprAnnotations(expressionGen(), ctor, List.<AnnotationFieldName>nil());
    }
   
    public JCAnnotation makeAtAnnotationInstantiation(AnnotationInvocation invocation) {
        return invocation.encode(this, ListBuffer.<JCExpression>lb());
    }

    private MethodDefinitionBuilder makeAnnotationMethod(Tree.Parameter parameter) {
        Parameter parameterModel = parameter.getParameterModel();
        JCExpression type = transformAnnotationMethodType(parameter);
        JCExpression defaultValue = parameterModel.isDefaulted() ? transformAnnotationParameterDefault(parameter) : null;
        MethodDefinitionBuilder mdb = MethodDefinitionBuilder.method(this, parameterModel.getModel(), Naming.NA_ANNOTATION_MEMBER);
        if (isMetamodelReference(parameterModel.getType())
                ||
                (typeFact().isIterableType(parameterModel.getType())
                && isMetamodelReference(typeFact().getIteratedType(parameterModel.getType())))) {
            mdb.modelAnnotations(List.of(make().Annotation(make().Type(syms().ceylonAtDeclarationReferenceType),
                    List.<JCExpression>nil())));
        } else if (Decl.isEnumeratedTypeWithAnonCases(parameterModel.getType())
                ||
                (typeFact().isIterableType(parameterModel.getType())
                        && Decl.isEnumeratedTypeWithAnonCases(typeFact().getIteratedType(parameterModel.getType())))) {
            mdb.modelAnnotations(List.of(make().Annotation(make().Type(syms().ceylonAtEnumerationReferenceType),
                    List.<JCExpression>nil())));
        }
        mdb.modifiers(PUBLIC | ABSTRACT);
        mdb.resultType(null, type);
        mdb.defaultValue(defaultValue);
        mdb.noBody();
        return mdb;
    }

    public List<MethodDefinitionBuilder> transform(Tree.AnyMethod def, TransformationPlan plan, ClassDefinitionBuilder classBuilder) {
        if (def.getDeclarationModel().isParameter()) {
            return List.nil();
        }
        if (plan instanceof ThrowerMethod) {
            addRefinedThrowerMethod(classBuilder,
                    plan.getErrorMessage().getMessage(),
                    (Class)def.getDeclarationModel().getContainer(),
                    (Method)def.getDeclarationModel().getRefinedDeclaration());
            return List.<MethodDefinitionBuilder>nil();
        }
        // Transform the method body of the 'inner-most method'
        boolean prevSyntheticClassBody = expressionGen().withinSyntheticClassBody(Decl.isMpl(def.getDeclarationModel())
                || Decl.isLocalNotInitializer(def)
                || expressionGen().isWithinSyntheticClassBody());
        List<JCStatement> body = transformMethodBody(def);
        expressionGen().withinSyntheticClassBody(prevSyntheticClassBody);
        return transform(def, classBuilder, body);
    }

    List<MethodDefinitionBuilder> transform(Tree.AnyMethod def,
            ClassDefinitionBuilder classBuilder, List<JCStatement> body) {
        final Method model = def.getDeclarationModel();
       
        List<MethodDefinitionBuilder> result = List.<MethodDefinitionBuilder>nil();
        if (!Decl.withinInterface(model)) {
            // Transform to the class
            boolean refinedResultType = !model.getType().isExactly(
                    ((TypedDeclaration)model.getRefinedDeclaration()).getType());
            result = transformMethod(def,
                    true,
                    true,
                    true,
                    transformMplBodyUnlessSpecifier(def, model, body),
                    refinedResultType
                    && !Decl.withinInterface(model.getRefinedDeclaration())? daoSuper : daoThis,
                    !Strategy.defaultParameterMethodOnSelf(model));
        } else {// Is within interface
            // Transform the definition to the companion class, how depends
            // on what kind of method it is
            List<MethodDefinitionBuilder> companionDefs;
            if (def instanceof Tree.MethodDeclaration) {
                final SpecifierExpression specifier = ((Tree.MethodDeclaration) def).getSpecifierExpression();
                if (specifier == null) {
                    // formal or abstract
                    // (still need overloads and DPMs on the companion)
                    companionDefs = transformMethod(def, 
                            false,
                            true,
                            true,
                            null,
                            daoCompanion,
                            false);  
                } else {
                    companionDefs = transformMethod(def,
                            true,
                            false,
                            !model.isShared(),
                            transformMplBodyUnlessSpecifier(def, model, body),
                            daoCompanion,
                            false);
                }
            } else if (def instanceof Tree.MethodDefinition) {
                companionDefs = transformMethod(def, 
                        true,
                        false,
                        !model.isShared(),
                        transformMplBodyUnlessSpecifier(def, model, body),
                        daoCompanion,
                        false);
            } else {
                throw BugException.unhandledNodeCase(def);
            }
            if(!companionDefs.isEmpty())
                classBuilder.getCompanionBuilder((TypeDeclaration)model.getContainer())
                    .methods(companionDefs);
           
            // Transform the declaration to the target interface
            // but only if it's shared
            if (Decl.isShared(model)) {
                result = transformMethod(def,
                        true,
                        true,
                        true,
                        null,
                        daoAbstract,
                        !Strategy.defaultParameterMethodOnSelf(model));
            }
        }
        return result;
    }

   
    /**
     * Transforms a method, generating default argument overloads and
     * default value methods
     * @param def The method
     * @param model The method model
     * @param methodName The method name
     * @param transformMethod Whether the method itself should be transformed.
     * @param actualAndAnnotations Whether the method itself is actual and has
     * model annotations
     * @param body The body of the method (or null for an abstract method)
     * @param daoTransformation The default argument overload transformation
     * @param transformDefaultValues Whether to generate default value methods
     * @param defaultValuesBody Whether the default value methods should have a body
     */
    private List<MethodDefinitionBuilder> transformMethod(Tree.AnyMethod def,
            boolean transformMethod, boolean actual, boolean includeAnnotations, List<JCStatement> body,
            DaoBody daoTransformation,
            boolean defaultValuesBody) {
        return transformMethod(def.getDeclarationModel(),
                def.getTypeParameterList(),
                def.getParameterLists(),
                def.getAnnotationList(),
                transformMethod, actual, includeAnnotations, body,
                daoTransformation,
                defaultValuesBody);
    }
   
    private List<MethodDefinitionBuilder> transformMethod(
            final Method methodModel,
            Tree.TypeParameterList typeParameterList,
            java.util.List<Tree.ParameterList> parameterLists,
            Tree.AnnotationList annotationList,
            boolean transformMethod, boolean actual, boolean includeAnnotations, List<JCStatement> body,
            DaoBody daoTransformation,
            boolean defaultValuesBody) {
       
        ListBuffer<MethodDefinitionBuilder> lb = ListBuffer.<MethodDefinitionBuilder>lb();
        Declaration refinedDeclaration = methodModel.getRefinedDeclaration();
       
        final MethodDefinitionBuilder methodBuilder = MethodDefinitionBuilder.method(this, methodModel);
       
        // do the reified type param arguments
        if (typeParameterList != null && gen().supportsReified(methodModel)) {
            methodBuilder.reifiedTypeParameters(typeParameterListModel(typeParameterList));
        }
       
        if (methodModel.getParameterLists().size() > 1) {
            methodBuilder.mpl(methodModel.getParameterLists());
        }
       
        boolean hasOverloads = false;
        Tree.ParameterList parameterList = parameterLists.get(0);
        for (final Tree.Parameter parameter : parameterList.getParameters()) {
            Parameter parameterModel = parameter.getParameterModel();
            List<JCAnnotation> annotations = null;
            if (includeAnnotations
                    && parameter instanceof Tree.ParameterDeclaration
                    && ((Tree.ParameterDeclaration)parameter).getTypedDeclaration() != null) {
                annotations = expressionGen().transform(((Tree.ParameterDeclaration)parameter).getTypedDeclaration().getAnnotationList());
            }
           
            methodBuilder.parameter(parameterModel, annotations, 0, true);

            if (Strategy.hasDefaultParameterValueMethod(parameterModel)
                    || Strategy.hasDefaultParameterOverload(parameterModel)) {
                if (Decl.equal(refinedDeclaration, methodModel)
                        || (!Decl.withinInterface(methodModel) && body != null)) {
                   
                    if (daoTransformation != null && (daoTransformation instanceof DaoCompanion == false || body != null)) {
                        DaoBody daoTrans = (body == null) ? daoAbstract : daoThis;
                        MethodDefinitionBuilder overloadBuilder = MethodDefinitionBuilder.method(this, methodModel);
                        overloadBuilder.location(null);
                        MethodDefinitionBuilder overloadedMethod = new DefaultedArgumentMethod(daoTrans, methodModel)
                            .makeOverload(
                                overloadBuilder,
                                parameterList.getModel(),
                                parameter.getParameterModel(),
                                typeParameterListModel(typeParameterList));
                        lb.append(overloadedMethod);
                    }
                   
                    if (Decl.equal(refinedDeclaration, methodModel)
                            && Strategy.hasDefaultParameterValueMethod(parameterModel)) {
                        lb.append(makeParamDefaultValueMethod(defaultValuesBody, methodModel, parameterList, parameter, typeParameterList));
                    }
                }
               
                hasOverloads = true;
            }
        }

        // Determine if we need to generate a "canonical" method
        boolean createCanonical = hasOverloads
                && Decl.withinClassOrInterface(methodModel)
                && body != null;
       
        if (createCanonical) {
            // Creates the private "canonical" method containing the actual body
            MethodDefinitionBuilder canonicalBuilder = MethodDefinitionBuilder.method(this, methodModel, Naming.NA_CANONICAL_METHOD);
            MethodDefinitionBuilder canonicalMethod = new CanonicalMethod(daoTransformation, methodModel, body)
                .makeOverload(
                    canonicalBuilder,
                    parameterList.getModel(),
                    null,
                    typeParameterListModel(typeParameterList));
            lb.append(canonicalMethod);
        }
       
        if (transformMethod) {
            methodBuilder.modifiers(transformMethodDeclFlags(methodModel));
            if (actual) {
                methodBuilder.isOverride(methodModel.isActual());
            }
            if (includeAnnotations) {
                methodBuilder.userAnnotations(expressionGen().transform(annotationList));
                methodBuilder.modelAnnotations(methodModel.getAnnotations());
            } else {
                methodBuilder.ignoreModelAnnotations();
            }
            methodBuilder.resultType(methodModel, 0);
            copyTypeParameters(methodModel, methodBuilder);
           
            if (createCanonical) {
                // Creates method that redirects to the "canonical" method containing the actual body
                MethodDefinitionBuilder overloadedMethod = new CanonicalMethod(daoThis, methodModel)
                    .makeOverload(
                        methodBuilder,
                        parameterList.getModel(),
                        null,
                        typeParameterListModel(typeParameterList));
                lb.append(overloadedMethod);
            } else {
                if (body != null) {
                    // Construct the outermost method using the body we've built so far
                    methodBuilder.body(body);
                } else {
                    methodBuilder.noBody();
                }
                lb.append(methodBuilder);
            }
        }
        return lb.toList();
    }

    List<JCStatement> transformMplBodyUnlessSpecifier(Tree.AnyMethod def,
            Method model,
            List<JCStatement> body) {
        if (def instanceof Tree.MethodDeclaration) {
            Tree.SpecifierExpression specifier = ((Tree.MethodDeclaration)def).getSpecifierExpression();
            if (specifier == null) {
                return body;
            } else if (!(specifier instanceof Tree.LazySpecifierExpression)) {
                if (!CodegenUtil.canOptimiseMethodSpecifier(specifier.getExpression().getTerm(), model)) {
                    return body;
                }
            }
        }
        return transformMplBody(def.getParameterLists(), model, body);
    }
   
    /**
     * Constructs all but the outer-most method of a {@code Method} with
     * multiple parameter lists
     * @param model The {@code Method} model
     * @param body The inner-most body
     */
    List<JCStatement> transformMplBody(java.util.List<Tree.ParameterList> parameterListsTree,
            Method model,
            List<JCStatement> body) {
        ProducedType resultType = model.getType();
        for (int index = model.getParameterLists().size() - 1; index >  0; index--) {
            ParameterList pl = model.getParameterLists().get(index);
            resultType = gen().typeFact().getCallableType(List.of(resultType, typeFact().getParameterTypesAsTupleType(pl.getParameters(), model.getReference())));
            CallableBuilder cb = CallableBuilder.mpl(gen(), resultType, pl, parameterListsTree.get(index), body);
            body = List.<JCStatement>of(make().Return(cb.build()));
        }
        return body;
    }

    private List<JCStatement> transformMethodBody(Tree.AnyMethod def) {
        List<JCStatement> body = null;
        final Method model = def.getDeclarationModel();
       
        if (model.isDeferred()) {
            // Uninitialized or deferred initialized method => Make a Callable field
            String fieldName = naming.selector(model);
            final Parameter initializingParameter = CodegenUtil.findParamForDecl(def);
            int mods = PRIVATE;
            JCExpression initialValue;
            if (initializingParameter != null) {
                mods |= FINAL;
                initialValue = makeUnquotedIdent(Naming.getAliasedParameterName(initializingParameter));
            } else {
                // The field isn't initialized by a parameter, but later in the block
                initialValue = makeNull();
            }
            ProducedType callableType = model.getReference().getFullType();
            current().field(mods, fieldName, makeJavaType(callableType), initialValue, false);
            Invocation invocation = new CallableSpecifierInvocation(
                    this,
                    model,
                    makeUnquotedIdent(fieldName),
                    // we don't have to give a Term here because it's used for casting the Callable in case of callable erasure,
                    // but with deferred methods we can't define them so that they are erased so we're good
                    null,
                    def);
            invocation.handleBoxing(true);
            JCExpression call = expressionGen().transformInvocation(invocation);
            JCStatement stmt;
            if (!isVoid(def) || !Decl.isUnboxedVoid(model) || Strategy.useBoxedVoid((Method)model)) {
                stmt = make().Return(call);
            } else {
                stmt = make().Exec(call);
            }
           
            JCStatement result;
            if (initializingParameter == null) {
                // If the field isn't initialized by a parameter we have to
                // cope with the possibility that it's never initialized
                final JCBinary cond = make().Binary(JCTree.EQ, makeUnquotedIdent(fieldName), makeNull());
                final JCStatement throw_ = make().Throw(make().NewClass(null, null,
                        makeIdent(syms().ceylonUninitializedMethodErrorType),
                        List.<JCExpression>nil(),
                        null));
                result = make().If(cond, throw_, stmt);
            } else {
                result = stmt;
            }
            return List.<JCStatement>of(result);
        } else if (def instanceof Tree.MethodDefinition) {
            body = transformMethodBlock((Tree.MethodDefinition)def);
        } else if (def instanceof MethodDeclaration
                && ((MethodDeclaration) def).getSpecifierExpression() != null) {
            body = transformSpecifiedMethodBody((MethodDeclaration)def, ((MethodDeclaration) def).getSpecifierExpression());
        }
        return body;
    }

    private List<JCStatement> transformMethodBlock(
            final Tree.MethodDefinition def) {
        final Method model = def.getDeclarationModel();
        final Tree.Block block = def.getBlock();
        List<JCStatement> body;
        boolean prevNoExpressionlessReturn = statementGen().noExpressionlessReturn;
        try {
            statementGen().noExpressionlessReturn = Decl.isMpl(model) || Strategy.useBoxedVoid(model);
            body = statementGen().transformBlock(block);
        } finally {
            statementGen().noExpressionlessReturn = prevNoExpressionlessReturn;
        }
        // We void methods need to have their Callables return null
        // so adjust here.
        if ((Decl.isMpl(model) || Strategy.useBoxedVoid(model))
                && !block.getDefinitelyReturns()) {
            if (Decl.isUnboxedVoid(model)) {
                body = body.append(make().Return(makeNull()));
            } else {
                body = body.append(make().Return(makeErroneous(block, "compiler bug: non-void method doesn't definitely return")));
            }
        }
        return body;
    }

    List<JCStatement> transformSpecifiedMethodBody(Tree.MethodDeclaration  def, SpecifierExpression specifierExpression) {
        final Method model = def.getDeclarationModel();
        List<JCStatement> body;
        Tree.MethodDeclaration methodDecl = def;
        boolean isLazy = specifierExpression instanceof Tree.LazySpecifierExpression;
        boolean returnNull = false;
        JCExpression bodyExpr;
        Tree.Term term = null;
        if (specifierExpression != null
                && specifierExpression.getExpression() != null) {
            term = Decl.unwrapExpressionsUntilTerm(specifierExpression.getExpression());
            HasErrorException error = errors().getFirstExpressionErrorAndMarkBrokenness(term);
            if (error != null) {
                return List.<JCStatement>of(this.makeThrowUnresolvedCompilationError(error));
            }
        }
        if (!isLazy && term instanceof Tree.FunctionArgument) {
            // Method specified with lambda: Don't bother generating a
            // Callable, just transform the expr to use as the method body.
            Tree.FunctionArgument fa = (Tree.FunctionArgument)term;
            ProducedType resultType = model.getType();
            returnNull = isAnything(resultType) && fa.getExpression().getUnboxed();
            final java.util.List<com.redhat.ceylon.compiler.typechecker.tree.Tree.Parameter> lambdaParams = fa.getParameterLists().get(0).getParameters();
            final java.util.List<com.redhat.ceylon.compiler.typechecker.tree.Tree.Parameter> defParams = def.getParameterLists().get(0).getParameters();
            List<Substitution> substitutions = List.nil();
            for (int ii = 0; ii < lambdaParams.size(); ii++) {
                substitutions = substitutions.append(naming.addVariableSubst(
                        (TypedDeclaration)lambdaParams.get(ii).getParameterModel().getModel(),
                        defParams.get(ii).getParameterModel().getName()));
            }
            bodyExpr = gen().expressionGen().transformExpression(fa.getExpression(),
                            returnNull ? BoxingStrategy.INDIFFERENT : CodegenUtil.getBoxingStrategy(model),
                            resultType);
            for (Substitution subs : substitutions) {
                subs.close();
            }
        } else if (!isLazy && typeFact().isCallableType(term.getTypeModel())) {
            returnNull = isAnything(term.getTypeModel()) && term.getUnboxed();
            Method method = methodDecl.getDeclarationModel();
            boolean lazy = specifierExpression instanceof Tree.LazySpecifierExpression;
            boolean inlined = CodegenUtil.canOptimiseMethodSpecifier(term, method);
            Invocation invocation;
            if ((lazy || inlined)
                    && term instanceof Tree.MemberOrTypeExpression
                    && ((Tree.MemberOrTypeExpression)term).getDeclaration() instanceof Functional) {
                Declaration primaryDeclaration = ((Tree.MemberOrTypeExpression)term).getDeclaration();
                ProducedReference producedReference = ((Tree.MemberOrTypeExpression)term).getTarget();
                invocation = new MethodReferenceSpecifierInvocation(
                        this,
                        (Tree.MemberOrTypeExpression)term,
                        primaryDeclaration,
                        producedReference,
                        method,
                        specifierExpression);
            } else if (!lazy && !inlined) {
                // must be a callable we stored
                String name = naming.getMethodSpecifierAttributeName(method);
                invocation = new CallableSpecifierInvocation(
                        this,
                        method,
                        naming.makeUnquotedIdent(name),
                        term,
                        term);
            } else if (isCeylonCallableSubtype(term.getTypeModel())) {
                invocation = new CallableSpecifierInvocation(
                        this,
                        method,
                        expressionGen().transformExpression(term),
                        term,
                        term);
            } else {
                throw new BugException(term, "unhandled primary: " + term == null ? "null" : term.getNodeType());
            }
            invocation.handleBoxing(true);
            bodyExpr = expressionGen().transformInvocation(invocation);
        } else {
            bodyExpr = expressionGen().transformExpression(model, term);
            // The innermost of an MPL method declared void needs to return null
            returnNull = Decl.isUnboxedVoid(model) && Decl.isMpl(model);
        }
        if (!Decl.isUnboxedVoid(model)
                || Decl.isMpl(model)
                || Strategy.useBoxedVoid(model)) {
            if (returnNull) {
                body = List.<JCStatement>of(make().Exec(bodyExpr), make().Return(makeNull()));
            } else {
                body = List.<JCStatement>of(make().Return(bodyExpr));
            }
        } else {
            body = List.<JCStatement>of(make().Exec(bodyExpr));
        }
        return body;
    }

    private boolean isVoid(Tree.Declaration def) {
        if (def instanceof Tree.AnyMethod) {
            return gen().isAnything(((Tree.AnyMethod)def).getType().getTypeModel());
        } else if (def instanceof Tree.AnyClass) {
            // Consider classes void since ctors don't require a return statement
            return true;
        }
        throw BugException.unhandledNodeCase(def);
    }
   
    private boolean isVoid(Declaration def) {
        if (def instanceof Method) {
            return gen().isAnything(((Method)def).getType());
        } else if (def instanceof Class) {
            // Consider classes void since ctors don't require a return statement
            return true;
        }
        throw BugException.unhandledDeclarationCase(def);
    }

    /**
     * Generate a body for the overload method which delegates to the
     * canonical method using a let to spubstitute defaulted parameters
     */
    private static int OL_BODY_DELEGATE_CANONICAL = 1<<0;
    /**
     * Modifies OL_BODY_DELEGATE_CANONICAL so that the method is suitable
     * for a companion class.
     */
    private static int OL_COMPANION = 1<<1;
    /**
     * Modifies OL_BODY_DELEGATE_CANONICAL so that the canonical method on the
     * interface instantce, {@code $this}, is called, rather than the
     * canonical method on {@code this}.
     */
    private static int OL_DELEGATE_INTERFACE_INSTANCE = 1<<2;
   
    /**
     * Abstraction over possible transformations for the body of an overloaded
     * declaration which supplies a defaulted argument.
     * @see DefaultedArgumentOverload
     */
    abstract class DaoBody {
       
        protected List<JCExpression> makeTypeArguments(DefaultedArgumentOverload ol) {
            if (ol.defaultParameterMethodOnSelf()
                    || ol.defaultParameterMethodOnOuter()) {
                return List.<JCExpression>nil();
            } else if (ol.defaultParameterMethodStatic()){
                return typeArguments((Functional)ol.getModel());
            } else {
                return List.<JCExpression>nil();
            }
        }

        abstract void makeBody(DefaultedArgumentOverload overloaded,
                MethodDefinitionBuilder overloadBuilder,
                ParameterList parameterList,
                Parameter currentParameter,
                java.util.List<TypeParameter> typeParameterList);

        JCExpression makeMethodNameQualifier() {
            return null;
        }
       
    }
   
    /**
     * a body-less (i.e. abstract) transformation.
     */
    private class DaoAbstract extends DaoBody {
        @Override
        public void makeBody(DefaultedArgumentOverload overloaded,
                MethodDefinitionBuilder overloadBuilder,
                ParameterList parameterList,
                Parameter currentParameter,
                java.util.List<TypeParameter> typeParameterList) {
            overloadBuilder.noBody();
        }
    };
    final DaoAbstract daoAbstract = new DaoAbstract();
    /**
     * a transformation for an overloaded
     * default parameter body which delegates to the "canonical"
     * method/constructor using a {@code let} expresssion
     * to substitute defaulted arguments
     */
    private class DaoThis extends DaoBody {
        public void makeBody(DefaultedArgumentOverload overloaded,
                MethodDefinitionBuilder overloadBuilder,
                ParameterList parameterList,
                Parameter currentParameter,
                java.util.List<TypeParameter> typeParameterList) {
            ListBuffer<JCExpression> args = ListBuffer.<JCExpression>lb();
            overloaded.appendImplicitArguments(typeParameterList, overloadBuilder, args);
           
            ListBuffer<JCStatement> vars = ListBuffer.<JCStatement>lb();
           
            boolean initedVars = false;
            boolean useDefault = false;
            for (Parameter parameterModel : parameterList.getParameters()) {
                if (currentParameter != null && Decl.equal(parameterModel, currentParameter)) {
                    useDefault = true;
                }
                if (useDefault) {
                    JCExpression defaultArgument;
                    if (Strategy.hasDefaultParameterValueMethod(parameterModel)) {
                        if (!initedVars) {
                            // Only call init vars if we actually invoke a defaulted param method
                            overloaded.initVars(currentParameter, vars);
                        }
                        initedVars = true;
                        JCExpression defaultValueMethodName = naming.makeDefaultedParamMethod(overloaded.makeDefaultArgumentValueMethodQualifier(), parameterModel);
                        at(null);
                        defaultArgument = make().Apply(makeTypeArguments(overloaded),
                                defaultValueMethodName,
                                ListBuffer.<JCExpression>lb().appendList(args).toList());
                    } else if (Strategy.hasEmptyDefaultArgument(parameterModel)) {
                        defaultArgument = makeEmptyAsSequential(true);
                    } else {
                        defaultArgument = makeErroneous(null, "compiler bug: parameter " + parameterModel.getName() + " has an unsupported default value");
                    }
                    Naming.SyntheticName varName = naming.temp(parameterModel.getName());
                    ProducedType paramType = overloaded.parameterType(parameterModel);
                    vars.append(makeVar(varName,
                            makeJavaType(paramType, CodegenUtil.isUnBoxed(parameterModel.getModel()) ? 0 : JT_NO_PRIMITIVES),
                            defaultArgument));
                    args.add(varName.makeIdent());
                } else {
                    args.add(naming.makeName(parameterModel.getModel(), Naming.NA_MEMBER | Naming.NA_ALIASED));
                }
            }
            makeBody(overloaded, overloadBuilder, args, vars);
        }
       
        protected final void makeBody(DefaultedArgumentOverload overloaded, MethodDefinitionBuilder overloadBuilder, ListBuffer<JCExpression> args, ListBuffer<JCStatement> vars) {
            JCExpression invocation = overloaded.makeInvocation(args);
            Declaration model = overloaded.getModel();// TODO Yuk
            if (!isVoid(model)
                    // MPL overloads always return a Callable
                    || (model instanceof Functional && Decl.isMpl((Functional) model))
                    || (model instanceof Method && !(Decl.isUnboxedVoid(model)))
                    || (model instanceof Method && Strategy.useBoxedVoid((Method)model))
                    || Strategy.generateInstantiator(model) && overloaded instanceof DefaultedArgumentInstantiator) {
                if (!vars.isEmpty()) {
                    invocation = make().LetExpr(vars.toList(), invocation);
                }
                overloadBuilder.body(make().Return(invocation));
            } else {
                vars.append(make().Exec(invocation));
                invocation = make().LetExpr(vars.toList(), makeNull());
                overloadBuilder.body(make().Exec(invocation));
            }
        }
    }
    final DaoThis daoThis = new DaoThis();
   
    /**
     * specialises {@link DaoThis} for transforming declarations for companion classes
     */
    private class DaoCompanion extends DaoThis {
        @Override
        protected final List<JCExpression> makeTypeArguments(DefaultedArgumentOverload ol) {
            return List.<JCExpression>nil();
        }
    }
    final DaoCompanion daoCompanion = new DaoCompanion();
   
    /**
     * a transformation for an overloaded
     * default parameter method body which delegates
     * to the super class. This is used when we need to refine the return
     * type of a DPM.
     */
    private class DaoSuper extends DaoBody {

        JCExpression makeMethodNameQualifier() {
            return naming.makeSuper();
        }
       
        @Override
        void makeBody(
                DefaultedArgumentOverload overloaded,
                MethodDefinitionBuilder overloadBuilder,
                ParameterList parameterList,
                Parameter currentParameter,
                java.util.List<TypeParameter> typeParameterList) {
           
            ListBuffer<JCExpression> args = ListBuffer.<JCExpression>lb();
            for (Parameter parameter : parameterList.getParameters()) {
                if (currentParameter != null && Decl.equal(parameter, currentParameter)) {
                    break;
                }
                args.add(naming.makeUnquotedIdent(parameter.getName()));
            }
            JCExpression superCall = overloaded.makeInvocation(args);
            /*JCMethodInvocation superCall = make().Apply(null,
                    naming.makeQualIdent(naming.makeSuper(), ((Method)overloaded.getModel()).getName()),
                    args.toList());*/
            JCExpression refinedType = makeJavaType(((Method)overloaded.getModel()).getType(), JT_NO_PRIMITIVES);
            overloadBuilder.body(make().Return(make().TypeCast(refinedType, superCall)));
        }
    }
    final DaoSuper daoSuper = new DaoSuper();
   
    java.util.List<TypeParameter> typeParameterListModel(Tree.TypeParameterList typeParameterList) {
        java.util.List<TypeParameter> tpList = null;
        if (typeParameterList != null) {
            tpList = new ArrayList<TypeParameter>();
            for (TypeParameterDeclaration tpd : typeParameterList.getTypeParameterDeclarations()) {
                tpList.add(tpd.getDeclarationModel());
            }
        }
        return tpList;
    }
   
    /**
     * A base class for transformations used for Ceylon declarations
     * which have defaulted parameters. We generate an overloaded
     * method/constructor whose implementation which supplies the default
     * argument and delegates to the "canonical" method.
     *
     * Subclasses specialise for the different kinds of declaration, and
     * a separate set of classes handle the various transformations for the
     * body of an overloaded declaration (see {@link DaoBody})
     */
    abstract class DefaultedArgumentOverload {
        protected final DaoBody daoBody;
       
        protected DefaultedArgumentOverload(DaoBody daoBody){
            this.daoBody = daoBody;
        }
       
        protected abstract long getModifiers();

        protected abstract JCExpression makeMethodName();

        protected abstract void resultType(MethodDefinitionBuilder overloadBuilder);

        protected abstract void typeParameters(MethodDefinitionBuilder overloadBuilder);

        protected void parameters(MethodDefinitionBuilder overloadBuilder, ParameterList parameterList, Parameter currentParameter) {
            for (Parameter parameter : parameterList.getParameters()) {
                if (currentParameter != null && Decl.equal(parameter, currentParameter)) {
                    break;
                }
                overloadBuilder.parameter(parameter, null, 0, false);
            }
        }
       
        protected final void appendImplicitParameters(java.util.List<TypeParameter> typeParameterList,
                MethodDefinitionBuilder overloadBuilder) {
            if(typeParameterList != null){
                overloadBuilder.reifiedTypeParameters(typeParameterList);
            }
        }
       
        protected abstract void appendImplicitArguments(java.util.List<TypeParameter> typeParameterList,
                MethodDefinitionBuilder overloadBuilder, ListBuffer<JCExpression> args);
       
        protected ProducedType parameterType(Parameter parameterModel) {
            ProducedType paramType = null;
            if (parameterModel.getModel() instanceof Method) {
                paramType = typeFact().getCallableType(parameterModel.getType());
            } else {
                paramType = parameterModel.getType();
            }
            return paramType;
        }

        protected abstract void initVars(Parameter currentParameter, ListBuffer<JCStatement> vars);

        protected final boolean defaultParameterMethodOnSelf() {
            return Strategy.defaultParameterMethodOnSelf(getModel());
        }

        protected final boolean defaultParameterMethodOnOuter() {
            return Strategy.defaultParameterMethodOnOuter(getModel());
        }

        protected final boolean defaultParameterMethodStatic() {
            return Strategy.defaultParameterMethodStatic(getModel());
        }

        protected abstract Declaration getModel();

        protected JCExpression makeInvocation(ListBuffer<JCExpression> args) {
            final JCExpression methName = makeMethodName();
            return make().Apply(List.<JCExpression>nil(),
                    methName, args.toList());           
        }

        /** Returns the qualiifier to use when invoking the default parameter value method */
        protected abstract JCIdent makeDefaultArgumentValueMethodQualifier();
       
       
        /**
         * Generates an overloaded method or constructor.
         */
        public MethodDefinitionBuilder makeOverload (
                MethodDefinitionBuilder overloadBuilder,
                ParameterList parameterList,
                Parameter currentParameter,
                java.util.List<TypeParameter> typeParameterList) {

            // Make the declaration
            // need annotations for BC, but the method isn't really there
            overloadBuilder.ignoreModelAnnotations();
            overloadBuilder.modifiers(getModifiers());
            resultType(overloadBuilder);
            typeParameters(overloadBuilder);

            appendImplicitParameters(typeParameterList, overloadBuilder);
            parameters(overloadBuilder, parameterList, currentParameter);
           
            // Make the body
            // TODO MPL
            // TODO Type args on method call
           
            daoBody.makeBody(this, overloadBuilder,
                    parameterList,
                    currentParameter,
                    typeParameterList);
           
            return overloadBuilder;
        }
    }
   
    /**
     * A transformation for generating overloaded <em>methods</em> for
     * defaulted arguments.
     */
    class DefaultedArgumentMethod extends DefaultedArgumentOverload {
        private final Method method;

        DefaultedArgumentMethod(DaoBody daoBody, Method method) {
            super(daoBody);
            this.method = method;
        }

        @Override
        protected Method getModel() {
            return method;
        }
       
        @Override
        protected long getModifiers() {
            long mods = transformMethodDeclFlags(method);
            if (daoBody instanceof DaoAbstract == false) {
                mods &= ~ABSTRACT;
            }
            if (daoBody instanceof DaoCompanion) {
                mods |= FINAL;
            }
            return mods;
        }

        @Override
        protected final JCExpression makeMethodName() {
            int flags = Naming.NA_MEMBER;
            if (Decl.withinClassOrInterface(method)) {
                flags |= Naming.NA_CANONICAL_METHOD;
            }
            return naming.makeQualifiedName(daoBody.makeMethodNameQualifier(), method, flags);
        }

        @Override
        protected void resultType(MethodDefinitionBuilder overloadBuilder) {
            overloadBuilder.resultType(method, 0);
        }

        @Override
        protected void typeParameters(MethodDefinitionBuilder overloadBuilder) {
            copyTypeParameters(method, overloadBuilder);
        }

        @Override
        protected void appendImplicitArguments(java.util.List<TypeParameter> typeParameterList,
                MethodDefinitionBuilder overloadBuilder, ListBuffer<JCExpression> args) {
            if(typeParameterList != null){
                // we pass the reified type parameters along
                for(TypeParameter tp : typeParameterList){
                    args.append(makeUnquotedIdent(naming.getTypeArgumentDescriptorName(tp)));
                }
            }
        }

        @Override
        protected void initVars(Parameter currentParameter, ListBuffer<JCStatement> vars) {
        }

        @Override
        protected JCIdent makeDefaultArgumentValueMethodQualifier() {
            return null;
        }
    }
   
    /**
     * A transformation for generating overloaded <em>methods</em> for
     * defaulted arguments.
     */
    class DefaultedArgumentMethodTyped extends DefaultedArgumentMethod {
        private ProducedTypedReference typedMember;

        DefaultedArgumentMethodTyped(DaoBody daoBody, ProducedTypedReference typedMember) {
            super(daoBody, (Method)typedMember.getDeclaration());
            this.typedMember = typedMember;
        }

        @Override
        protected void resultType(MethodDefinitionBuilder overloadBuilder) {
            if (!isAnything(getModel().getType())
                    || !Decl.isUnboxedVoid(getModel())
                    || Strategy.useBoxedVoid((Method)getModel())) {
                ProducedTypedReference typedRef = (ProducedTypedReference) typedMember;
                overloadBuilder.resultTypeNonWidening(typedMember.getQualifyingType(), typedRef, typedMember.getType(), 0);
            } else {
                super.resultType(overloadBuilder);
            }
        }
       
        @Override
        protected void parameters(MethodDefinitionBuilder overloadBuilder, ParameterList parameterList, Parameter currentParameter) {
            for (Parameter param : parameterList.getParameters()) {
                if (currentParameter != null && Decl.equal(param, currentParameter)) {
                    break;
                }
                ProducedType type = paramType(param);
                overloadBuilder.parameter(param, type, FINAL, 0, true);
            }
        }
       
        @Override
        protected ProducedType parameterType(Parameter parameter) {
            ProducedType paramType = paramType(parameter);
            if (parameter.getModel() instanceof Method) {
                paramType = typeFact().getCallableType(parameter.getType());
            }
            return paramType;
        }

        private ProducedType paramType(Parameter parameter) {
            final ProducedTypedReference typedParameter = typedMember.getTypedParameter(parameter);
            ProducedType paramType;
            // if the supertype method itself got erased to Object, we can't do better than this
            if (gen().willEraseToObject(parameter.getType()) && !gen().willEraseToBestBounds(parameter)) {
                paramType = typeFact().getObjectDeclaration().getType();
            } else {
                paramType = typedParameter.getType();
            }
            return paramType;
        }

        @Override
        public MethodDefinitionBuilder makeOverload(
                MethodDefinitionBuilder overloadBuilder,
                ParameterList parameterList, Parameter currentParameter,
                java.util.List<TypeParameter> typeParameterList) {
            overloadBuilder.isOverride(true);
            return super.makeOverload(overloadBuilder, parameterList, currentParameter,
                    typeParameterList);
        }
       
       
    }
   
    /**
     * A transformation for generating the canonical <em>method</em> used by the
     * defaulted argument overload methods
     */
    class CanonicalMethod extends DefaultedArgumentMethod {
        private List<JCStatement> body;
        private boolean useBody;
       
        CanonicalMethod(DaoBody daoBody, Method method) {
            super(daoBody, method);
            useBody = false;
        }
       
        CanonicalMethod(DaoBody daoBody, Method method, List<JCStatement> body) {
            super(daoBody, method);
            this.body = body;
            useBody = true;
        }
       
        @Override
        protected long getModifiers() {
            long mods = super.getModifiers();
            if (useBody) {
                mods = mods & ~PUBLIC & ~FINAL | PRIVATE;
            }
            return mods;
        }
       
        /**
         * Generates a canonical method
         */
        public MethodDefinitionBuilder makeOverload (
                MethodDefinitionBuilder canonicalBuilder,
                ParameterList parameterList,
                Parameter currentParameter,
                java.util.List<TypeParameter> typeParameterList) {

            if (!useBody) {
                daoBody.makeBody(this, canonicalBuilder,
                        parameterList,
                        currentParameter,
                        typeParameterList);
            } else {
                // Make the declaration
                // need annotations for BC, but the method isn't really there
                canonicalBuilder.ignoreModelAnnotations();
                canonicalBuilder.modifiers(getModifiers());
                resultType(canonicalBuilder);
                typeParameters(canonicalBuilder);

                appendImplicitParameters(typeParameterList, canonicalBuilder);
                parameters(canonicalBuilder, parameterList, currentParameter);
               
                if (body != null) {
                    // Construct the outermost method using the body we've built so far
                    canonicalBuilder.body(body);
                } else {
                    canonicalBuilder.noBody();
                }
            }
           
            return canonicalBuilder;
        }

    }
   
    /**
     * A base class for transformations that generate overloaded declarations for
     * defaulted arguments.
     */
    abstract class DefaultedArgumentClass extends DefaultedArgumentOverload {
       
        protected final Class klass;
       
        protected Naming.SyntheticName companionInstanceName = null;

        DefaultedArgumentClass(DaoBody daoBody, Class klass) {
            super(daoBody);
            this.klass = klass;
        }
       
        @Override
        protected final Class getModel() {
            return klass;
        }
       
        @Override
        protected void initVars(Parameter currentParameter, ListBuffer<JCStatement> vars) {
            if (!Strategy.defaultParameterMethodStatic(klass)
                    && !Strategy.defaultParameterMethodOnOuter(klass)
                    && currentParameter != null) {
                companionInstanceName = naming.temp("impl");
                vars.append(makeVar(companionInstanceName,
                        makeJavaType(klass.getType(), AbstractTransformer.JT_COMPANION),
                        make().NewClass(null,
                                null,
                                makeJavaType(klass.getType(), AbstractTransformer.JT_CLASS_NEW | AbstractTransformer.JT_COMPANION),
                                List.<JCExpression>nil(), null)));
            }
        }
       
        @Override
        protected JCIdent makeDefaultArgumentValueMethodQualifier() {
            if (defaultParameterMethodOnSelf()
                    || defaultParameterMethodOnOuter()
                    || daoBody instanceof DaoCompanion) {
                return null;
            } else if (defaultParameterMethodStatic()){
                return null;
            } else {
                return companionInstanceName.makeIdent();
            }
        }
    }
   
    /**
     * A transformation for generating overloaded <em>constructors</em> for
     * defaulted arguments.
     */
    class DefaultedArgumentConstructor extends DefaultedArgumentClass {

        DefaultedArgumentConstructor(DaoBody daoBody, Class klass) {
            super(daoBody, klass);
        }

        @Override
        protected long getModifiers() {
            return transformClassDeclFlags(klass) & (PUBLIC | PRIVATE | PROTECTED);
        }

        @Override
        protected JCExpression makeMethodName() {
            return naming.makeQualifiedThis(daoBody.makeMethodNameQualifier());
        }

        @Override
        protected void resultType(MethodDefinitionBuilder overloadBuilder) {
            // Constructor has no result type
        }

        @Override
        protected void typeParameters(MethodDefinitionBuilder overloadBuilder) {
            // Constructor has type parameters
        }
       
        @Override
        protected void appendImplicitArguments(java.util.List<TypeParameter> typeParameterList,
                MethodDefinitionBuilder overloadBuilder, ListBuffer<JCExpression> args) {
            if(typeParameterList != null){
                // we pass the reified type parameters along
                for(TypeParameter tp : typeParameterList){
                    args.append(makeUnquotedIdent(naming.getTypeArgumentDescriptorName(tp)));
                }
            }
        }
    }
   
    /**
     * A transformation for generating overloaded <em>instantiator methods</em> for
     * defaulted arguments.
     */
    class DefaultedArgumentInstantiator extends DefaultedArgumentClass {

        DefaultedArgumentInstantiator(DaoBody daoBody, Class klass) {
            super(daoBody, klass);
        }

        @Override
        protected long getModifiers() {
            // remove the FINAL bit in case it gets set, because that is valid for a class decl, but
            // not for a method if in an interface
            long modifiers = transformClassDeclFlags(klass) & ~FINAL;
            // alias classes cannot be abstract since they're placeholders, but it's possible to have formal class aliases
            // and the instantiator method needs the abstract bit
            if(klass.isFormal() && klass.isAlias())
                modifiers |= ABSTRACT;
            return modifiers;
           
        }

        @Override
        protected JCExpression makeMethodName() {
            return naming.makeInstantiatorMethodName(daoBody.makeMethodNameQualifier(), klass);
        }

        @Override
        protected void resultType(MethodDefinitionBuilder overloadBuilder) {
            /* Not actually part of the return type */
            overloadBuilder.ignoreModelAnnotations();
            if (!klass.isAlias()
                    && Strategy.generateInstantiator(klass.getExtendedTypeDeclaration())
                    && klass.isActual()
                    && klass.getExtendedTypeDeclaration().getContainer() instanceof Class) {
                overloadBuilder.isOverride(true);
            }
            /**/
           
            JCExpression resultType;
            ProducedType type = klass.isAlias() ? klass.getExtendedType() : klass.getType();
            if (Strategy.isInstantiatorUntyped(klass)) {
                // We can't expose a local type name to a place it's not visible
                resultType = make().Type(syms().objectType);
            } else {
                resultType = makeJavaType(type);
            }
            overloadBuilder.resultType(null, resultType);
        }

        @Override
        protected void typeParameters(MethodDefinitionBuilder overloadBuilder) {
            for (TypeParameter tp : typeParametersForInstantiator(klass)) {
                overloadBuilder.typeParameter(tp);
            }
        }
       
        @Override
        protected void appendImplicitArguments(java.util.List<TypeParameter> typeParameterList,
                MethodDefinitionBuilder overloadBuilder, ListBuffer<JCExpression> args) {
            ProducedType type = klass.isAlias() ? klass.getExtendedType() : klass.getType();
            type = type.resolveAliases();
            // fetch the type parameters from the klass we're instantiating itself if any
            for(ProducedType pt : type.getTypeArgumentList()){
                args.append(makeReifiedTypeArgument(pt));
            }
        }

        @Override
        protected JCExpression makeInvocation(ListBuffer<JCExpression> args) {
            ProducedType type = klass.isAlias() ? klass.getExtendedType() : klass.getType();
            return make().NewClass(null,
                    null,
                    makeJavaType(type, JT_CLASS_NEW | JT_NON_QUALIFIED),
                    args.toList(),
                    null);
        }
    }

    /**
     * When generating an instantiator method if the inner class has a type
     * parameter with the same name as a type parameter of an outer type, then the
     * instantiator method shouldn't declare its own type parameter of that
     * name -- it should use the captured one. This method filters out the
     * type parameters of the inner class which are the same as type parameters
     * of the outer class so that they can be captured.
     */
    private java.util.List<TypeParameter> typeParametersForInstantiator(final Class model) {
        if (!Strategy.generateInstantiator(model)) {
            throw new BugException();
        }
        java.util.List<TypeParameter> filtered = new ArrayList<TypeParameter>();
        java.util.List<TypeParameter> tps = model.getTypeParameters();
        if (tps != null) {
            for (TypeParameter tp : tps) {
                boolean omit = false;
                Scope s = model.getContainer();
                while (!(s instanceof Package)) {
                    if (s instanceof Generic) {
                        for (TypeParameter outerTp : ((Generic)s).getTypeParameters()) {
                            if (tp.getName().equals(outerTp.getName())) {
                                omit = true;
                            }
                        }
                    }
                    s = s.getContainer();
                }
                if (!omit) {
                    filtered.add(tp);
                }
            }
        }
        return filtered;
    }

    private java.util.List<TypeParameter> typeParametersOfAllContainers(final ClassOrInterface model, boolean includeModelTypeParameters) {
        java.util.List<java.util.List<TypeParameter>> r = new ArrayList<java.util.List<TypeParameter>>(1);
        Scope s = model.getContainer();
        while (!(s instanceof Package)) {
            if (s instanceof Generic) {
                r.add(0, ((Generic)s).getTypeParameters());
            }
            s = s.getContainer();
        }
        Set<String> names = new HashSet<String>();
        for(TypeParameter tp : model.getTypeParameters()){
            names.add(tp.getName());
        }
        java.util.List<TypeParameter> result = new ArrayList<TypeParameter>(1);
        for (java.util.List<TypeParameter> tps : r) {
            for(TypeParameter tp : tps){
                if(names.add(tp.getName())){
                    result.add(tp);
                }
            }
        }
        if(includeModelTypeParameters)
            result.addAll(model.getTypeParameters());
        return result;
    }
   
   
    /**
     * Creates a (possibly abstract) method for retrieving the value for a
     * defaulted parameter
     * @param typeParameterList
     */
    MethodDefinitionBuilder makeParamDefaultValueMethod(boolean noBody, Declaration container,
            Tree.ParameterList params, Tree.Parameter currentParam, TypeParameterList typeParameterList) {
        at(currentParam);
        Parameter parameter = currentParam.getParameterModel();
        if (!Strategy.hasDefaultParameterValueMethod(parameter)) {
            throw new BugException();
        }
        MethodDefinitionBuilder methodBuilder = MethodDefinitionBuilder.systemMethod(this, Naming.getDefaultedParamMethodName(container, parameter));
        methodBuilder.ignoreModelAnnotations();
        if (container != null && Decl.isAnnotationConstructor(container)) {
            AnnotationInvocation ac = (AnnotationInvocation)((Method)container).getAnnotationConstructor();
            for (AnnotationConstructorParameter acp : ac.getConstructorParameters()) {
                if (acp.getParameter().equals(parameter)
                        && acp.getDefaultArgument() != null) {
                    methodBuilder.userAnnotations(acp.getDefaultArgument().makeDpmAnnotations(expressionGen()));
                }
            }
        }
        int modifiers = 0;
        if (noBody) {
            modifiers |= PUBLIC | ABSTRACT;
        } else if (container == null
                || !(container instanceof Class
                        && Strategy.defaultParameterMethodStatic(container))) {
            // initializers can override parameter defaults
            modifiers |= FINAL;
        }
        if (container != null && container.isShared()) {
            modifiers |= PUBLIC;
        } else if (container == null || (!container.isToplevel()
                && !noBody)){
            modifiers |= PRIVATE;
        }
        if (Strategy.defaultParameterMethodStatic(container)) {
            // static default parameter methods should be consistently public so that if non-shared class Top and
            // shared class Bottom which extends Top both have the same default param name, we don't get an error
            // if the Bottom class tries to "hide" a static public method with a private one
            modifiers |= STATIC | PUBLIC;
        }
        methodBuilder.modifiers(modifiers);
       
        if (container instanceof Functional) {
            copyTypeParameters((Functional)container, methodBuilder);
        }
       
        // make sure reified type parameters are accepted
        if(typeParameterList != null)
            methodBuilder.reifiedTypeParameters(typeParameterListModel(typeParameterList));
       
        // Add any of the preceding parameters as parameters to the method
        for (Tree.Parameter p : params.getParameters()) {
            if (p.equals(currentParam)) {
                break;
            }
            at(p);
            methodBuilder.parameter(p.getParameterModel(), null, 0, container instanceof Class);
        }

        // The method's return type is the same as the parameter's type
        methodBuilder.resultType(parameter.getModel(), parameter.getType(), 0);

        // The implementation of the method
        if (noBody) {
            methodBuilder.noBody();
        } else {
            HasErrorException error = errors().getFirstExpressionErrorAndMarkBrokenness(Decl.getDefaultArgument(currentParam).getExpression());
            if (error != null) {
                methodBuilder.body(this.makeThrowUnresolvedCompilationError(error));
            } else {
                JCExpression expr = expressionGen().transform(currentParam);
                JCBlock body = at(currentParam).Block(0, List.<JCStatement> of(at(currentParam).Return(expr)));
                methodBuilder.block(body);
            }
        }

        return methodBuilder;
    }

    public List<JCTree> transformObjectDefinition(Tree.ObjectDefinition def, ClassDefinitionBuilder containingClassBuilder) {
        return transformObject(def, def.getSatisfiedTypes(), def.getDeclarationModel(),
                def.getAnonymousClass(), containingClassBuilder, Decl.isLocalNotInitializer(def));
    }
   
    public List<JCTree> transformObjectArgument(Tree.ObjectArgument def) {
        return transformObject(def, def.getSatisfiedTypes(), def.getDeclarationModel(),
                def.getAnonymousClass(), null, false);
    }
   
    private List<JCTree> transformObject(Tree.StatementOrArgument def,
            Tree.SatisfiedTypes satisfiesTypes,
            Value model,
            Class klass,
            ClassDefinitionBuilder containingClassBuilder,
            boolean makeLocalInstance) {
        naming.clearSubstitutions(model);
       
        String name = model.getName();
        ClassDefinitionBuilder objectClassBuilder = ClassDefinitionBuilder.object(
                this, name, Decl.isLocal(model)).forDefinition(klass);
       
        CeylonVisitor visitor = gen().visitor;
        final ListBuffer<JCTree> prevDefs = visitor.defs;
        final boolean prevInInitializer = visitor.inInitializer;
        final ClassDefinitionBuilder prevClassBuilder = visitor.classBuilder;
        List<JCStatement> childDefs;
        try {
            visitor.defs = new ListBuffer<JCTree>();
            visitor.inInitializer = true;
            visitor.classBuilder = objectClassBuilder;
           
            def.visitChildren(visitor);
            childDefs = (List<JCStatement>)visitor.getResult().toList();
        } finally {
            visitor.classBuilder = prevClassBuilder;
            visitor.inInitializer = prevInInitializer;
            visitor.defs = prevDefs;
        }
        addMissingUnrefinedMembers(def, klass, objectClassBuilder);
        satisfaction(satisfiesTypes, klass, objectClassBuilder);
        serialization(klass, objectClassBuilder);
        TypeDeclaration decl = model.getType().getDeclaration();

        if (Decl.isToplevel(model)
                && def instanceof Tree.ObjectDefinition) {
            // generate a field and getter
            AttributeDefinitionBuilder builder = AttributeDefinitionBuilder
                    // TODO attr build take a JCExpression className
                    .wrapped(this, null, objectClassBuilder, model.getName(), model, true)
                    .userAnnotations(makeAtIgnore())
                    .userAnnotationsSetter(makeAtIgnore())
                    .immutable()
                    .initialValue(makeNewClass(naming.makeName(model, Naming.NA_FQ | Naming.NA_WRAPPER)))
                    .is(PUBLIC, Decl.isShared(decl))
                    .is(STATIC, true);
            if (def instanceof Tree.ObjectDefinition) {
                builder.userAnnotations(expressionGen().transform(((Tree.ObjectDefinition) def).getAnnotationList()));
            }           
            objectClassBuilder.defs(builder.build());
        }

        // Make sure top types satisfy reified type
        addReifiedTypeInterface(objectClassBuilder, klass);
        if(supportsReifiedAlias(klass))
            objectClassBuilder.reifiedAlias(model.getType());
       
        // make sure we set the container in case we move it out
        addAtContainer(objectClassBuilder, klass);

        List<JCTree> result = objectClassBuilder
            .annotations(makeAtObject())
            .modelAnnotations(model.getAnnotations())
            .modifiers(transformObjectDeclFlags(model))
            .constructorModifiers(PRIVATE)
            .satisfies(decl.getSatisfiedTypes())
            .init(childDefs)
            .addGetTypeMethod(model.getType())
            .build();
       
        if (makeLocalInstance) {
            if(model.isSelfCaptured()){
                // if it's captured we need to box it and define the var before the class, so it can access it
                JCNewClass newInstance = makeNewClass(objectClassBuilder.getClassName(), false, null);
                JCFieldAccess setter = naming.makeSelect(Naming.getLocalValueName(model), Naming.getSetterName(model));
                JCStatement assign = make().Exec(make().Assign(setter, newInstance));
                result = result.prepend(assign);

                JCVariableDecl localDecl = makeVariableBoxDecl(null, model);
                result = result.prepend(localDecl);
            }else{
                // not captured, we can define the var after the class
                JCVariableDecl localDecl = makeLocalIdentityInstance(name, objectClassBuilder.getClassName(), false);
                result = result.append(localDecl);
            }
           
        } else if (Decl.withinClassOrInterface(model)) {
            boolean visible = Decl.isCaptured(model);
            int modifiers = FINAL | ((visible) ? PRIVATE : 0);
            JCExpression type = makeJavaType(klass.getType());
            JCExpression initialValue = makeNewClass(makeJavaType(klass.getType()), null);
            containingClassBuilder.field(modifiers, name, type, initialValue, !visible);
           
            if (visible) {
                AttributeDefinitionBuilder getter = AttributeDefinitionBuilder
                .getter(this, name, model)
                .modifiers(transformAttributeGetSetDeclFlags(model, false));
                if (def instanceof Tree.ObjectDefinition) {
                    getter.userAnnotations(expressionGen().transform(((Tree.ObjectDefinition)def).getAnnotationList()));
                }
               
                result = result.appendList(getter.build());
            }
        }
       
        return result;
    }
   
    /**
     * Makes a {@code main()} method which calls the given top-level method
     * @param def
     */
    private MethodDefinitionBuilder makeMainForClass(ClassOrInterface model) {
        at(null);
        if(model.isAlias())
            model = model.getExtendedTypeDeclaration();
        JCExpression nameId = makeJavaType(model.getType(), JT_RAW);
        List<JCExpression> arguments = makeBottomReifiedTypeParameters(model.getTypeParameters());
        JCNewClass expr = make().NewClass(null, null, nameId, arguments, null);
        return makeMainMethod(model, expr);
    }
   
    /**
     * Makes a {@code main()} method which calls the given top-level method
     * @param method
     */
    private MethodDefinitionBuilder makeMainForFunction(Method method) {
        at(null);
        JCExpression qualifiedName = naming.makeName(method, Naming.NA_FQ | Naming.NA_WRAPPER | Naming.NA_MEMBER);
        List<JCExpression> arguments = makeBottomReifiedTypeParameters(method.getTypeParameters());
        MethodDefinitionBuilder mainMethod = makeMainMethod(method, make().Apply(null, qualifiedName, arguments));
        return mainMethod;
    }
   
    private List<JCExpression> makeBottomReifiedTypeParameters(
            java.util.List<TypeParameter> typeParameters) {
        List<JCExpression> arguments = List.nil();
        for(int i=typeParameters.size()-1;i>=0;i--){
            arguments = arguments.prepend(gen().makeNothingTypeDescriptor());
        }
        return arguments;
    }

    /**
     * Makes a {@code main()} method which calls the given callee
     * (a no-args method or class)
     * @param decl
     * @param callee
     */
    private MethodDefinitionBuilder makeMainMethod(Declaration decl, JCExpression callee) {
        // Add a main() method
        MethodDefinitionBuilder methbuilder = MethodDefinitionBuilder
                .main(this)
                .ignoreModelAnnotations();
        // Add call to process.setupArguments
        JCExpression argsId = makeUnquotedIdent("args");
        JCMethodInvocation processExpr = make().Apply(null, naming.makeLanguageValue("process"), List.<JCTree.JCExpression>nil());
        methbuilder.body(make().Exec(make().Apply(null, makeSelect(processExpr, "setupArguments"), List.<JCTree.JCExpression>of(argsId))));
        // Add call to toplevel method
        methbuilder.body(make().Exec(callee));
        return methbuilder;
    }
   
    void copyTypeParameters(Functional def, MethodDefinitionBuilder methodBuilder) {
        if (def.getTypeParameters() != null) {
            for (TypeParameter t : def.getTypeParameters()) {
                methodBuilder.typeParameter(t);
            }
        }
    }

    public List<JCTree> transform(final Tree.TypeAliasDeclaration def) {
        final TypeAlias model = def.getDeclarationModel();
       
        // we only create types for aliases so they can be imported with the model loader
        // and since we can't import local declarations let's just not create those types
        // in that case
        if(Decl.isAncestorLocal(def))
            return List.nil();
       
        naming.clearSubstitutions(model);
        String ceylonClassName = def.getIdentifier().getText();
        final String javaClassName = Naming.quoteClassName(def.getIdentifier().getText());

        ClassDefinitionBuilder classBuilder = ClassDefinitionBuilder
                .klass(this, javaClassName, ceylonClassName, Decl.isLocal(model));

        // class alias
        classBuilder.constructorModifiers(PRIVATE);
        classBuilder.annotations(makeAtTypeAlias(model.getExtendedType()));
        classBuilder.annotations(expressionGen().transform(def.getAnnotationList()));
        classBuilder.isAlias(true);

        // make sure we set the container in case we move it out
        addAtContainer(classBuilder, model);

        visitClassOrInterfaceDefinition(def, classBuilder);

        return classBuilder
            .modelAnnotations(model.getAnnotations())
            .modifiers(transformTypeAliasDeclFlags(model))
            .satisfies(model.getSatisfiedTypes())
            .build();
    }
}
TOP

Related Classes of com.redhat.ceylon.compiler.java.codegen.ClassTransformer$SatisfactionVisitor

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.