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

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

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


import static com.redhat.ceylon.compiler.typechecker.analyzer.DeclarationVisitor.setVisibleScope;
import static com.redhat.ceylon.compiler.typechecker.analyzer.ExpressionVisitor.getRefinedMember;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.checkAssignable;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.checkAssignableToOneOf;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.checkIsExactly;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.checkIsExactlyForInterop;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.checkIsExactlyOneOf;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.declaredInPackage;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.getTypeErrorNode;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.getTypedDeclaration;
import static com.redhat.ceylon.compiler.typechecker.analyzer.Util.message;
import static com.redhat.ceylon.compiler.typechecker.model.Util.getInterveningRefinements;
import static com.redhat.ceylon.compiler.typechecker.model.Util.getRealScope;
import static com.redhat.ceylon.compiler.typechecker.model.Util.getSignature;
import static com.redhat.ceylon.compiler.typechecker.model.Util.isOverloadedVersion;
import static com.redhat.ceylon.compiler.typechecker.tree.Util.name;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonMap;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import com.redhat.ceylon.compiler.typechecker.model.Annotation;
import com.redhat.ceylon.compiler.typechecker.model.Class;
import com.redhat.ceylon.compiler.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.Functional;
import com.redhat.ceylon.compiler.typechecker.model.Generic;
import com.redhat.ceylon.compiler.typechecker.model.LazyProducedType;
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.Scope;
import com.redhat.ceylon.compiler.typechecker.model.Setter;
import com.redhat.ceylon.compiler.typechecker.model.Specification;
import com.redhat.ceylon.compiler.typechecker.model.TypeAlias;
import com.redhat.ceylon.compiler.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.TypeParameter;
import com.redhat.ceylon.compiler.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.Value;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;

/**
* Validates some simple rules relating to refinement.
*
* @see TypeHierarchyVisitor for the fancy stuff!
*
* @author Gavin King
*
*/
public class RefinementVisitor extends Visitor {
       
    @Override
    public void visit(Tree.AnyMethod that) {
        super.visit(that);
        inheritDefaultedArguments(that.getDeclarationModel());
    }

    @Override
    public void visit(Tree.AnyClass that) {
        super.visit(that);
        inheritDefaultedArguments(that.getDeclarationModel());
    }

    private void inheritDefaultedArguments(Declaration d) {
        Declaration rd = d.getRefinedDeclaration();
        if (rd!=d && rd!=null) {
            List<ParameterList> tdpls = ((Functional) d).getParameterLists();
            List<ParameterList> rdpls = ((Functional) rd).getParameterLists();
            if (!tdpls.isEmpty() && !rdpls.isEmpty()) {
                List<Parameter> tdps = tdpls.get(0).getParameters();
                List<Parameter> rdps = rdpls.get(0).getParameters();
                for (int i=0; i<tdps.size()&&i<rdps.size(); i++) {
                    Parameter tdp = tdps.get(i);
                    Parameter rdp = rdps.get(i);
                    if (tdp!=null && rdp!=null) {
                        tdp.setDefaulted(rdp.isDefaulted());
                    }
                }
            }
        }
    }

    @Override public void visit(Tree.Declaration that) {
        super.visit(that);
        Declaration dec = that.getDeclarationModel();
        if (dec!=null) {
            boolean toplevel =
                dec.getContainer() instanceof Package;
            boolean member =
                dec.isClassOrInterfaceMember() &&
                    dec.isShared() &&
                    !(dec instanceof TypeParameter); //TODO: what about nested interfaces and abstract classes?!
           
            if (!toplevel && !member && dec.isShared()) {
                that.addError("shared declaration is not a member of a class, interface, or package", 1200);
            }
           
            boolean mayBeShared =
                    dec instanceof MethodOrValue ||
                    dec instanceof ClassOrInterface ||
                    dec instanceof TypeAlias;
            if (!mayBeShared && dec.isShared()) {
                that.addError("shared declaration is not a function, value, class, interface, or alias", 1200);
            }
           
            boolean mayBeRefined =
                    dec instanceof Value ||
                    dec instanceof Method ||
                    dec instanceof Class;
            if (!mayBeRefined) {
                checkNonrefinableDeclaration(that, dec);
            }
           
            if (!member) {
                checkNonMember(that, dec, mayBeShared);
            }
           
            /*if (!dec.isShared()) {
                checkUnshared(that, dec);
            }*/
           
            if (member) {
                checkMember(that, dec);
            }
            else if (isOverloadedVersion(dec)) {
                that.addError("name is not unique in scope: " + dec.getName());
            }
           
        }
       
    }

    private void checkMember(Tree.Declaration that, Declaration dec) {
        if (dec instanceof Setter) {
            Value getter = ((Setter) dec).getGetter();
            dec.setRefinedDeclaration(getter.getRefinedDeclaration());
            return;
        }
        ClassOrInterface type = (ClassOrInterface) dec.getContainer();
        if (dec.isFormal() && type instanceof Class) {
            Class c = (Class) type;
            if (!c.isAbstract() && !c.isFormal()) {
                that.addError("formal member belongs to non-abstract, non-formal class", 1100);
            }
        }
        if (!dec.isFormal() && type.isDynamic()) {
            that.addError("non-formal member belongs to dynamic interface");
        }
        List<ProducedType> signature = getSignature(dec);
        Declaration root =
                type.getRefinedMember(dec.getName(), signature, false);
        boolean legallyOverloaded = !isOverloadedVersion(dec);
        if (root == null || root.equals(dec)) {
            dec.setRefinedDeclaration(dec);
            if (dec.isActual()) {
                that.addError("actual member does not refine any inherited member: " +
                        dec.getName(), 1300);
            }
            else if (!legallyOverloaded) {
                if (dec.isActual()) {
                    that.addError("overloaded member does not refine an inherited overloaded member: " +
                            dec.getName());
                }
                else {
                    that.addError("duplicate or overloaded member name: " + dec.getName());
                }
            }
        }
        else {
            dec.setRefinedDeclaration(root);
            if (root.isPackageVisibility() &&
                    !declaredInPackage(root, that.getUnit())) {
                that.addError("refined declaration is not visible: " +
                        message(root));
            }
            boolean found = false;
            TypeDeclaration rootType = (TypeDeclaration) root.getContainer();
            for (Declaration refined:
                    getInterveningRefinements(dec.getName(),
                            signature, root, type, rootType)) {
                if (isOverloadedVersion(refined)) {
                    //if this member is overloaded, the
                    //inherited member it refines must
                    //also be overloaded
                    legallyOverloaded = true;
                }
                found = true;
                if (dec instanceof Method) {
                    if (!(refined instanceof Method)) {
                        that.addError("refined declaration is not a method: " +
                                message(refined));
                    }
                }
                else if (dec instanceof Class) {
                    if (!(refined instanceof Class)) {
                        that.addError("refined declaration is not a class: " +
                                message(refined));
                    }
                }
                else if (dec instanceof TypedDeclaration) {
                    if (refined instanceof Class ||
                        refined instanceof Method) {
                        that.addError("refined declaration is not an attribute: " +
                                message(refined));
                    }
                    else if (refined instanceof TypedDeclaration) {
                        if ( ((TypedDeclaration) refined).isVariable() &&
                                !((TypedDeclaration) dec).isVariable()) {
                            if (dec instanceof Value) {
                                that.addError("non-variable attribute refines a variable attribute: " +
                                        message(refined), 804);
                            }
                            else {
                                that.addError("non-variable attribute refines a variable attribute: " +
                                        message(refined));
                            }
                        }
                    }
                }
                if (!dec.isActual()) {
                    that.addError("non-actual member refines an inherited member: " +
                            message(refined), 600);
                }
                if (!refined.isDefault() && !refined.isFormal()) {
                    that.addError("member refines a non-default, non-formal member: " +
                            message(refined), 500);
                }
                if (!type.isInconsistentType()) {
                    checkRefinedTypeAndParameterTypes(that, dec, type, refined);
                }
            }
            if (!found) {
                that.addError("actual member does not exactly refine any overloaded inherited member");
            }
            else if (!legallyOverloaded) {
                that.addError("overloaded member does not refine an inherited overloaded member");
            }
        }
    }

    /*private boolean refinesOverloaded(Declaration dec,
        Declaration refined, ProducedType st) {
        Functional fun1 = (Functional) dec;
        Functional fun2 = (Functional) refined;
        if (fun1.getParameterLists().size()!=1 ||
            fun2.getParameterLists().size()!=1) {
            return false;
        }
        List<Parameter> pl1 = fun1.getParameterLists()
            .get(0).getParameters();
        List<Parameter> pl2 = fun2.getParameterLists()
            .get(0).getParameters();
        if (pl1.size()!=pl2.size()) {
            return false;
        }
        for (int i=0; i<pl1.size(); i++) {
            Parameter p1 = pl1.get(i);
            Parameter p2 = pl2.get(i);
            if (p1==null || p2==null ||
                p1.getType()==null ||
                p2.getType()==null) {
              return false;
            }
            else {
              ProducedType p2st = p2.getType()
                  .substitute(st.getTypeArguments());
        if (!matches(p1.getType(), p2st, dec.getUnit())) {
                    return false;
              }
            }
        }
        return true;
    }*/
   
    private void checkRefinedTypeAndParameterTypes(Tree.Declaration that,
            Declaration dec, ClassOrInterface ci, Declaration refined) {
       
      List<ProducedType> typeArgs;
        if (refined instanceof Generic && dec instanceof Generic) {
            List<TypeParameter> refinedTypeParams = ((Generic) refined).getTypeParameters();
            List<TypeParameter> refiningTypeParams = ((Generic) dec).getTypeParameters();
            checkRefiningMemberTypeParameters(that, refined,
                refinedTypeParams, refiningTypeParams);
            typeArgs = checkRefiningMemberUpperBounds(that, ci, refined,
                refinedTypeParams, refiningTypeParams);
        }
        else {
          typeArgs = emptyList();
        }
       
        ProducedReference refinedMember = ci.getType().getTypedReference(refined, typeArgs);
        ProducedReference refiningMember = ci.getType().getTypedReference(dec, typeArgs);
        Declaration refinedMemberDec = refinedMember.getDeclaration();
    Declaration refiningMemberDec = refiningMember.getDeclaration();
    Node typeNode = getTypeErrorNode(that);
    if (refinedMemberIsDynamicallyTyped(refinedMemberDec, refiningMemberDec)) {
          checkRefiningMemberDynamicallyTyped(refined, refiningMemberDec, typeNode);
        }
    else if (refiningMemberIsDynamicallyTyped(refinedMemberDec, refiningMemberDec)) {
          checkRefinedMemberDynamicallyTyped(refined, refinedMemberDec, typeNode);
        }
    else if (refinedMemberIsVariable(refinedMemberDec)) {
            checkRefinedMemberTypeExactly(refiningMember, refinedMember, typeNode, refined);
        }
        else {
            //note: this version checks return type and parameter types in one shot, but the
            //resulting error messages aren't as friendly, so do it the hard way instead!
            //checkAssignable(refiningMember.getFullType(), refinedMember.getFullType(), that,
            checkRefinedMemberTypeAssignable(refiningMember, refinedMember, typeNode, refined);
        }
        if (dec instanceof Functional && refined instanceof Functional) {
           checkRefiningMemberParameters(that, dec, refined, refinedMember, refiningMember);
        }
    }

  private void checkRefiningMemberParameters(Tree.Declaration that,
            Declaration dec, Declaration refined,
            ProducedReference refinedMember, ProducedReference refiningMember) {
    List<ParameterList> refiningParamLists = ((Functional) dec).getParameterLists();
    List<ParameterList> refinedParamLists = ((Functional) refined).getParameterLists();
    if (refinedParamLists.size()!=refiningParamLists.size()) {
      that.addError("member must have the same number of parameter lists as refined member: " +
          message(refined));
    }
    for (int i=0; i<refinedParamLists.size() && i<refiningParamLists.size(); i++) {
      checkParameterTypes(that, getParameterList(that, i),
          refiningMember, refinedMember,
          refiningParamLists.get(i), refinedParamLists.get(i));
    }
    }

  private boolean refinedMemberIsVariable(Declaration refinedMemberDec) {
      return refinedMemberDec instanceof TypedDeclaration &&
                ((TypedDeclaration) refinedMemberDec).isVariable();
    }

  private void checkRefinedMemberDynamicallyTyped(Declaration refined,
            Declaration refinedMemberDec, Node typeNode) {
      if (!((TypedDeclaration) refinedMemberDec).isDynamicallyTyped()) {
        typeNode.addError("member which refines statically typed refined member must also be statically typed: " +
            message(refined));
      }
    }

  private void checkRefiningMemberDynamicallyTyped(Declaration refined,
            Declaration refiningMemberDec, Node typeNode) {
      if (!((TypedDeclaration) refiningMemberDec).isDynamicallyTyped()) {
        typeNode.addError("member which refines dynamically typed refined member must also be dynamically typed: " +
            message(refined));
      }
    }

  private boolean refiningMemberIsDynamicallyTyped(
            Declaration refinedMemberDec, Declaration refiningMemberDec) {
      return refinedMemberDec instanceof TypedDeclaration &&
        refiningMemberDec instanceof TypedDeclaration &&
            ((TypedDeclaration) refiningMemberDec).isDynamicallyTyped();
    }

  private boolean refinedMemberIsDynamicallyTyped(
            Declaration refinedMemberDec, Declaration refiningMemberDec) {
      return refinedMemberDec instanceof TypedDeclaration &&
        refiningMemberDec instanceof TypedDeclaration &&
            ((TypedDeclaration) refinedMemberDec).isDynamicallyTyped();
    }

  private void checkRefiningMemberTypeParameters(Tree.Declaration that,
            Declaration refined, List<TypeParameter> refinedTypeParams,
            List<TypeParameter> refiningTypeParams) {
      int refiningSize = refiningTypeParams.size();
      int refinedSize = refinedTypeParams.size();
      if (refiningSize!=refinedSize) {
          that.addError("member does not have the same number of type parameters as refined member: " +
                      message(refined));
      }
    }

  private List<ProducedType> checkRefiningMemberUpperBounds(Tree.Declaration that,
            ClassOrInterface ci, Declaration refined,
            List<TypeParameter> refinedTypeParams,
            List<TypeParameter> refiningTypeParams) {
        int refiningSize = refiningTypeParams.size();
        int refinedSize = refinedTypeParams.size();
      int max = refiningSize <= refinedSize ? refiningSize : refinedSize;
      if (max==0) {
        return emptyList();
      }
    List<ProducedType> typeArgs = new ArrayList<ProducedType>(max);
    for (int i=0; i<max; i++) {
          TypeParameter refinedTypeParam = refinedTypeParams.get(i);
          TypeParameter refiningTypeParam = refiningTypeParams.get(i);
          ProducedType refinedProducedType = refinedTypeParam.getType();
            Map<TypeParameter, ProducedType> args = ci.getType()
                    .getSupertype((TypeDeclaration)refined.getContainer())
                    .getTypeArguments();
          for (ProducedType t: refiningTypeParam.getSatisfiedTypes()) {
              ProducedType bound =
                  t.substitute(singletonMap(refiningTypeParam, refinedProducedType));
              //for every type constraint of the refining member, there must
              //be at least one type constraint of the refined member which
              //is assignable to it, guaranteeing that the intersection of
              //the refined member bounds is assignable to the intersection
              //of the refining member bounds
              //TODO: would it be better to just form the intersections and
              //      test assignability directly (the error messages might
              //      not be as helpful, but it might be less restrictive)
              boolean ok = false;
              for (ProducedType st: refinedTypeParam.getSatisfiedTypes()) {
                  if (st.substitute(args).isSubtypeOf(bound)) {
                      ok = true;
                  }
              }
              if (!ok) {
                  that.addError("member type parameter '" + refiningTypeParam.getName() +
                          "' has upper bound which refined member type parameter '" +
                          refinedTypeParam.getName() + "' of " + message(refined) +
                          " does not satisfy: '" + t.getProducedTypeName(that.getUnit()) + "'");
              }
          }
            for (ProducedType st: refinedTypeParam.getSatisfiedTypes()) {
                boolean ok = false;
                for (ProducedType t: refiningTypeParam.getSatisfiedTypes()) {
                    ProducedType bound =
                            t.substitute(singletonMap(refiningTypeParam, refinedProducedType));
                    if (st.substitute(args).isSubtypeOf(bound)) {
                        ok = true;
                    }
                }
                if (!ok) {
                    that.addUnsupportedError("refined member type parameter " +
                            refinedTypeParam.getName() + " of " + message(refined) +
                            " with upper bound which member type parameter " + refiningTypeParam.getName() +
                            " does not satisfy not yet supported: '" + st.getProducedTypeName(that.getUnit()) + "'");
                }
            }
          typeArgs.add(refinedProducedType);
      }
      return typeArgs;
    }

    private void checkRefinedMemberTypeAssignable(ProducedReference refiningMember,
        ProducedReference refinedMember, Node that, Declaration refined) {
        if (hasUncheckedNullType(refinedMember)) {
            ProducedType optionalRefinedType = refiningMember.getDeclaration()
                .getUnit().getOptionalType(refinedMember.getType());
            checkAssignableToOneOf(refiningMember.getType(), refinedMember.getType(),
                optionalRefinedType, that,
                "type of member must be assignable to type of refined member: " +
                    message(refined));
        }
        else {
            checkAssignable(refiningMember.getType(), refinedMember.getType(), that,
                "type of member must be assignable to type of refined member: " +
                    message(refined), 9000);
        }
    }

    private void checkRefinedMemberTypeExactly(ProducedReference refiningMember,
        ProducedReference refinedMember, Node that, Declaration refined) {
        if (hasUncheckedNullType(refinedMember)) {
            ProducedType optionalRefinedType = refiningMember.getDeclaration()
                .getUnit().getOptionalType(refinedMember.getType());
            checkIsExactlyOneOf(refiningMember.getType(), refinedMember.getType(),
                optionalRefinedType, that,
                "type of member must be exactly the same as type of variable refined member: " +
                          message(refined));
        }
        else {
            checkIsExactly(refiningMember.getType(), refinedMember.getType(), that,
                "type of member must be exactly the same as type of variable refined member: " +
                          message(refined), 9000);
        }
    }

    private boolean hasUncheckedNullType(ProducedReference member) {
        return member.getDeclaration() instanceof TypedDeclaration
                && ((TypedDeclaration)member.getDeclaration()).hasUncheckedNullType();
    }

    /*private void checkUnshared(Tree.Declaration that, Declaration dec) {
        if (dec.isActual()) {
            that.addError("actual member is not shared", 701);
        }
        if (dec.isFormal()) {
            that.addError("formal member is not shared", 702);
        }
        if (dec.isDefault()) {
            that.addError("default member is not shared", 703);
        }
    }*/

    private void checkNonrefinableDeclaration(Tree.Declaration that,
            Declaration dec) {
        if (dec.isActual()) {
            that.addError("actual declaration is not a method, getter, reference attribute, or class", 1301);
        }
        if (dec.isFormal()) {
            that.addError("formal declaration is not a method, getter, reference attribute, or class", 1302);
        }
        if (dec.isDefault()) {
            that.addError("default declaration is not a method, getter, reference attribute, or class", 1303);
        }
    }

    private void checkNonMember(Tree.Declaration that, Declaration dec, boolean mayBeShared) {
        if (!dec.isClassOrInterfaceMember() && mayBeShared) {
            if (dec.isActual()) {
                that.addError("actual declaration is not a member of a class or interface: '" +
                        dec.getName() + "'", 1301);
            }
            if (dec.isFormal()) {
                that.addError("formal declaration is not a member of a class or interface: '" +
                        dec.getName() + "'", 1302);
            }
            if (dec.isDefault()) {
                that.addError("default declaration is not a member of a class or interface: '" +
                        dec.getName() + "'", 1303);
            }
        }
        else if (!dec.isShared() && mayBeShared) {
            if (dec.isActual()) {
                that.addError("actual declaration must be shared: '" + dec.getName() + "'", 701);
            }
            if (dec.isFormal()) {
                that.addError("formal declaration must be shared: '" + dec.getName() + "'", 702);
            }
            if (dec.isDefault()) {
                that.addError("default declaration must be shared: '" + dec.getName() + "'", 703);
            }
        }
        else {
            if (dec.isActual()) {
                that.addError("declaration may not be actual: '" + dec.getName() + "'", 1301);
            }
            if (dec.isFormal()) {
                that.addError("declaration may not be formal: '" + dec.getName() + "'", 1302);
            }
            if (dec.isDefault()) {
                that.addError("declaration may not be default: '" + dec.getName() + "'", 1303);
            }
        }
    }
   
    private static String containerName(ProducedReference member) {
        return ((Declaration) member.getDeclaration().getContainer()).getName();
    }

    private void checkParameterTypes(Tree.Declaration that, Tree.ParameterList pl,
            ProducedReference member, ProducedReference refinedMember,
            ParameterList params, ParameterList refinedParams) {
        List<Parameter> paramsList = params.getParameters();
    List<Parameter> refinedParamsList = refinedParams.getParameters();
    if (paramsList.size()!=refinedParamsList.size()) {
           handleWrongParameterListLength(that, member, refinedMember);
        }
        else {
            for (int i=0; i<paramsList.size(); i++) {
                Parameter rparam = refinedParamsList.get(i);
                Parameter param = paramsList.get(i);
                ProducedType refinedParameterType =
                    refinedMember.getTypedParameter(rparam).getFullType();
                ProducedType parameterType =
                    member.getTypedParameter(param).getFullType();
                Tree.Parameter parameter = pl.getParameters().get(i);
                Node typeNode = parameter;
                if (parameter instanceof Tree.ParameterDeclaration) {
                  Tree.Type type = ((Tree.ParameterDeclaration) parameter)
                      .getTypedDeclaration().getType();
                  if (type!=null) {
                    typeNode = type;
                  }
                }
                if (parameter!=null) {
                if (rparam.getModel().isDynamicallyTyped()) {
                      checkRefiningParameterDynamicallyTyped(member,
                                refinedMember, param, typeNode);
                    }
                else if (param.getModel().isDynamicallyTyped()) {
                      checkRefinedParameterDynamicallyTyped(member,
                                refinedMember, rparam, param, typeNode);
                    }
                else if (refinedParameterType==null || parameterType==null) {
                  handleUnknownParameterType(member, refinedMember,
                                param, typeNode);
                    }
                    else {
                        checkRefiningParameterType(member, refinedMember,
                                refinedParams, rparam, refinedParameterType,
                                param, parameterType, typeNode);
                    }
                }
            }
        }
    }

  private void handleWrongParameterListLength(Tree.Declaration that,
            ProducedReference member, ProducedReference refinedMember) {
      that.addError("member does not have the same number of parameters as the member it refines: '" +
                   member.getDeclaration().getName() +
                   "' declared by '" + containerName(member) +
                   "' refining '" + refinedMember.getDeclaration().getName() +
                   "' declared by '" + containerName(refinedMember) + "'", 9100);
    }

  private static void checkRefiningParameterType(ProducedReference member,
            ProducedReference refinedMember, ParameterList refinedParams,
            Parameter rparam, ProducedType refinedParameterType,
            Parameter param, ProducedType parameterType, Node typeNode) {
      //TODO: consider type parameter substitution!!!
      checkIsExactlyForInterop(typeNode.getUnit(),
              refinedParams.isNamedParametersSupported(),
              parameterType, refinedParameterType, typeNode,
              "type of parameter '" + param.getName() + "' of '" +
                      member.getDeclaration().getName() +
                      "' declared by '" + containerName(member) +
                      "' is different to type of corresponding parameter '" +
                      rparam.getName() + "' of refined member '" +
                      refinedMember.getDeclaration().getName() + "' of '" +
                      containerName(refinedMember) + "'");
    }

  private void handleUnknownParameterType(ProducedReference member,
            ProducedReference refinedMember, Parameter param, Node typeNode) {
      typeNode.addError("could not determine if parameter type is the same as the corresponding parameter of refined member: '" +
              param.getName() + "' of '" + member.getDeclaration().getName() +
              "' declared by '" + containerName(member) +
              "' refining '" + refinedMember.getDeclaration().getName() +
              "' declared by '" + containerName(refinedMember) + "'");
    }

  private void checkRefinedParameterDynamicallyTyped(
            ProducedReference member, ProducedReference refinedMember,
            Parameter rparam, Parameter param, Node typeNode) {
      if (!rparam.getModel().isDynamicallyTyped()) {
        typeNode.addError("parameter which refines statically typed parameter must also be statically typed: '" +
            param.getName() + "' of '" + member.getDeclaration().getName() +
                  "' declared by '" + containerName(member) +
                  "' refining '" + refinedMember.getDeclaration().getName() +
                  "' declared by '" + containerName(refinedMember) + "'");
      }
    }

  private void checkRefiningParameterDynamicallyTyped(
            ProducedReference member, ProducedReference refinedMember,
            Parameter param, Node typeNode) {
      if (!param.getModel().isDynamicallyTyped()) {
        typeNode.addError("parameter which refines dynamically typed parameter must also be dynamically typed: '" +
            param.getName() + "' of '" + member.getDeclaration().getName() +
                  "' declared by '" + containerName(member) +
                  "' refining '" + refinedMember.getDeclaration().getName() +
                  "' declared by '" + containerName(refinedMember) + "'");
      }
    }

    private static Tree.ParameterList getParameterList(Tree.Declaration that, int i) {
        if (that instanceof Tree.AnyMethod) {
            return ((Tree.AnyMethod) that).getParameterLists().get(i);
        }
        else if (that instanceof Tree.AnyClass) {
            return ((Tree.AnyClass) that).getParameterList();
        }
        else {
            return null;
        }
    }
   
    @Override
    public void visit(Tree.ParameterList that) {
        super.visit(that);
        boolean foundSequenced = false;
        boolean foundDefault = false;
        ParameterList pl = that.getModel();
        for (Tree.Parameter p: that.getParameters()) {
            if (p!=null) {
                Parameter pm = p.getParameterModel();
                if (pm.isDefaulted()) {
                    if (foundSequenced) {
                        p.addError("defaulted parameter must occur before variadic parameter");
                    }
                    foundDefault = true;
                    if (!pl.isFirst()) {
                        p.addError("only the first parameter list may have defaulted parameters");
                    }
                }
                else if (pm.isSequenced()) {
                    if (foundSequenced) {
                        p.addError("parameter list may have at most one variadic parameter");
                    }
                    foundSequenced = true;
                    if (!pl.isFirst()) {
                        p.addError("only the first parameter list may have a variadic parameter");
                    }
                    if (foundDefault &&
                            pm.isAtLeastOne()) {
                        p.addError("parameter list with defaulted parameters may not have a nonempty variadic parameter");
                    }
                }
                else {
                    if (foundDefault) {
                        p.addError("required parameter must occur before defaulted parameters");
                    }
                    if (foundSequenced) {
                        p.addError("required parameter must occur before variadic parameter");
                    }
                }
            }
        }
    }
   
    @Override public void visit(Tree.SpecifierStatement that) {
        super.visit(that);
        List<ProducedType> sig = new ArrayList<ProducedType>();
        Tree.Term me = that.getBaseMemberExpression();
        while (me instanceof Tree.ParameterizedExpression) {
            sig.clear();
            Tree.ParameterizedExpression pe = (Tree.ParameterizedExpression) me;
            Tree.ParameterList pl = pe.getParameterLists().get(0);
            for (Tree.Parameter p: pl.getParameters()) {
                if (p!=null && p.getParameterModel()!=null) {
                    sig.add(p.getParameterModel().getType());
                }
                else {
                    sig.add(null);
                }
            }
            me = pe.getPrimary();
        }
        if (me instanceof Tree.BaseMemberExpression) {
            Tree.BaseMemberExpression bme = (Tree.BaseMemberExpression) me;
            Declaration d = getTypedDeclaration(bme.getScope(),
                    name(bme.getIdentifier()), sig, false, that.getUnit());
            if (d instanceof TypedDeclaration) {
                that.setDeclaration((TypedDeclaration) d);
                Scope cs = getRealScope(that.getScope().getContainer());
                if (cs instanceof ClassOrInterface &&
                        d.isClassOrInterfaceMember() &&
                        !d.getContainer().equals(cs) &&
                        ((ClassOrInterface) cs).inherits((ClassOrInterface) d.getContainer())) {
                    // interpret this specification as a
                    // refinement of an inherited member
                    if (d.getContainer()==that.getScope()) {
                        that.addError("parameter declaration hides refining member: '" +
                                d.getName() + "' (rename parameter)");
                    }
                    else if (d instanceof Value) {
                        refineValue((Value) d, bme, that, (ClassOrInterface) cs);
                    }
                    else if (d instanceof Method) {
                        refineMethod((Method) d, bme, that, (ClassOrInterface) cs);
                    }
                    else {
                        //TODO!
                        bme.addError("not a reference to a formal attribute: '" +
                                d.getName(that.getUnit()) + "'");
                    }
                }
            }
        }
    }

    private void refineValue(final Value sv,
            Tree.BaseMemberExpression bme,
            Tree.SpecifierStatement that,
            ClassOrInterface c) {
        final ProducedReference rv = getRefinedMember(sv, c);
        if (!sv.isFormal() && !sv.isDefault()
                && !sv.isShortcutRefinement()) { //this condition is here to squash a dupe message
            that.addError("inherited attribute may not be assigned in initializer and is neither formal nor default so may not be refined: " +
                    message(sv), 510);
        }
        else if (sv.isVariable()) {
            that.addError("inherited attribute may not be assigned in initializer and is variable so may not be refined by non-variable: " +
                    message(sv));
        }
        Value v = new Value();
        v.setName(sv.getName());
        v.setShared(true);
        v.setActual(true);
        v.getAnnotations().add(new Annotation("shared"));
        v.getAnnotations().add(new Annotation("actual"));
        v.setRefinedDeclaration(sv.getRefinedDeclaration());
        v.setUnit(that.getUnit());
        v.setContainer(c);
        v.setScope(c);
        v.setShortcutRefinement(true);
        setVisibleScope(v);
        c.addMember(v);
        that.setRefinement(true);
        that.setDeclaration(v);
        that.setRefined(sv);
        that.getUnit().addDeclaration(v);
        v.setType(new LazyProducedType(that.getUnit()) {
            @Override
            public Map<TypeParameter, ProducedType> initTypeArguments() {
                return rv.getType().getTypeArguments();
            }
            @Override
            public TypeDeclaration initDeclaration() {
                return rv.getType().getDeclaration();
            }
        });
    }

    private void refineMethod(final Method sm,
            Tree.BaseMemberExpression bme,
            Tree.SpecifierStatement that,
            ClassOrInterface c) {
        final ProducedReference rm = getRefinedMember(sm, c);
        if (!sm.isFormal() && !sm.isDefault()
                && !sm.isShortcutRefinement()) { //this condition is here to squash a dupe message
            that.addError("inherited method is neither formal nor default so may not be refined: " +
                    message(sm));
        }
        Method m = new Method();
        m.setName(sm.getName());
        List<Tree.ParameterList> tpls;
        Tree.Term me = that.getBaseMemberExpression();
        if (me instanceof Tree.ParameterizedExpression) {
            tpls = ((Tree.ParameterizedExpression) me).getParameterLists();
        }
        else {
            tpls = Collections.emptyList();
        }
        int i=0;
        for (ParameterList pl: sm.getParameterLists()) {
            ParameterList l = new ParameterList();
            Tree.ParameterList tpl = tpls.size()<=i ?
                    null : tpls.get(i++);
            int j=0;
            for (final Parameter p: pl.getParameters()) {
                //TODO: meaningful errors when parameters don't line up
                //      currently this is handled elsewhere, but we can
                //      probably do it better right here
                if (tpl==null || tpl.getParameters().size()<=j) {
                    Parameter vp = new Parameter();
                    Value v = new Value();
                    vp.setModel(v);
                    v.setInitializerParameter(vp);
                    vp.setSequenced(p.isSequenced());
                    vp.setAtLeastOne(p.isAtLeastOne());
                    vp.setDefaulted(p.isDefaulted());
                    vp.setName(p.getName());
                    v.setName(p.getName());
                    vp.setDeclaration(m);
                    v.setContainer(m);
                    v.setScope(m);
                    l.getParameters().add(vp);
                    v.setType(new LazyProducedType(that.getUnit()) {
                        @Override
                        public Map<TypeParameter, ProducedType> initTypeArguments() {
                            return rm.getTypedParameter(p).getFullType()
                                    .getTypeArguments();
                        }
                        @Override
                        public TypeDeclaration initDeclaration() {
                            return rm.getTypedParameter(p).getFullType()
                                    .getDeclaration();
                        }
                    });
                }
                else {
                    Tree.Parameter tp = tpl.getParameters().get(j);
                    Parameter rp = tp.getParameterModel();
                    rp.setDefaulted(p.isDefaulted());
                    rp.setDeclaration(m);
                    l.getParameters().add(rp);
                }
                j++;
            }
            m.getParameterLists().add(l);
        }
        if (!sm.getTypeParameters().isEmpty()) {
            bme.addError("method has type parameters: "
                    message(sm));
        }
        m.setShared(true);
        m.setActual(true);
        m.getAnnotations().add(new Annotation("shared"));
        m.getAnnotations().add(new Annotation("actual"));
        m.setRefinedDeclaration(sm.getRefinedDeclaration()); //Note: this is not the real root, so we set it again in ExpressionVisitor
        m.setUnit(that.getUnit());
        m.setContainer(c);
        m.setShortcutRefinement(true);
        m.setDeclaredVoid(sm.isDeclaredVoid());
        setVisibleScope(m);
        c.addMember(m);
        that.setRefinement(true);
        that.setDeclaration(m);
        that.setRefined(sm);
        that.getUnit().addDeclaration(m);
        if (that.getScope() instanceof Specification){
            ((Specification) that.getScope()).setDeclaration(m);
        }
        m.setType(new LazyProducedType(that.getUnit()) {
            @Override
            public Map<TypeParameter, ProducedType> initTypeArguments() {
                return rm.getType().getTypeArguments();
            }
            @Override
            public TypeDeclaration initDeclaration() {
                return rm.getType().getDeclaration();
            }
        });
    }
   
}
TOP

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

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.