Search in sources :

Example 31 with Type

use of org.eclipse.ceylon.langtools.tools.javac.code.Type in project ceylon by eclipse.

the class Attr method attribClass.

/**
 * Attribute class definition associated with given class symbol.
 *  @param c   The class symbol whose definition will be attributed.
 */
void attribClass(ClassSymbol c) throws CompletionFailure {
    if (c.type.hasTag(ERROR))
        return;
    // Check for cycles in the inheritance graph, which can arise from
    // ill-formed class files.
    chk.checkNonCyclic(null, c.type);
    Type st = types.supertype(c.type);
    if ((c.flags_field & Flags.COMPOUND) == 0) {
        // First, attribute superclass.
        if (st.hasTag(CLASS))
            attribClass((ClassSymbol) st.tsym);
        // Next attribute owner, if it is a class.
        if (c.owner.kind == TYP && c.owner.type.hasTag(CLASS))
            attribClass((ClassSymbol) c.owner);
    }
    // UNATTRIBUTED.
    if ((c.flags_field & UNATTRIBUTED) != 0) {
        c.flags_field &= ~UNATTRIBUTED;
        // Get environment current at the point of class definition.
        Env<AttrContext> env = typeEnvs.get(c);
        // The info.lint field in the envs stored in typeEnvs is deliberately uninitialized,
        // because the annotations were not available at the time the env was created. Therefore,
        // we look up the environment chain for the first enclosing environment for which the
        // lint value is set. Typically, this is the parent env, but might be further if there
        // are any envs created as a result of TypeParameter nodes.
        Env<AttrContext> lintEnv = env;
        while (lintEnv.info.lint == null) lintEnv = lintEnv.next;
        // Having found the enclosing lint value, we can initialize the lint value for this class
        env.info.lint = lintEnv.info.lint.augment(c);
        Lint prevLint = chk.setLint(env.info.lint);
        JavaFileObject prev = log.useSource(c.sourcefile);
        ResultInfo prevReturnRes = env.info.returnResult;
        try {
            deferredLintHandler.flush(env.tree);
            env.info.returnResult = null;
            // java.lang.Enum may not be subclassed by a non-enum
            if (st.tsym == syms.enumSym && ((c.flags_field & (Flags.ENUM | Flags.COMPOUND)) == 0))
                log.error(env.tree.pos(), "enum.no.subclassing");
            // Enums may not be extended by source-level classes
            if (st.tsym != null && ((st.tsym.flags_field & Flags.ENUM) != 0) && ((c.flags_field & (Flags.ENUM | Flags.COMPOUND)) == 0)) {
                log.error(env.tree.pos(), "enum.types.not.extensible");
            }
            if (isSerializable(c.type)) {
                env.info.isSerializable = true;
            }
            attribClassBody(env, c);
            chk.checkDeprecatedAnnotation(env.tree.pos(), c);
            chk.checkClassOverrideEqualsAndHashIfNeeded(env.tree.pos(), c);
            chk.checkFunctionalInterface((JCClassDecl) env.tree, c);
        } finally {
            env.info.returnResult = prevReturnRes;
            log.useSource(prev);
            chk.setLint(prevLint);
        }
    }
}
Also used : JavaFileObject(org.eclipse.ceylon.javax.tools.JavaFileObject) Type(org.eclipse.ceylon.langtools.tools.javac.code.Type)

Example 32 with Type

use of org.eclipse.ceylon.langtools.tools.javac.code.Type in project ceylon by eclipse.

the class Attr method visitLambda.

/*
     * A lambda expression can only be attributed when a target-type is available.
     * In addition, if the target-type is that of a functional interface whose
     * descriptor contains inference variables in argument position the lambda expression
     * is 'stuck' (see DeferredAttr).
     */
@Override
public void visitLambda(final JCLambda that) {
    if (pt().isErroneous() || (pt().hasTag(NONE) && pt() != Type.recoveryType)) {
        if (pt().hasTag(NONE)) {
            // lambda only allowed in assignment or method invocation/cast context
            log.error(that.pos(), "unexpected.lambda");
        }
        result = that.type = types.createErrorType(pt());
        return;
    }
    // create an environment for attribution of the lambda expression
    final Env<AttrContext> localEnv = lambdaEnv(that, env);
    boolean needsRecovery = resultInfo.checkContext.deferredAttrContext().mode == DeferredAttr.AttrMode.CHECK;
    try {
        Type currentTarget = pt();
        if (needsRecovery && isSerializable(currentTarget)) {
            localEnv.info.isSerializable = true;
        }
        List<Type> explicitParamTypes = null;
        if (that.paramKind == JCLambda.ParameterKind.EXPLICIT) {
            // attribute lambda parameters
            attribStats(that.params, localEnv);
            explicitParamTypes = TreeInfo.types(that.params);
        }
        Type lambdaType;
        if (pt() != Type.recoveryType) {
            /* We need to adjust the target. If the target is an
                 * intersection type, for example: SAM & I1 & I2 ...
                 * the target will be updated to SAM
                 */
            currentTarget = targetChecker.visit(currentTarget, that);
            if (explicitParamTypes != null) {
                currentTarget = infer.instantiateFunctionalInterface(that, currentTarget, explicitParamTypes, resultInfo.checkContext);
            }
            currentTarget = types.removeWildcards(currentTarget);
            lambdaType = types.findDescriptorType(currentTarget);
        } else {
            currentTarget = Type.recoveryType;
            lambdaType = fallbackDescriptorType(that);
        }
        setFunctionalInfo(localEnv, that, pt(), lambdaType, currentTarget, resultInfo.checkContext);
        if (lambdaType.hasTag(FORALL)) {
            // lambda expression target desc cannot be a generic method
            resultInfo.checkContext.report(that, diags.fragment("invalid.generic.lambda.target", lambdaType, kindName(currentTarget.tsym), currentTarget.tsym));
            result = that.type = types.createErrorType(pt());
            return;
        }
        if (that.paramKind == JCLambda.ParameterKind.IMPLICIT) {
            // add param type info in the AST
            List<Type> actuals = lambdaType.getParameterTypes();
            List<JCVariableDecl> params = that.params;
            boolean arityMismatch = false;
            while (params.nonEmpty()) {
                if (actuals.isEmpty()) {
                    // not enough actuals to perform lambda parameter inference
                    arityMismatch = true;
                }
                // reset previously set info
                Type argType = arityMismatch ? syms.errType : actuals.head;
                params.head.vartype = make.at(params.head).Type(argType);
                params.head.sym = null;
                actuals = actuals.isEmpty() ? actuals : actuals.tail;
                params = params.tail;
            }
            // attribute lambda parameters
            attribStats(that.params, localEnv);
            if (arityMismatch) {
                resultInfo.checkContext.report(that, diags.fragment("incompatible.arg.types.in.lambda"));
                result = that.type = types.createErrorType(currentTarget);
                return;
            }
        }
        // from this point on, no recovery is needed; if we are in assignment context
        // we will be able to attribute the whole lambda body, regardless of errors;
        // if we are in a 'check' method context, and the lambda is not compatible
        // with the target-type, it will be recovered anyway in Attr.checkId
        needsRecovery = false;
        FunctionalReturnContext funcContext = that.getBodyKind() == JCLambda.BodyKind.EXPRESSION ? new ExpressionLambdaReturnContext((JCExpression) that.getBody(), resultInfo.checkContext) : new FunctionalReturnContext(resultInfo.checkContext);
        ResultInfo bodyResultInfo = lambdaType.getReturnType() == Type.recoveryType ? recoveryInfo : new ResultInfo(VAL, lambdaType.getReturnType(), funcContext);
        localEnv.info.returnResult = bodyResultInfo;
        if (that.getBodyKind() == JCLambda.BodyKind.EXPRESSION) {
            attribTree(that.getBody(), localEnv, bodyResultInfo);
        } else {
            JCBlock body = (JCBlock) that.body;
            attribStats(body.stats, localEnv);
        }
        result = check(that, currentTarget, VAL, resultInfo);
        boolean isSpeculativeRound = resultInfo.checkContext.deferredAttrContext().mode == DeferredAttr.AttrMode.SPECULATIVE;
        preFlow(that);
        flow.analyzeLambda(env, that, make, isSpeculativeRound);
        // avoids recovery at this stage
        that.type = currentTarget;
        checkLambdaCompatible(that, lambdaType, resultInfo.checkContext);
        if (!isSpeculativeRound) {
            // add thrown types as bounds to the thrown types free variables if needed:
            if (resultInfo.checkContext.inferenceContext().free(lambdaType.getThrownTypes())) {
                List<Type> inferredThrownTypes = flow.analyzeLambdaThrownTypes(env, that, make);
                List<Type> thrownTypes = resultInfo.checkContext.inferenceContext().asUndetVars(lambdaType.getThrownTypes());
                chk.unhandled(inferredThrownTypes, thrownTypes);
            }
            checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), lambdaType, currentTarget);
        }
        result = check(that, currentTarget, VAL, resultInfo);
    } catch (Types.FunctionDescriptorLookupError ex) {
        JCDiagnostic cause = ex.getDiagnostic();
        resultInfo.checkContext.report(that, cause);
        result = that.type = types.createErrorType(pt());
        return;
    } finally {
        localEnv.info.scope.leave();
        if (needsRecovery) {
            attribTree(that, env, recoveryInfo);
        }
    }
}
Also used : Type(org.eclipse.ceylon.langtools.tools.javac.code.Type)

Example 33 with Type

use of org.eclipse.ceylon.langtools.tools.javac.code.Type in project ceylon by eclipse.

the class Attr method setFunctionalInfo.

/**
 * Set functional type info on the underlying AST. Note: as the target descriptor
 * might contain inference variables, we might need to register an hook in the
 * current inference context.
 */
private void setFunctionalInfo(final Env<AttrContext> env, final JCFunctionalExpression fExpr, final Type pt, final Type descriptorType, final Type primaryTarget, final CheckContext checkContext) {
    if (checkContext.inferenceContext().free(descriptorType)) {
        checkContext.inferenceContext().addFreeTypeListener(List.of(pt, descriptorType), new FreeTypeListener() {

            public void typesInferred(InferenceContext inferenceContext) {
                setFunctionalInfo(env, fExpr, pt, inferenceContext.asInstType(descriptorType), inferenceContext.asInstType(primaryTarget), checkContext);
            }
        });
    } else {
        ListBuffer<Type> targets = new ListBuffer<>();
        if (pt.hasTag(CLASS)) {
            if (pt.isCompound()) {
                // this goes first
                targets.append(types.removeWildcards(primaryTarget));
                for (Type t : ((IntersectionClassType) pt()).interfaces_field) {
                    if (t != primaryTarget) {
                        targets.append(types.removeWildcards(t));
                    }
                }
            } else {
                targets.append(types.removeWildcards(primaryTarget));
            }
        }
        fExpr.targets = targets.toList();
        if (checkContext.deferredAttrContext().mode == DeferredAttr.AttrMode.CHECK && pt != Type.recoveryType) {
            // check that functional interface class is well-formed
            try {
                /* Types.makeFunctionalInterfaceClass() may throw an exception
                     * when it's executed post-inference. See the listener code
                     * above.
                     */
                ClassSymbol csym = types.makeFunctionalInterfaceClass(env, names.empty, List.of(fExpr.targets.head), ABSTRACT);
                if (csym != null) {
                    chk.checkImplementations(env.tree, csym, csym);
                }
            } catch (Types.FunctionDescriptorLookupError ex) {
                JCDiagnostic cause = ex.getDiagnostic();
                resultInfo.checkContext.report(env.tree, cause);
            }
        }
    }
}
Also used : Type(org.eclipse.ceylon.langtools.tools.javac.code.Type) InferenceContext(org.eclipse.ceylon.langtools.tools.javac.comp.Infer.InferenceContext) FreeTypeListener(org.eclipse.ceylon.langtools.tools.javac.comp.Infer.FreeTypeListener)

Example 34 with Type

use of org.eclipse.ceylon.langtools.tools.javac.code.Type in project ceylon by eclipse.

the class Attr method condType.

/**
 * Compute the type of a conditional expression, after
 *  checking that it exists.  See JLS 15.25. Does not take into
 *  account the special case where condition and both arms
 *  are constants.
 *
 *  @param pos      The source position to be used for error
 *                  diagnostics.
 *  @param thentype The type of the expression's then-part.
 *  @param elsetype The type of the expression's else-part.
 */
private Type condType(DiagnosticPosition pos, Type thentype, Type elsetype) {
    // If same type, that is the result
    if (types.isSameType(thentype, elsetype))
        return thentype.baseType();
    Type thenUnboxed = (!allowBoxing || thentype.isPrimitive()) ? thentype : types.unboxedType(thentype);
    Type elseUnboxed = (!allowBoxing || elsetype.isPrimitive()) ? elsetype : types.unboxedType(elsetype);
    // arm is short, the other is char).
    if (thenUnboxed.isPrimitive() && elseUnboxed.isPrimitive()) {
        // that fits into the subrange, return the subrange type.
        if (thenUnboxed.getTag().isStrictSubRangeOf(INT) && elseUnboxed.hasTag(INT) && types.isAssignable(elseUnboxed, thenUnboxed)) {
            return thenUnboxed.baseType();
        }
        if (elseUnboxed.getTag().isStrictSubRangeOf(INT) && thenUnboxed.hasTag(INT) && types.isAssignable(thenUnboxed, elseUnboxed)) {
            return elseUnboxed.baseType();
        }
        for (TypeTag tag : primitiveTags) {
            Type candidate = syms.typeOfTag[tag.ordinal()];
            if (types.isSubtype(thenUnboxed, candidate) && types.isSubtype(elseUnboxed, candidate)) {
                return candidate;
            }
        }
    }
    // Those were all the cases that could result in a primitive
    if (allowBoxing) {
        if (thentype.isPrimitive())
            thentype = types.boxedClass(thentype).type;
        if (elsetype.isPrimitive())
            elsetype = types.boxedClass(elsetype).type;
    }
    if (types.isSubtype(thentype, elsetype))
        return elsetype.baseType();
    if (types.isSubtype(elsetype, thentype))
        return thentype.baseType();
    if (!allowBoxing || thentype.hasTag(VOID) || elsetype.hasTag(VOID)) {
        log.error(pos, "neither.conditional.subtype", thentype, elsetype);
        return thentype.baseType();
    }
    // always be possible to infer "Object" if nothing better.
    return types.lub(thentype.baseType(), elsetype.baseType());
}
Also used : Type(org.eclipse.ceylon.langtools.tools.javac.code.Type) TypeTag(org.eclipse.ceylon.langtools.tools.javac.code.TypeTag)

Example 35 with Type

use of org.eclipse.ceylon.langtools.tools.javac.code.Type in project ceylon by eclipse.

the class Attr method checkLambdaCompatible.

/**
 * Lambda compatibility. Check that given return types, thrown types, parameter types
 * are compatible with the expected functional interface descriptor. This means that:
 * (i) parameter types must be identical to those of the target descriptor; (ii) return
 * types must be compatible with the return type of the expected descriptor.
 */
private void checkLambdaCompatible(JCLambda tree, Type descriptor, CheckContext checkContext) {
    Type returnType = checkContext.inferenceContext().asUndetVar(descriptor.getReturnType());
    // the descriptor's return type must be void
    if (tree.getBodyKind() == JCLambda.BodyKind.STATEMENT && tree.canCompleteNormally && !returnType.hasTag(VOID) && returnType != Type.recoveryType) {
        checkContext.report(tree, diags.fragment("incompatible.ret.type.in.lambda", diags.fragment("missing.ret.val", returnType)));
    }
    List<Type> argTypes = checkContext.inferenceContext().asUndetVars(descriptor.getParameterTypes());
    if (!types.isSameTypes(argTypes, TreeInfo.types(tree.params))) {
        checkContext.report(tree, diags.fragment("incompatible.arg.types.in.lambda"));
    }
}
Also used : Type(org.eclipse.ceylon.langtools.tools.javac.code.Type)

Aggregations

Type (org.eclipse.ceylon.langtools.tools.javac.code.Type)164 Symbol (org.eclipse.ceylon.langtools.tools.javac.code.Symbol)46 DeferredType (org.eclipse.ceylon.langtools.tools.javac.comp.DeferredAttr.DeferredType)13 DiagnosticType (org.eclipse.ceylon.langtools.tools.javac.util.JCDiagnostic.DiagnosticType)13 ArrayType (org.eclipse.ceylon.langtools.tools.javac.code.Type.ArrayType)10 MethodType (org.eclipse.ceylon.langtools.tools.javac.code.Type.MethodType)10 ClassSymbol (org.eclipse.ceylon.langtools.tools.javac.code.Symbol.ClassSymbol)9 JCTree (org.eclipse.ceylon.langtools.tools.javac.tree.JCTree)9 MethodSymbol (org.eclipse.ceylon.langtools.tools.javac.code.Symbol.MethodSymbol)8 JavaFileObject (org.eclipse.ceylon.javax.tools.JavaFileObject)7 TypeSymbol (org.eclipse.ceylon.langtools.tools.javac.code.Symbol.TypeSymbol)7 DiagnosticPosition (org.eclipse.ceylon.langtools.tools.javac.util.JCDiagnostic.DiagnosticPosition)7 JavacType (org.eclipse.ceylon.compiler.java.loader.mirror.JavacType)5 PackageSymbol (org.eclipse.ceylon.langtools.tools.javac.code.Symbol.PackageSymbol)5 FunctionalInterfaceType (org.eclipse.ceylon.model.loader.mirror.FunctionalInterfaceType)5 UnknownType (org.eclipse.ceylon.model.typechecker.model.UnknownType)5 HashSet (java.util.HashSet)4 CompletionFailure (org.eclipse.ceylon.langtools.tools.javac.code.Symbol.CompletionFailure)4 VarSymbol (org.eclipse.ceylon.langtools.tools.javac.code.Symbol.VarSymbol)4 FreeTypeListener (org.eclipse.ceylon.langtools.tools.javac.comp.Infer.FreeTypeListener)4