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);
}
}
}
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);
}
}
}
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);
}
}
}
}
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());
}
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"));
}
}
Aggregations