All Downloads are FREE. Search and download functionalities are using the official Maven repository.

framework.src.org.checkerframework.qualframework.poly.QualifierParameterTypeFactory Maven / Gradle / Ivy

package org.checkerframework.qualframework.poly;

import org.checkerframework.javacutil.ElementUtils;
import org.checkerframework.javacutil.Pair;
import org.checkerframework.javacutil.TreeUtils;
import org.checkerframework.qualframework.base.DefaultQualifiedTypeFactory;
import org.checkerframework.qualframework.base.QualifiedTypeMirror;
import org.checkerframework.qualframework.base.QualifiedTypeMirror.QualifiedExecutableType;
import org.checkerframework.qualframework.base.QualifiedTypeMirror.QualifiedTypeVariable;
import org.checkerframework.qualframework.base.QualifiedTypeMirror.QualifiedWildcardType;
import org.checkerframework.qualframework.base.QualifierHierarchy;
import org.checkerframework.qualframework.base.QualifierMapVisitor;
import org.checkerframework.qualframework.base.SetQualifierVisitor;
import org.checkerframework.qualframework.util.QualifierContext;

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

import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;

import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.Tree.Kind;

/** Type factory with qualifier polymorphism support.  This type factory
 * extends an underlying qualifier system with qualifier variables, combined
 * qualifiers (using {@link CombiningOperation}), wildcards, typechecking
 * support for all of the above, substitution for accessing fields whose types
 * refer to qualifier parameters, and qualifier inference for method qualifier
 * parameters.
 */
public abstract class QualifierParameterTypeFactory extends DefaultQualifiedTypeFactory> {
    QualifierHierarchy groundHierarchy;

    public QualifierParameterTypeFactory(QualifierContext> context) {
        super(context);
    }

    @Override
    protected abstract QualifierParameterAnnotationConverter createAnnotationConverter();

    @Override
    public QualifierParameterAnnotationConverter getAnnotationConverter() {
        return (QualifierParameterAnnotationConverter)super.getAnnotationConverter();
    }

    /** Create a {@link QualifierHierarchy} for ground qualifiers (represented
     * by instances of {@code Q}).
     */
    protected abstract QualifierHierarchy createGroundQualifierHierarchy();

    /** Get the ground qualifier hierarchy used by this type system. */
    public QualifierHierarchy getGroundQualifierHierarchy() {
        if (groundHierarchy == null) {
            groundHierarchy = createGroundQualifierHierarchy();
        }
        return groundHierarchy;
    }

    @Override
    protected QualifierHierarchy> createQualifierHierarchy() {
        return QualifierParameterHierarchy.fromGround(getGroundQualifierHierarchy());
    }

    @Override
    protected QualifierParameterTypeAnnotator createTypeAnnotator() {
        return new QualifierParameterTypeAnnotator(getContext(), getAnnotationConverter(),
                new ContainmentHierarchy<>(new PolyQualHierarchy<>(getGroundQualifierHierarchy())));
    }

    @Override
    protected QualifierParameterTreeAnnotator createTreeAnnotator() {
        return new QualifierParameterTreeAnnotator(this);
    }

    /*
    public final QualParams applyCaptureConversion(QualParams objectQual) {
        if (objectQual == null || objectQual == QualParams.getBottom()
                || objectQual == QualParams.getTop())
            return objectQual;
        return objectQual.capture();
    }
    */

    /** Apply substitution to get the effective type of a class member when
     * accessed through an instance with particular qualifiers.  This method
     * roughly corresponds to AnnotatedTypes.asMemberOf.
     */
    private QualParams qualifierAsMemberOf(QualParams memberQual, QualParams objectQual) {

        if (memberQual == getQualifierHierarchy().getBottom()) {
            // Substituting in the object qualifier would not change the bottom qualifier.
            return getQualifierHierarchy().getBottom();

        } else if (objectQual == getQualifierHierarchy().getBottom()) {
            // objectQual (the receiver) is bottom. Right now just return the existing qualifier.
            // If objectQual is not an @Var, then nothing should have been substituted anyway.
            // If objectQual is an @Var, then what ground qualifier should be used?

            return memberQual;
        }

        return memberQual.substituteAll(objectQual);
    }

    /** Visitor to apply {@code qualifierAsMemberOf} at every location within a
     * {@link QualifiedTypeMirror}.
     */
    private final QualifierMapVisitor, QualParams, QualParams> AS_MEMBER_OF_VISITOR =
        new QualifierMapVisitor, QualParams, QualParams>() {
            @Override
            public QualParams process(QualParams memberQual, QualParams objectQual) {
                return qualifierAsMemberOf(memberQual, objectQual);
            }
        };

    @Override
    public QualifiedTypeMirror> postAsMemberOf(
            QualifiedTypeMirror> memberType,
            QualifiedTypeMirror> receiverType,
            Element memberElement) {

        // Don't run postAsMemberOf when viewing members from inside a class.
        if (receiverType.getUnderlyingType().isDeclaration()) {
            return memberType;
        }

        final QualParams effectiveReceiverQualifier;
        switch (receiverType.getKind()) {
            case WILDCARD:
                effectiveReceiverQualifier = ((QualifiedWildcardType>) receiverType).getExtendsBound().getQualifier();
                break;
            case TYPEVAR:
                if (((QualifiedTypeVariable>) receiverType).isPrimaryQualifierValid()) {
                    effectiveReceiverQualifier = receiverType.getQualifier();
                } else {
                    effectiveReceiverQualifier = this.getQualifiedTypeParameterBounds(
                            ((QualifiedTypeVariable>) receiverType).getDeclaration().getUnderlyingType()).
                            getUpperBound().getQualifier();
                }
                break;
            default:
                effectiveReceiverQualifier = receiverType.getQualifier();
        }

        return AS_MEMBER_OF_VISITOR.visit(memberType, effectiveReceiverQualifier);
    }

    /** Visitor to apply substitution at every location within a {@link
     * QualifiedTypeMirror}.  This is used in {@code methodFromUse} to
     * substitute in the newly-inferred values for method qualifier parameters.
     */
    private final QualifierMapVisitor, QualParams, Map>> SUBSTITUTE_VISITOR =
        new QualifierMapVisitor, QualParams, Map>>() {
            @Override
            public QualParams process(QualParams params, Map> substs) {
                if (params.equals(getQualifierHierarchy().getBottom())) {
                    return getQualifierHierarchy().getBottom();
                }
                return params.substituteAll(substs);
            }
        };


    @Override
    public Pair>, List>>> methodFromUse(ExpressionTree tree,
            ExecutableElement methodElt, QualifiedTypeMirror> receiverType) {

        Pair>, List>>> result = super.methodFromUse(tree,
                methodElt, receiverType);

        Element elt = result.first.getUnderlyingType().asElement();
        Set qualParams = getAnnotationConverter().getDeclaredParameters(
                elt, getDeclAnnotations(elt), getDecoratedElement(elt));

        if (qualParams.isEmpty()) {
            // This check is not just a performance optimization - it saves us
            // from crashing in one obscure corner case.  An `enum`
            // declarations gets an auto-generated constructor with an
            // auto-generated `super()` call.  But the actual java.lang.Enum
            // constructor takes two arguments.  So trying to do inference on
            // that super call will cause a crash.  (This problem shows up as
            // an IndexOutOfBoundsException in tests/all-systems/Enums.java.)
            // The constructor has no qualifier parameters, though, so we can
            // skip processing it using this check.
            return result;
        }

        List>> formals = new ArrayList<>();
        List>> actuals = new ArrayList<>();
        if (tree.getKind() == Kind.METHOD_INVOCATION) {
            formals.addAll(getQualifiedTypes().expandVarArgs(result.first, ((MethodInvocationTree)tree).getArguments()));
            for (ExpressionTree actualExpr : ((MethodInvocationTree)tree).getArguments()) {
                actuals.add(getQualifiedType(actualExpr));
            }
        }

        if (! ElementUtils.isStatic(TreeUtils.elementFromUse(tree))) {
            // Need to include receivers in the inference.
            formals.add(result.first.getReceiverType());
            actuals.add(receiverType);
        }

        QualifierParameterHierarchy hierarchy = (QualifierParameterHierarchy)getQualifierHierarchy();
        MethodParameterInference inference = new MethodParameterInference<>(
                new ArrayList<>(qualParams), formals, actuals,
                groundHierarchy, new PolyQualHierarchy<>(groundHierarchy),
                hierarchy, getTypeHierarchy());

        Map> subst = inference.infer();

        if (subst != null) {
            Map> wildSubst = new HashMap<>();
            for (String name : subst.keySet()) {
                wildSubst.put(name, new Wildcard<>(subst.get(name)));
            }

            QualifiedExecutableType> newMethodType =
                    (QualifiedExecutableType>)SUBSTITUTE_VISITOR.visit(result.first, wildSubst);
            List>> newTypeArgs = new ArrayList<>();
            for (QualifiedTypeMirror> qtm : result.second) {
                newTypeArgs.add(SUBSTITUTE_VISITOR.visit(qtm, wildSubst));
            }
            result = Pair.of(newMethodType, newTypeArgs);
        } else {
            // TODO: report error
        }

        return result;
    }

    @Override
    public List>> postDirectSuperTypes(
            QualifiedTypeMirror> subtype,
            List>> supertypes) {
        QualParams subQuals = subtype.getQualifier();
        if (subQuals == null) {
            return new ArrayList<>(supertypes);
        }

        List>> result = new ArrayList<>();
        for (QualifiedTypeMirror> supertype : supertypes) {
            QualParams superQuals;
            if (subQuals == getQualifierHierarchy().getBottom()) {
                // If subclass qualifier is bottom, use bottom for the superclass qualifier.

                // Substituting in bottom is undefined -- If there are any superclass qualifier parameters,
                // what should be the arguments to those parameters? Because of invariant subtyping, there
                // are no arguments where the resulting qualifier would still be bottom.

                superQuals = subQuals;
            } else {
                superQuals = supertype.getQualifier().substituteAll(subQuals);
                // substituteAll performs substitutions on the primary, but when viewing the superclass we want to
                // use the exact primary qualifier of the subclass.
                // This was needed to get the Ternary.java test to work.
                superQuals.setPrimary(subQuals.getPrimary());
            }

            result.add(SetQualifierVisitor.apply(supertype, superQuals));

        }

        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy