blob: bd429fbfe18ab1e88b77949df43a3f510d98498f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2014 Wind River Systems, Inc. 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:
* Markus Schorn - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics;
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.IASTUnaryExpression.op_alignOf;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_amper;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_minus;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_noexcept;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_not;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_plus;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_postFixDecr;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_postFixIncr;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_prefixDecr;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_prefixIncr;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_sizeof;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_sizeofParameterPack;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_star;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_throw;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_tilde;
import static org.eclipse.cdt.core.dom.ast.IASTUnaryExpression.op_typeid;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExpressionTypes.glvalueType;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExpressionTypes.prvalueType;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExpressionTypes.prvalueTypeWithResolvedTypedefs;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.ExpressionTypes.valueCategoryFromFunctionCall;
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 org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IArrayType;
import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IPointerType;
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
import org.eclipse.cdt.core.dom.ast.ISemanticProblem;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMember;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameterMap;
import org.eclipse.cdt.internal.core.dom.parser.DependentValue;
import org.eclipse.cdt.internal.core.dom.parser.ITypeMarshalBuffer;
import org.eclipse.cdt.internal.core.dom.parser.IntegralValue;
import org.eclipse.cdt.internal.core.dom.parser.ProblemType;
import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator;
import org.eclipse.cdt.internal.core.dom.parser.SizeofCalculator.SizeAndAlignment;
import org.eclipse.cdt.internal.core.dom.parser.ValueFactory;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPArithmeticConversion;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPClosureType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPFunction;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitFunction;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerToMemberType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPPointerType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.InstantiationContext;
import org.eclipse.cdt.internal.core.dom.parser.cpp.OverloadableOperator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics.LookupMode;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalUtil.Pair;
import org.eclipse.core.runtime.CoreException;
public class EvalUnary extends CPPDependentEvaluation {
private static final ICPPEvaluation ZERO_EVAL = new EvalFixed(CPPSemantics.INT_TYPE, PRVALUE, IntegralValue.create(0));
private final int fOperator;
private final ICPPEvaluation fArgument;
private final IBinding fAddressOfQualifiedNameBinding;
private ICPPFunction fOverload= CPPFunction.UNINITIALIZED_FUNCTION;
private IType fType;
private boolean fCheckedIsConstantExpression;
private boolean fIsConstantExpression;
public EvalUnary(int operator, ICPPEvaluation operand, IBinding addressOfQualifiedNameBinding,
IASTNode pointOfDefinition) {
this(operator, operand, addressOfQualifiedNameBinding, findEnclosingTemplate(pointOfDefinition));
}
public EvalUnary(int operator, ICPPEvaluation operand, IBinding addressOfQualifiedNameBinding,
IBinding templateDefinition) {
super(templateDefinition);
fOperator= operator;
fArgument= operand;
fAddressOfQualifiedNameBinding= addressOfQualifiedNameBinding;
}
public int getOperator() {
return fOperator;
}
public ICPPEvaluation getArgument() {
return fArgument;
}
public IBinding getAddressOfQualifiedNameBinding() {
return fAddressOfQualifiedNameBinding;
}
@Override
public boolean isInitializerList() {
return false;
}
@Override
public boolean isFunctionSet() {
return false;
}
@Override
public boolean isTypeDependent() {
if (fType != null)
return fType instanceof TypeOfDependentExpression;
switch (fOperator) {
case op_alignOf:
case op_not:
case op_sizeof:
case op_sizeofParameterPack:
case op_throw:
case op_typeid:
return false;
default:
return fArgument.isTypeDependent();
}
}
@Override
public boolean isValueDependent() {
switch (fOperator) {
case op_alignOf:
case op_sizeof:
case op_sizeofParameterPack:
case op_typeid:
return fArgument.isTypeDependent();
case op_noexcept:
return fArgument.referencesTemplateParameter();
case op_throw:
return false;
default:
return fArgument.isValueDependent();
}
}
@Override
public boolean isConstantExpression() {
if (!fCheckedIsConstantExpression) {
fCheckedIsConstantExpression = true;
fIsConstantExpression = computeIsConstantExpression();
}
return fIsConstantExpression;
}
private boolean computeIsConstantExpression() {
return fArgument.isConstantExpression()
&& isNullOrConstexprFunc(getOverload());
}
@Override
public boolean isEquivalentTo(ICPPEvaluation other) {
if (!(other instanceof EvalUnary)) {
return false;
}
EvalUnary o = (EvalUnary) other;
return fOperator == o.fOperator
&& fArgument.isEquivalentTo(o.fArgument);
}
public ICPPFunction getOverload() {
if (fOverload == CPPFunction.UNINITIALIZED_FUNCTION) {
fOverload= computeOverload();
}
return fOverload;
}
private ICPPFunction computeOverload() {
OverloadableOperator op = OverloadableOperator.fromUnaryExpression(fOperator);
if (op == null)
return null;
if (fArgument.isTypeDependent())
return null;
if (fAddressOfQualifiedNameBinding instanceof ICPPMember) {
ICPPMember member= (ICPPMember) fAddressOfQualifiedNameBinding;
if (!member.isStatic())
return null;
}
IType type = fArgument.getType();
type = SemanticUtil.getNestedType(type, TDEF | REF | CVTYPE);
if (!CPPSemantics.isUserDefined(type))
return null;
ICPPEvaluation[] args;
if (fOperator == op_postFixDecr || fOperator == op_postFixIncr) {
args = new ICPPEvaluation[] { fArgument, ZERO_EVAL };
} else {
args = new ICPPEvaluation[] { fArgument };
}
return CPPSemantics.findOverloadedOperator(getTemplateDefinitionScope(), args, type,
op, LookupMode.LIMITED_GLOBALS);
}
@Override
public IType getType() {
if (fType == null)
fType= computeType();
return fType;
}
private IType computeType() {
if (isTypeDependent())
return new TypeOfDependentExpression(this);
ICPPFunction overload = getOverload();
if (overload != null)
return ExpressionTypes.typeFromFunctionCall(overload);
switch (fOperator) {
case op_sizeof:
case op_sizeofParameterPack:
return CPPVisitor.get_SIZE_T();
case op_typeid:
return CPPVisitor.get_type_info();
case op_throw:
return CPPSemantics.VOID_TYPE;
case op_amper:
if (fAddressOfQualifiedNameBinding instanceof ICPPMember) {
ICPPMember member= (ICPPMember) fAddressOfQualifiedNameBinding;
if (!member.isStatic()) {
try {
return new CPPPointerToMemberType(member.getType(), member.getClassOwner(),
false, false, false);
} catch (DOMException e) {
return e.getProblem();
}
}
}
return new CPPPointerType(fArgument.getType());
case op_star:
IType type= fArgument.getType();
type = prvalueTypeWithResolvedTypedefs(type);
if (type instanceof IPointerType) {
return glvalueType(((IPointerType) type).getType());
}
if (type instanceof ISemanticProblem) {
return type;
}
return ProblemType.UNKNOWN_FOR_EXPRESSION;
case op_noexcept:
case op_not:
return CPPBasicType.BOOLEAN;
case op_postFixDecr:
case op_postFixIncr:
return prvalueType(fArgument.getType());
case op_plus:
return promoteType(fArgument.getType(), true);
case op_minus:
case op_tilde:
return promoteType(fArgument.getType(), false);
}
return fArgument.getType();
}
private IType promoteType(IType type, boolean allowPointer) {
final IType t1 = prvalueType(type);
final IType t2 = SemanticUtil.getNestedType(t1, TDEF);
if (allowPointer) {
if (t2 instanceof CPPClosureType) {
ICPPMethod conversionOperator = ((CPPClosureType) t2).getConversionOperator();
if (conversionOperator == null)
return ProblemType.UNKNOWN_FOR_EXPRESSION;
return new CPPPointerType(conversionOperator.getType().getReturnType());
}
if (t2 instanceof IPointerType) {
return t1;
}
}
final IType t3= CPPArithmeticConversion.promoteCppType(t2);
if (t3 == null && !(t2 instanceof IBasicType))
return ProblemType.UNKNOWN_FOR_EXPRESSION;
return (t3 == null || t3 == t2) ? t1 : t3;
}
@Override
public IValue getValue() {
if (isValueDependent())
return DependentValue.create(this);
ICPPEvaluation arg = fArgument;
ICPPFunction overload = getOverload();
if (overload != null) {
ICPPFunctionType functionType = overload.getType();
IType[] parameterTypes = functionType.getParameterTypes();
if (parameterTypes.length == 0)
return IntegralValue.ERROR;
IType targetType = parameterTypes[0];
arg = maybeApplyConversion(arg, targetType, fOperator == op_not);
if (!(overload instanceof CPPImplicitFunction)) {
if (!overload.isConstexpr())
return IntegralValue.ERROR;
ICPPEvaluation eval = new EvalBinding(overload, null, (IBinding) null);
arg = new EvalFunctionCall(new ICPPEvaluation[] {eval, arg}, null, (IBinding) null);
return arg.getValue();
}
}
switch (fOperator) {
case op_sizeof: {
SizeAndAlignment info =
SizeofCalculator.getSizeAndAlignment(fArgument.getType());
return info == null ? IntegralValue.UNKNOWN : IntegralValue.create(info.size);
}
case op_alignOf: {
SizeAndAlignment info =
SizeofCalculator.getSizeAndAlignment(fArgument.getType());
return info == null ? IntegralValue.UNKNOWN : IntegralValue.create(info.alignment);
}
case op_noexcept:
return IntegralValue.UNKNOWN; // TODO(sprigogin): Implement
case op_sizeofParameterPack:
IValue opVal = fArgument.getValue();
return IntegralValue.create(opVal.numberOfSubValues());
case op_typeid:
return IntegralValue.UNKNOWN; // TODO(sprigogin): Implement
case op_throw:
return IntegralValue.UNKNOWN; // TODO(sprigogin): Implement
}
IValue val = arg.getValue();
if (val == null)
return IntegralValue.UNKNOWN;
return ValueFactory.evaluateUnaryExpression(fOperator, val);
}
@Override
public ValueCategory getValueCategory() {
ICPPFunction overload = getOverload();
if (overload != null)
return valueCategoryFromFunctionCall(overload);
switch (fOperator) {
case op_typeid:
case op_star:
case op_prefixDecr:
case op_prefixIncr:
return LVALUE;
default:
return PRVALUE;
}
}
@Override
public void marshal(ITypeMarshalBuffer buffer, boolean includeValue) throws CoreException {
buffer.putShort(ITypeMarshalBuffer.EVAL_UNARY);
buffer.putByte((byte) fOperator);
buffer.marshalEvaluation(fArgument, includeValue);
buffer.marshalBinding(fAddressOfQualifiedNameBinding);
marshalTemplateDefinition(buffer);
}
public static ICPPEvaluation unmarshal(short firstBytes, ITypeMarshalBuffer buffer)
throws CoreException {
int op= buffer.getByte();
ICPPEvaluation arg= buffer.unmarshalEvaluation();
IBinding binding= buffer.unmarshalBinding();
IBinding templateDefinition= buffer.unmarshalBinding();
return new EvalUnary(op, arg, binding, templateDefinition);
}
@Override
public ICPPEvaluation instantiate(InstantiationContext context, int maxDepth) {
ICPPEvaluation argument = fArgument.instantiate(context, maxDepth);
IBinding binding = fAddressOfQualifiedNameBinding;
if (binding instanceof ICPPUnknownBinding) {
try {
binding= CPPTemplates.resolveUnknown((ICPPUnknownBinding) binding, context);
} catch (DOMException e) {
}
}
if (argument == fArgument && binding == fAddressOfQualifiedNameBinding)
return this;
if (binding instanceof IProblemBinding)
return EvalFixed.INCOMPLETE;
return new EvalUnary(fOperator, argument, binding, getTemplateDefinition());
}
private ICPPEvaluation createOperatorOverloadEvaluation(ICPPFunction overload, ICPPEvaluation arg) {
IBinding templateDefinition = getTemplateDefinition();
if (overload instanceof ICPPMethod) {
EvalMemberAccess opAccess = new EvalMemberAccess(arg.getType(), ValueCategory.LVALUE, overload,
arg, false, templateDefinition);
ICPPEvaluation[] args = new ICPPEvaluation[]{opAccess};
return new EvalFunctionCall(args, arg, templateDefinition);
} else {
EvalBinding op = new EvalBinding(overload, overload.getType(), templateDefinition);
ICPPEvaluation[] args = new ICPPEvaluation[]{op, arg};
return new EvalFunctionCall(args, null, templateDefinition);
}
}
private static boolean isModifyingOperation(int op) {
return op == op_prefixIncr || op == op_prefixDecr || op == op_postFixIncr || op == op_postFixDecr;
}
@Override
public ICPPEvaluation computeForFunctionCall(ActivationRecord record, ConstexprEvaluationContext context) {
ICPPFunction overload = getOverload();
if (overload != null) {
ICPPEvaluation operatorCall = createOperatorOverloadEvaluation(overload, fArgument);
ICPPEvaluation eval = operatorCall.computeForFunctionCall(record, context);
return eval;
}
Pair<ICPPEvaluation, ICPPEvaluation> vp = EvalUtil.getValuePair(fArgument, record, context);
final ICPPEvaluation updateable = vp.getFirst();
final ICPPEvaluation fixed = vp.getSecond();
ICPPEvaluation evalUnary = fixed == fArgument || fixed == EvalFixed.INCOMPLETE ? this : new EvalUnary(fOperator, fixed, fAddressOfQualifiedNameBinding, getTemplateDefinition());
if (fOperator == op_star) {
if (fixed instanceof EvalPointer) {
EvalPointer evalPointer = (EvalPointer) fixed;
return evalPointer.dereference();
} else if (updateable instanceof EvalBinding && isStarOperatorOnArrayName(context)) {
EvalBinding evalBinding = (EvalBinding) updateable;
IBinding binding = evalBinding.getBinding();
ICPPEvaluation eval = record.getVariable(binding);
if (eval == null)
return EvalFixed.INCOMPLETE;
EvalCompositeAccess compositeAccess = new EvalCompositeAccess(eval, 0);
return new EvalReference(record, compositeAccess, getTemplateDefinition());
}
return evalUnary;
} else if (fOperator == op_amper) {
if (updateable instanceof EvalBinding) {
EvalBinding evalBinding = (EvalBinding) updateable;
IBinding binding = evalBinding.getBinding();
return new EvalPointer(record, binding, getTemplateDefinition());
} else if (updateable instanceof EvalReference) {
EvalReference evalRef = (EvalReference) updateable;
return EvalPointer.createFromAddress(evalRef);
}
return evalUnary;
} else if (isModifyingOperation(fOperator)) {
if (fixed instanceof EvalPointer) {
EvalPointer evalPointer = (EvalPointer) fixed;
applyPointerArithmetics(evalPointer);
return evalPointer;
} else {
EvalFixed newValue = new EvalFixed(evalUnary.getType(), evalUnary.getValueCategory(), evalUnary.getValue());
if (updateable instanceof EvalReference) {
EvalReference evalRef = (EvalReference) updateable;
evalRef.update(newValue);
} else if (updateable instanceof EvalCompositeAccess) {
EvalCompositeAccess evalCompAccess = (EvalCompositeAccess) updateable;
evalCompAccess.update(newValue);
} else if (updateable instanceof EvalBinding) {
EvalBinding evalBinding = (EvalBinding) updateable;
IBinding binding = evalBinding.getBinding();
record.update(binding, newValue);
}
if (this.getValueCategory() == ValueCategory.LVALUE) {
return updateable;
} else {
return fixed;
}
}
} else {
return evalUnary;
}
}
private boolean isStarOperatorOnArrayName(ConstexprEvaluationContext context) {
return fOperator == op_star && fArgument.getType() instanceof IArrayType;
}
private void applyPointerArithmetics(EvalPointer poiner) {
switch (fOperator) {
case op_postFixIncr:
case op_prefixIncr:
poiner.setPosition(poiner.getPosition() + 1);
break;
case op_postFixDecr:
case op_prefixDecr:
poiner.setPosition(poiner.getPosition() - 1);
break;
}
}
@Override
public int determinePackSize(ICPPTemplateParameterMap tpMap) {
return fArgument.determinePackSize(tpMap);
}
@Override
public boolean referencesTemplateParameter() {
return fArgument.referencesTemplateParameter();
}
}