/******************************************************************************* * Copyright (c) 2004, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * John Camelon (IBM) - Initial API and implementation * Markus Schorn (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser.cpp; import static org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory.LVALUE; import static org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory.PRVALUE; import static org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory.XVALUE; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExpressionTypes.prvalueType; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.CVTYPE; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.REF; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.TDEF; import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.getNestedType; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression; import org.eclipse.cdt.core.dom.ast.IASTConditionalExpression; import org.eclipse.cdt.core.dom.ast.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression; import org.eclipse.cdt.core.dom.ast.IBasicType.Kind; import org.eclipse.cdt.core.dom.ast.ISemanticProblem; import org.eclipse.cdt.core.dom.ast.IType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPBasicType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction; import org.eclipse.cdt.internal.core.dom.parser.ASTNode; import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguityParent; import org.eclipse.cdt.internal.core.dom.parser.ProblemType; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CVQualifier; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Conversions; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Conversions.Context; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Conversions.UDCMode; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Cost; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Cost.Rank; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExpressionTypes; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil; public class CPPASTConditionalExpression extends ASTNode implements IASTConditionalExpression, IASTAmbiguityParent { private IASTExpression fCondition; private IASTExpression fPositive; private IASTExpression fNegative; private IType fType; private ValueCategory fValueCategory; public CPPASTConditionalExpression() { } public CPPASTConditionalExpression(IASTExpression condition, IASTExpression postive, IASTExpression negative) { setLogicalConditionExpression(condition); setPositiveResultExpression(postive); setNegativeResultExpression(negative); } public CPPASTConditionalExpression copy() { return copy(CopyStyle.withoutLocations); } public CPPASTConditionalExpression copy(CopyStyle style) { CPPASTConditionalExpression copy = new CPPASTConditionalExpression(); copy.setLogicalConditionExpression(fCondition == null ? null : fCondition.copy(style)); copy.setPositiveResultExpression(fPositive == null ? null : fPositive.copy(style)); copy.setNegativeResultExpression(fNegative == null ? null : fNegative.copy(style)); copy.setOffsetAndLength(this); if (style == CopyStyle.withLocations) { copy.setCopyLocation(this); } return copy; } public IASTExpression getLogicalConditionExpression() { return fCondition; } public void setLogicalConditionExpression(IASTExpression expression) { assertNotFrozen(); fCondition = expression; if (expression != null) { expression.setParent(this); expression.setPropertyInParent(LOGICAL_CONDITION); } } public IASTExpression getPositiveResultExpression() { return fPositive; } public void setPositiveResultExpression(IASTExpression expression) { assertNotFrozen(); this.fPositive = expression; if (expression != null) { expression.setParent(this); expression.setPropertyInParent(POSITIVE_RESULT); } } public IASTExpression getNegativeResultExpression() { return fNegative; } public void setNegativeResultExpression(IASTExpression expression) { assertNotFrozen(); this.fNegative = expression; if (expression != null) { expression.setParent(this); expression.setPropertyInParent(NEGATIVE_RESULT); } } @Override public boolean accept(ASTVisitor action) { if (action.shouldVisitExpressions) { switch (action.visit(this)) { case ASTVisitor.PROCESS_ABORT: return false; case ASTVisitor.PROCESS_SKIP: return true; default: break; } } if (fCondition != null && !fCondition.accept(action)) return false; if (fPositive != null && !fPositive.accept(action)) return false; if (fNegative != null && !fNegative.accept(action)) return false; if (action.shouldVisitExpressions && action.leave(this) == ASTVisitor.PROCESS_ABORT) return false; return true; } public void replace(IASTNode child, IASTNode other) { if (child == fCondition) { other.setPropertyInParent(child.getPropertyInParent()); other.setParent(child.getParent()); fCondition = (IASTExpression) other; } if (child == fPositive) { other.setPropertyInParent(child.getPropertyInParent()); other.setParent(child.getParent()); fPositive = (IASTExpression) other; } if (child == fNegative) { other.setPropertyInParent(child.getPropertyInParent()); other.setParent(child.getParent()); fNegative = (IASTExpression) other; } } public IType getExpressionType() { evaluate(); return fType; } public ValueCategory getValueCategory() { evaluate(); return fValueCategory; } public boolean isLValue() { return getValueCategory() == LVALUE; } private void evaluate() { if (fValueCategory != null) return; fValueCategory= PRVALUE; // Gnu-extension: Empty positive expression is replaced by condition. IASTExpression expr2 = getPositiveResultExpression(); final IASTExpression expr3 = getNegativeResultExpression(); if (expr2 == null) { expr2= getLogicalConditionExpression(); } IType t2 = expr2.getExpressionType(); IType t3 = expr3.getExpressionType(); if (t2 == null || t3 == null) { fType= new ProblemType(ISemanticProblem.TYPE_UNKNOWN_FOR_EXPRESSION); return; } final IType uqt2= getNestedType(t2, TDEF | REF | CVTYPE); final IType uqt3= getNestedType(t3, TDEF | REF | CVTYPE); if (uqt2 instanceof ISemanticProblem || uqt2 instanceof ICPPUnknownType) { fType= uqt2; return; } if (uqt3 instanceof ISemanticProblem || uqt3 instanceof ICPPUnknownType) { fType= uqt3; return; } final boolean void2= isVoidType(uqt2); final boolean void3= isVoidType(uqt3); // Void types: Either both are void or one is a throw expression. if (void2 || void3) { if (isThrowExpression(expr2)) { fType= Conversions.lvalue_to_rvalue(t3); } else if (isThrowExpression(expr3)) { fType= Conversions.lvalue_to_rvalue(t2); } else if (void2 && void3) { fType= uqt2; } else { fType= new ProblemType(ISemanticProblem.TYPE_UNKNOWN_FOR_EXPRESSION); } return; } final ValueCategory vcat2= expr2.getValueCategory(); final ValueCategory vcat3= expr3.getValueCategory(); // Same type if (t2.isSameType(t3)) { if (vcat2 == vcat3) { fType= t2; fValueCategory= vcat2; } else { fType= prvalueType(t2); fValueCategory= PRVALUE; } return; } final boolean isClassType2 = uqt2 instanceof ICPPClassType; final boolean isClassType3 = uqt3 instanceof ICPPClassType; // Different types with at least one class type if (isClassType2 || isClassType3) { final Cost cost2= convertToMatch(t2, vcat2, uqt2, t3, vcat3, uqt3); // sets fType and fValueCategory final Cost cost3= convertToMatch(t3, vcat3, uqt3, t2, vcat2, uqt2); // sets fType and fValueCategory if (cost2.converts() || cost3.converts()) { if (cost2.converts()) { if (cost3.converts() || cost2.isAmbiguousUDC()) { fType= new ProblemType(ISemanticProblem.TYPE_UNKNOWN_FOR_EXPRESSION); } } else if (cost3.isAmbiguousUDC()) { fType= new ProblemType(ISemanticProblem.TYPE_UNKNOWN_FOR_EXPRESSION); } return; } } else if (vcat2 == vcat3 && vcat2.isGLValue() && uqt2.isSameType(uqt3)) { // Two lvalues or two xvalues with same type up to qualification. final CVQualifier cv2 = SemanticUtil.getCVQualifier(t2); final CVQualifier cv3 = SemanticUtil.getCVQualifier(t3); if (cv2.isAtLeastAsQualifiedAs(cv3)) { fType= t2; fValueCategory= vcat2; } else if (cv3.isAtLeastAsQualifiedAs(cv2)) { fType= t3; fValueCategory= vcat3; } else { fType= new ProblemType(ISemanticProblem.TYPE_UNKNOWN_FOR_EXPRESSION); } return; } // 5.16-5: At least one class type but no conversion if (isClassType2 || isClassType3) { ICPPFunction builtin = CPPSemantics.findOverloadedConditionalOperator(expr2, expr3); if (builtin != null) { fType= ExpressionTypes.typeFromFunctionCall(builtin); } else { fType= new ProblemType(ISemanticProblem.TYPE_UNKNOWN_FOR_EXPRESSION); } return; } // 5.16-6 t2= Conversions.lvalue_to_rvalue(t2); t3= Conversions.lvalue_to_rvalue(t3); if (t2.isSameType(t3)) { fType= t2; } else { fType= CPPArithmeticConversion.convertCppOperandTypes(IASTBinaryExpression.op_plus, t2, t3); if (fType == null) { fType= Conversions.compositePointerType(t2, t3); if (fType == null) { fType= new ProblemType(ISemanticProblem.TYPE_UNKNOWN_FOR_EXPRESSION); } } } } private boolean isThrowExpression(IASTExpression expr) { while (expr instanceof IASTUnaryExpression) { final IASTUnaryExpression unaryExpr = (IASTUnaryExpression) expr; final int op = unaryExpr.getOperator(); if (op == IASTUnaryExpression.op_throw) { return true; } else if (op == IASTUnaryExpression.op_bracketedPrimary) { expr= unaryExpr.getOperand(); } else { return false; } } return false; } private Cost convertToMatch(IType t1, ValueCategory vcat1, IType uqt1, IType t2, ValueCategory vcat2, IType uqt2) { // E2 is an lvalue or E2 is an xvalue try { if (vcat2.isGLValue()) { IType target= new CPPReferenceType(t2, vcat2 == XVALUE); Cost c= Conversions.checkImplicitConversionSequence(target, t1, vcat1, UDCMode.ALLOWED, Context.REQUIRE_DIRECT_BINDING); if (c.converts()) { fType= t2; fValueCategory= vcat2; return c; } } // Both are class types and one derives from the other if (uqt1 instanceof ICPPClassType && uqt2 instanceof ICPPClassType) { int dist= SemanticUtil.calculateInheritanceDepth(uqt1, uqt2); if (dist >= 0) { CVQualifier cv1 = SemanticUtil.getCVQualifier(t1); CVQualifier cv2 = SemanticUtil.getCVQualifier(t2); if (cv2.isAtLeastAsQualifiedAs(cv1)) { fType= t2; fValueCategory= PRVALUE; return new Cost(t1, t2, Rank.IDENTITY); } return Cost.NO_CONVERSION; } if (SemanticUtil.calculateInheritanceDepth(uqt2, uqt1) >= 0) return Cost.NO_CONVERSION; } // Unrelated class types or just one class: if (vcat2 != PRVALUE) { t2= Conversions.lvalue_to_rvalue(t2); } Cost c= Conversions.checkImplicitConversionSequence(t2, t1, vcat1, UDCMode.ALLOWED, Context.ORDINARY); if (c.converts()) { fType= t2; fValueCategory= PRVALUE; return c; } } catch (DOMException e) { } return Cost.NO_CONVERSION; } private boolean isVoidType(IType t) { return t instanceof ICPPBasicType && ((ICPPBasicType) t).getKind() == Kind.eVoid; } }