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

framework.src.org.checkerframework.framework.util.typeinference.solver.SubtypesSolver Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java’s type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

There is a newer version: 3.42.0
Show newest version
package org.checkerframework.framework.util.typeinference.solver;

import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.framework.util.typeinference.GlbUtil;
import org.checkerframework.framework.util.typeinference.solver.InferredValue.InferredType;
import org.checkerframework.framework.util.typeinference.solver.TargetConstraints.Subtypes;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.util.Types;

/**
 * Infers type arguments by using the Greatest Lower Bound computation on the subtype relationships
 * in a constraint map.
 */
public class SubtypesSolver {

    /**
     * Infers type arguments using subtype constraints.
     * @param remainingTargets targets for which we still need to infer a value
     * @param constraints the set of constraints for all targets
     * @return a mapping of ( {@code target -> inferred type} ), note this class always infers concrete types
     *         and will not infer that the target is equivalent to another target
     */
    public InferenceResult solveFromSubtypes(final Set remainingTargets, final ConstraintMap constraints,
                                             final AnnotatedTypeFactory typeFactory) {
        return glbSubtypes(remainingTargets, constraints, typeFactory);
    }

    public InferenceResult glbSubtypes(final Set remainingTargets, final ConstraintMap constraints,
                                       final AnnotatedTypeFactory typeFactory) {
        final InferenceResult inferenceResult = new InferenceResult();
        final QualifierHierarchy qualifierHierarchy = typeFactory.getQualifierHierarchy();

        final Types types = typeFactory.getProcessingEnv().getTypeUtils();

        List targetsSubtypesLast = new ArrayList<>(remainingTargets);

        // If we have two type variables  order them A then B
        // this is required because we will use the fact that B must be below A
        // when determining the glb of B
        Collections.sort(targetsSubtypesLast, new Comparator() {
            @Override
            public int compare(TypeVariable o1, TypeVariable o2) {
                if (types.isSubtype(o1, o2)) {
                    return 1;
                } else if (types.isSubtype(o2, o1)) {
                    return -1;
                }

                return 0;
            }
        });

        for (final TypeVariable target : targetsSubtypesLast) {
            Subtypes subtypes = constraints.getConstraints(target).subtypes;

            if (subtypes.types.isEmpty()) {
                continue;
            }

            propagatePreviousGlbs(subtypes, inferenceResult, subtypes.types);

            // if the subtypes size is only 1 then we need not do any GLBing on the underlying types
            // but we may have primary annotations that need to be GLBed
            Map> primaries = subtypes.primaries;
            if (subtypes.types.size() == 1) {
                final Entry> entry = subtypes.types.entrySet().iterator().next();
                AnnotatedTypeMirror supertype = entry.getKey().deepCopy();

                for (AnnotationMirror top : entry.getValue()) {
                    final Set superAnnos = primaries.get(top);
                    if (superAnnos != null) { // if it is null we're just going to use the anno already on supertype
                        final AnnotationMirror supertypeAnno = supertype.getAnnotationInHierarchy(top);
                        superAnnos.add(supertypeAnno);
                    }
                }

                if (!primaries.isEmpty()) {
                    for (AnnotationMirror top : qualifierHierarchy.getTopAnnotations()) {
                        final AnnotationMirror glb = greatestLowerBound(subtypes.primaries.get(top), qualifierHierarchy);
                        supertype.replaceAnnotation(glb);
                    }
                }

                inferenceResult.put(target, new InferredType(supertype));

            }  else {

                // GLB all of the types than combine this with the GLB of primary annotation constraints
                final AnnotatedTypeMirror glbType = GlbUtil.glbAll(subtypes.types, typeFactory);
                if (glbType != null) {
                    if (!primaries.isEmpty()) {
                        for (AnnotationMirror top : qualifierHierarchy.getTopAnnotations()) {
                            final AnnotationMirror glb = greatestLowerBound(subtypes.primaries.get(top), qualifierHierarchy);
                            final AnnotationMirror currentAnno = glbType.getAnnotationInHierarchy(top);

                            if (currentAnno == null) {
                                glbType.addAnnotation(glb);
                            } else if (glb != null) {
                                glbType.replaceAnnotation(qualifierHierarchy.greatestLowerBound(glb, currentAnno));
                            }
                        }
                    }

                    inferenceResult.put(target, new InferredType(glbType));
                }
            }
        }

        return inferenceResult;
    }

    /**
     /**
     * If the target corresponding to targetRecord must be a subtype of another target for which
     * we have already determined a GLB, add that target's GLB to the list of subtypes to be GLBed
     * for this target.
     */
    protected static void propagatePreviousGlbs(final Subtypes targetSubtypes, InferenceResult solution,
                                                final Map> subtypesOfTarget ) {

        for (final Entry> subtypeTarget : targetSubtypes.targets.entrySet()) {
            final InferredValue subtargetInferredGlb = solution.get(subtypeTarget.getKey());

            if (subtargetInferredGlb != null) {
                final AnnotatedTypeMirror subtargetGlbType = ((InferredType)subtargetInferredGlb).type;
                Set subtargetAnnos = subtypesOfTarget.get(subtargetGlbType);
                if (subtargetAnnos != null) {
                    // there is already an equivalent type in the list of subtypes, just add
                    // any hierarchies that are not in its list but are in the supertarget's list
                    subtargetAnnos.addAll(subtypeTarget.getValue());
                } else {
                    subtypesOfTarget.put(subtargetGlbType, subtypeTarget.getValue());
                }
            }
        }

    }


    /**
     * @param annos a set of annotations in the same annotation hierarchy
     * @param qualifierHierarchy the qualifier of the annotation hierarchy
     * @return the GLB of annos
     */
    private final AnnotationMirror greatestLowerBound(final Iterable annos,
                                                      QualifierHierarchy qualifierHierarchy) {
        Iterator annoIter = annos.iterator();
        AnnotationMirror glb = annoIter.next();

        while (annoIter.hasNext()) {
            glb = qualifierHierarchy.greatestLowerBound(glb, annoIter.next());
        }

        return glb;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy