blob: c5678eb878219f43e10fedbf96c3e014ac43cdc4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2016 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
*******************************************************************************/
package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.ALLCVQ;
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 java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.IBasicType.Kind;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IFunctionType;
import org.eclipse.cdt.core.dom.ast.IPointerType;
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.ISemanticProblem;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPEnumeration;
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.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPPointerToMemberType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPReferenceType;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
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.CPPBuiltinParameter;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPFunctionType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitFunction;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPReferenceType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPScope;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.OverloadableOperator;
/**
* Generates built-in operators according to 13.6
*/
class BuiltinOperators {
private static final ICPPFunction[] EMPTY = {};
private static final int FIRST = 0;
private static final int SECOND = 1;
private static final IType PTR_DIFF = new CPPBasicType(Kind.eInt, 0);
public static ICPPFunction[] create(OverloadableOperator operator, ICPPEvaluation[] args,
Object[] globCandidates) {
if (operator == null || args == null || args.length == 0)
return EMPTY;
return new BuiltinOperators(operator, args, globCandidates).create();
}
private final OverloadableOperator fOperator;
private final boolean fUnary;
private IType fType1;
private IType fType2;
private IType[][] fClassConversionTypes= { null, null };
private boolean[] fIsClass= { false, false };
private IScope fFileScope;
private List<ICPPFunction> fResult;
private Set<String> fSignatures;
private Object[] fGlobalCandidates;
BuiltinOperators(OverloadableOperator operator, ICPPEvaluation[] args,
Object[] globCandidates) {
IASTNode point = CPPSemantics.getCurrentLookupPoint();
fFileScope= point == null ?
new CPPScope.CPPScopeProblem(null, IProblemBinding.SEMANTIC_BAD_SCOPE) :
point.getTranslationUnit().getScope();
fOperator= operator;
fUnary= args.length < 2;
fGlobalCandidates= globCandidates;
if (args.length > 0) {
IType type= args[0].getType();
if (!(type instanceof ISemanticProblem))
fType1= type;
}
if (args.length > 1) {
IType type= args[1].getType();
if (!(type instanceof ISemanticProblem))
fType2= type;
}
}
private ICPPFunction[] create() {
switch (fOperator) {
case ARROW:
case COMMA:
case DELETE:
case DELETE_ARRAY:
case NEW:
case NEW_ARRAY:
case PAREN:
return EMPTY;
case INCR:
case DECR:
opIncOrDec();
break;
case STAR:
if (fUnary) {
opDeref();
} else {
binaryPromotedArithmetic(true, ReturnType.CONVERT);
}
break;
case DIV:
binaryPromotedArithmetic(true, ReturnType.CONVERT);
break;
case PLUS:
if (fUnary) {
unaryPointer();
unaryPromotedArithmetic(true);
} else {
binaryPromotedArithmetic(true, ReturnType.CONVERT);
pointerArithmetic(false, false);
}
break;
case BRACKET:
pointerArithmetic(true, false);
break;
case MINUS:
if (fUnary) {
unaryPromotedArithmetic(true);
} else {
binaryPromotedArithmetic(true, ReturnType.CONVERT);
pointerArithmetic(false, true);
}
break;
case BITCOMPLEMENT:
unaryPromotedArithmetic(false);
break;
case ARROWSTAR:
opArrowStar();
break;
case EQUAL:
case NOTEQUAL:
binaryPromotedArithmetic(true, ReturnType.USE_BOOL);
comparison(true);
break;
case GT:
case GTEQUAL:
case LT:
case LTEQUAL:
binaryPromotedArithmetic(true, ReturnType.USE_BOOL);
comparison(false);
break;
case AMPER:
if (fUnary)
return EMPTY;
binaryPromotedArithmetic(false, ReturnType.CONVERT);
break;
case BITOR:
case XOR:
case MOD:
binaryPromotedArithmetic(false, ReturnType.CONVERT);
break;
case SHIFTL:
case SHIFTR:
binaryPromotedArithmetic(false, ReturnType.USE_FIRST);
break;
case ASSIGN:
arithmeticAssignement(true, Assignment.WITHOUT_OPERATION);
break;
case MINUSASSIGN:
case PLUSASSIGN:
arithmeticAssignement(true, Assignment.WITH_POINTER_OPERATION);
break;
case DIVASSIGN:
case STARASSIGN:
arithmeticAssignement(true, Assignment.WITH_OPERATION);
break;
case AMPERASSIGN:
case BITORASSIGN:
case MODASSIGN:
case SHIFTLASSIGN:
case SHIFTRASSIGN:
case XORASSIGN:
arithmeticAssignement(false, Assignment.WITH_OPERATION);
break;
case AND:
case OR:
addFunction(CPPBasicType.BOOLEAN, CPPBasicType.BOOLEAN, CPPBasicType.BOOLEAN);
break;
case NOT:
addFunction(CPPBasicType.BOOLEAN, CPPBasicType.BOOLEAN);
break;
case CONDITIONAL_OPERATOR:
binaryPromotedArithmetic(true, ReturnType.CONVERT);
conditional();
break;
}
if (fResult == null)
return EMPTY;
return fResult.toArray(new ICPPFunction[fResult.size()]);
}
// 13.6-3, 13.6-4, 13.6-5
private void opIncOrDec() {
IType[] types= getClassConversionTypes(FIRST);
for (IType type : types) {
type= SemanticUtil.getNestedType(type, TDEF);
if (type instanceof ICPPReferenceType) {
IType nested= ((ICPPReferenceType) type).getType();
CVQualifier cvq= SemanticUtil.getCVQualifier(nested);
if (!cvq.isConst()) {
nested= SemanticUtil.getNestedType(nested, TDEF | CVTYPE);
boolean ok= false;
if (isArithmetic(nested)) {
// 13.6-3 and 1.3.6-4
if (fOperator == OverloadableOperator.INCR || !isBoolean(type)) {
ok= true;
}
} else if (isPointer(nested)) {
// 13.6-5
nested= ((IPointerType) nested).getType();
if (!(SemanticUtil.getNestedType(nested, TDEF) instanceof IFunctionType)) {
ok= true;
}
}
if (ok) {
if (fType2 != null) {
addFunction(type, type, fType2);
} else {
addFunction(type, type);
}
}
}
}
}
}
// 13.6-6, 13.6-7
private void opDeref() {
IType[] types= getClassConversionTypes(FIRST);
for (IType type : types) {
type= SemanticUtil.getNestedType(type, TDEF|REF);
if (isPointer(type)) {
IType nested= SemanticUtil.getNestedType(((IPointerType) type).getType(), TDEF);
if (nested instanceof ICPPFunctionType) {
ICPPFunctionType ft= (ICPPFunctionType) nested;
if (ft.isConst() || ft.isVolatile())
return;
}
addFunction(new CPPReferenceType(nested, false), type);
}
}
}
// 13.6-8
private void unaryPointer() {
IType[] types= getClassConversionTypes(FIRST);
for (IType type : types) {
type= SemanticUtil.getNestedType(type, TDEF|REF);
if (isPointer(type)) {
addFunction(type, type);
}
}
}
// 13.6-9, 13.6-10
private void unaryPromotedArithmetic(boolean includeFloatingPoint) {
IType[] types= getClassConversionTypes(FIRST);
for (IType type : types) {
type= SemanticUtil.getNestedType(type, TDEF|REF|CVTYPE);
if (isFloatingPoint(type)) {
if (includeFloatingPoint) {
addFunction(type, type);
}
} else {
type= CPPArithmeticConversion.promoteCppType(type);
if (type != null) {
addFunction(type, type);
}
}
}
}
// 13.6-11
private void opArrowStar() {
List<IPointerType> classPointers= null;
List<ICPPPointerToMemberType> memberPointers= null;
IType[] types= getClassConversionTypes(FIRST);
for (IType type : types) {
type= SemanticUtil.getNestedType(type, TDEF | REF);
if (isPointer(type)) {
final IPointerType ptrType = (IPointerType) type;
if (SemanticUtil.getNestedType(ptrType.getType(), TDEF | CVTYPE) instanceof ICPPClassType) {
if (classPointers == null) {
classPointers= new ArrayList<>();
}
classPointers.add(ptrType);
}
}
}
if (classPointers == null)
return;
types= getClassConversionTypes(SECOND);
for (IType type : types) {
type= SemanticUtil.getNestedType(type, TDEF | REF);
if (type instanceof ICPPPointerToMemberType) {
if (memberPointers == null) {
memberPointers= new ArrayList<>();
}
memberPointers.add((ICPPPointerToMemberType) type);
}
}
if (memberPointers == null)
return;
for (IPointerType clsPtr : classPointers) {
IType cvClass= SemanticUtil.getNestedType(clsPtr.getType(), TDEF);
CVQualifier cv1= SemanticUtil.getCVQualifier(cvClass);
ICPPClassType c1= (ICPPClassType) SemanticUtil.getNestedType(cvClass, TDEF | CVTYPE);
for (ICPPPointerToMemberType memPtr : memberPointers) {
IType t2= SemanticUtil.getNestedType(memPtr.getMemberOfClass(), TDEF);
if (t2 instanceof ICPPClassType) {
ICPPClassType c2= (ICPPClassType) t2;
if (SemanticUtil.calculateInheritanceDepth(c1, c2) >= 0) {
IType cvt= SemanticUtil.getNestedType(memPtr.getType(), TDEF);
IType rt= new CPPReferenceType(
SemanticUtil.addQualifiers(cvt, cv1.isConst(), cv1.isVolatile(), cv1.isRestrict()), false);
addFunction(rt, clsPtr, memPtr);
}
}
}
}
}
// 13.6-12, 13.6-17
private static enum ReturnType {CONVERT, USE_FIRST, USE_BOOL}
private void binaryPromotedArithmetic(boolean fltPt, ReturnType rstrat) {
List<IType> p1= null, p2= null;
IType[] types1= getClassConversionTypes(FIRST);
IType[] types2= getClassConversionTypes(SECOND);
if (types1.length == 0 && types2.length == 0)
return;
for (IType t : types1) {
p1 = addPromotedArithmetic(t, fltPt, p1);
}
for (IType t : types2) {
p2 = addPromotedArithmetic(t, fltPt, p2);
}
p1= addPromotedArithmetic(fType1, fltPt, p1);
p2= addPromotedArithmetic(fType2, fltPt, p2);
if (p1 == null || p2 == null)
return;
for (IType t1 : p1) {
for (IType t2 : p2) {
IType rt= null;
switch (rstrat) {
case USE_BOOL:
rt= CPPBasicType.BOOLEAN;
break;
case USE_FIRST:
rt= t1;
break;
case CONVERT:
rt= CPPArithmeticConversion.convertCppOperandTypes(IASTBinaryExpression.op_plus, t1, t2);
break;
}
addFunction(rt, t1, t2);
}
}
}
private List<IType> addPromotedArithmetic(IType t, boolean fltPt, List<IType> p1) {
IType type= SemanticUtil.getNestedType(t, TDEF|REF|CVTYPE);
if (isFloatingPoint(type)) {
if (!fltPt) {
type= null;
}
} else {
type= CPPArithmeticConversion.promoteCppType(type);
}
if (type != null) {
if (p1 == null) {
p1= new ArrayList<>();
}
p1.add(type);
}
return p1;
}
// 13.6-13, 13.6.14
private void pointerArithmetic(boolean useRef, boolean isDiff) {
IType[] types= getClassConversionTypes(FIRST);
if (types.length == 0 && !fIsClass[FIRST]) {
types= new IType[] {fType1};
}
for (IType type : types) {
type= SemanticUtil.getNestedType(type, TDEF|REF);
if (isPointer(type)) {
final IType ptrTarget = ((IPointerType) type).getType();
final IType uqPtrTarget = SemanticUtil.getNestedType(ptrTarget, TDEF|CVTYPE);
if (!(uqPtrTarget instanceof IFunctionType)) {
final IType retType= useRef ? new CPPReferenceType(ptrTarget, false) : type;
addFunction(retType, type, PTR_DIFF);
if (isDiff) {
addFunction(PTR_DIFF, type, type);
}
}
}
}
types= getClassConversionTypes(SECOND);
if (types.length == 0 && !fIsClass[SECOND]) {
types= new IType[] {fType2};
}
for (IType type : types) {
type= SemanticUtil.getNestedType(type, TDEF|REF);
if (isPointer(type)) {
final IType ptrTarget = ((IPointerType) type).getType();
final IType uqPtrTarget = SemanticUtil.getNestedType(ptrTarget, TDEF|CVTYPE);
if (!(uqPtrTarget instanceof IFunctionType)) {
if (isDiff) {
addFunction(PTR_DIFF, type, type);
} else {
final IType retType= useRef ? new CPPReferenceType(ptrTarget, false) : type;
addFunction(retType, PTR_DIFF, type);
}
}
}
}
}
// 13.6-15, 13.6.16
private void comparison(boolean ordered) {
for (int i = FIRST; i <= SECOND; i++) {
IType[] types= getClassConversionTypes(i);
for (IType type : types) {
type= SemanticUtil.getNestedType(type, TDEF|REF|CVTYPE);
if (isPointer(type) || isEnumeration(type) || (!ordered && isPointerToMember(type))) {
addFunction(CPPBasicType.BOOLEAN, type, type);
}
}
}
}
// 13.6-18, 13.6-29, 13.6-20, 13.6-22
private static enum Assignment {WITHOUT_OPERATION, WITH_POINTER_OPERATION, WITH_OPERATION}
private void arithmeticAssignement(boolean fltPt, Assignment assign) {
IType[] types2= getClassConversionTypes(SECOND);
if (types2.length == 0)
return;
IType refType= SemanticUtil.getNestedType(fType1, TDEF);
if (refType instanceof ICPPReferenceType) {
IType t= SemanticUtil.getNestedType(((ICPPReferenceType) refType).getType(), TDEF);
if (!SemanticUtil.getCVQualifier(t).isConst()) {
switch (assign) {
case WITHOUT_OPERATION:
if (isEnumeration(t) || isPointerToMember(t) || isPointer(t)) {
addFunction(refType, refType, SemanticUtil.getNestedType(t, TDEF|ALLCVQ));
return;
}
break;
case WITH_POINTER_OPERATION:
if (isPointer(t)) {
addFunction(refType, refType, PTR_DIFF);
return;
}
break;
default:
break;
}
}
if (fltPt ? isArithmetic(t) : isIntegral(t)) {
List<IType> p2= null;
for (IType t2 : types2) {
p2 = addPromotedArithmetic(t2, fltPt, p2);
}
if (p2 != null) {
for (IType t2 : p2) {
addFunction(refType, refType, t2);
}
}
}
}
}
// 13.6-25
private void conditional() {
for (int i = FIRST; i <= SECOND; i++) {
IType[] types= getClassConversionTypes(i);
for (IType type : types) {
type= SemanticUtil.getNestedType(type, TDEF|REF|CVTYPE);
if (isPointer(type) || isScopedEnumeration(type) || isPointerToMember(type)) {
addFunction(type, type, type);
}
}
}
}
private void addFunction(IType returnType, IType p1) {
addFunction(returnType, new IType[] {p1});
}
private void addFunction(IType returnType, IType p1, IType p2) {
addFunction(returnType, new IType[] {p1, p2});
}
private void addFunction(IType returnType, IType[] parameterTypes) {
ICPPParameter[] parameter = new ICPPParameter[parameterTypes.length];
ICPPFunctionType functionType = new CPPFunctionType(returnType, parameterTypes);
String sig= ASTTypeUtil.getType(functionType, true);
if (fSignatures == null) {
fSignatures= new HashSet<>();
if (fGlobalCandidates != null) {
for (Object cand : fGlobalCandidates) {
if (cand instanceof IFunction && !(cand instanceof ICPPMethod)) {
fSignatures.add(ASTTypeUtil.getType(((IFunction) cand).getType(), true));
}
}
}
}
if (fSignatures.add(sig)) {
for (int i = 0; i < parameterTypes.length; i++) {
IType t = parameterTypes[i];
parameter[i]= new CPPBuiltinParameter(t);
}
if (fResult == null) {
fResult= new ArrayList<>();
}
fResult.add(new CPPImplicitFunction(fOperator.toCharArray(), fFileScope, functionType, parameter, true, false));
}
}
private boolean isEnumeration(IType type) {
return type instanceof IEnumeration;
}
private boolean isScopedEnumeration(IType type) {
return type instanceof ICPPEnumeration && ((ICPPEnumeration) type).isScoped();
}
private boolean isPointer(IType type) {
return type instanceof IPointerType && !(type instanceof ICPPPointerToMemberType);
}
private boolean isPointerToMember(IType type) {
return type instanceof ICPPPointerToMemberType;
}
private static boolean isBoolean(IType type) {
return type instanceof IBasicType && ((IBasicType) type).getKind() == Kind.eBoolean;
}
public static boolean isFloatingPoint(IType type) {
if (type instanceof IBasicType) {
IBasicType.Kind kind= ((IBasicType) type).getKind();
switch (kind) {
case eDouble:
case eFloat:
case eFloat128:
case eDecimal32:
case eDecimal64:
case eDecimal128:
return true;
case eBoolean:
case eChar:
case eChar16:
case eChar32:
case eInt:
case eInt128:
case eWChar:
case eUnspecified:
case eVoid:
case eNullPtr:
return false;
}
}
return false;
}
private static boolean isArithmetic(IType type) {
if (type instanceof IBasicType) {
IBasicType.Kind kind= ((IBasicType) type).getKind();
switch (kind) {
case eBoolean:
case eChar:
case eChar16:
case eChar32:
case eDouble:
case eFloat:
case eFloat128:
case eDecimal32:
case eDecimal64:
case eDecimal128:
case eInt:
case eInt128:
case eWChar:
return true;
case eUnspecified:
case eNullPtr:
case eVoid:
return false;
}
}
return false;
}
public static boolean isIntegral(IType type) {
if (type instanceof IBasicType) {
IBasicType.Kind kind= ((IBasicType) type).getKind();
switch (kind) {
case eBoolean:
case eChar:
case eChar16:
case eChar32:
case eInt:
case eInt128:
case eWChar:
return true;
case eDouble:
case eFloat:
case eFloat128:
case eDecimal32:
case eDecimal64:
case eDecimal128:
case eUnspecified:
case eVoid:
case eNullPtr:
return false;
}
}
return false;
}
private IType[] getClassConversionTypes(int idx) {
IType[] result = fClassConversionTypes[idx];
if (result == null) {
result= IType.EMPTY_TYPE_ARRAY;
IType type= idx == 0 ? fType1 : fType2;
if (type != null) {
type= SemanticUtil.getNestedType(type, TDEF | REF | CVTYPE);
if (type instanceof ICPPClassType) {
fIsClass[idx]= true;
try {
ICPPMethod[] ops = SemanticUtil.getConversionOperators((ICPPClassType) type);
result= new IType[ops.length];
int j= -1;
for (ICPPMethod op : ops) {
if (op.isExplicit())
continue;
final ICPPFunctionType functionType = op.getType();
if (functionType != null) {
IType retType= functionType.getReturnType();
if (retType != null) {
result[++j]= retType;
}
}
}
result= ArrayUtil.trimAt(IType.class, result, j);
} catch (DOMException e) {
}
}
}
fClassConversionTypes[idx]= result;
}
return result;
}
}