1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-22 06:02:11 +02:00

Bug 573764 - Ambiguous conversion on numeric type

Starting with C++11, a null pointer constant has to be specifically an
integer literal of 0 and not any constant expression. Before this
change, an expression like (0 & 1) would wrongly being considered a null
pointer constant. It also means it could implicitly convert to a pointer
type (like int *) and lead to problems during function resolution, like
ambiguity.

This change corrects the behavior for C++11 by tracking whether the
integer type (basic type) came from a literal expression so that we can
add this additional constraint when checking for a null pointer
constant. Because types are sometimes returned directly when evaluating
different kinds of non-literal expressions that contain literal
expressions, we have to be careful that we remove the flag that tracks
"from literal expression".

Unfortunately, the semantic code does not track the active C++ version
which means the behavior for pre-C++11 will be impacted. Tracking the
active C++ version would not be trivial and at least the new behavior is
more future-proof.

Change-Id: Ied625e96e70390872e36ab5bb4dc238d75809d2e
Signed-off-by: Marc-Andre Laperle <malaperle@gmail.com>
This commit is contained in:
Marc-Andre Laperle 2021-06-07 01:17:43 -04:00
parent 3f472a917b
commit 0748cd24c6
8 changed files with 157 additions and 19 deletions

View file

@ -13658,4 +13658,69 @@ public class AST2CPPTests extends AST2CPPTestBase {
parseAndCheckBindings(getAboveComment(), CPP, true); parseAndCheckBindings(getAboveComment(), CPP, true);
} }
// void function(int * a) { }
// void function(unsigned int b) { }
//
// void functionPtr(decltype(nullptr) c) { }
//
// int main()
// {
// function(0); // 0
// function(nullptr); // 1
// functionPtr(0); // 2
// functionPtr(nullptr); // 3
//
// function(0 & 1); // 4
// functionPtr(0 & 1); // 5
//
// function(0 << 1); // 6
// functionPtr(0 << 1); // 7
//
// function((0,0)); // 8
// functionPtr((0,0)); // 9
//
// function(int{0}); // 10
// functionPtr(int{0}); // 11
//
// function(true ? 0 : 0); // 12
// functionPtr(true ? 0 : 0); // 13
// }
public void testNullPointerConstantConversion_573764() throws Exception {
IASTTranslationUnit tu = parse(getAboveComment(), CPP);
NameCollector collector = new NameCollector();
tu.accept(collector);
IBinding funcDeclA = collector.getName(0).resolveBinding();
IBinding funcDeclB = collector.getName(2).resolveBinding();
IBinding funcDeclC = collector.getName(4).resolveBinding();
int callIndexStart = 7;
// Ambiguous
assertTrue(collector.getName(callIndexStart + 0).resolveBinding() instanceof IProblemBinding);
assertEquals(funcDeclA, collector.getName(callIndexStart + 1).resolveBinding());
assertEquals(funcDeclC, collector.getName(callIndexStart + 2).resolveBinding());
assertEquals(funcDeclC, collector.getName(callIndexStart + 3).resolveBinding());
assertEquals(funcDeclB, collector.getName(callIndexStart + 4).resolveBinding());
// Invalid argument (not null pointer constant)
assertTrue(collector.getName(callIndexStart + 5).resolveBinding() instanceof IProblemBinding);
assertEquals(funcDeclB, collector.getName(callIndexStart + 6).resolveBinding());
// Invalid argument (not null pointer constant)
assertTrue(collector.getName(callIndexStart + 7).resolveBinding() instanceof IProblemBinding);
// EvalComma
assertEquals(funcDeclB, collector.getName(callIndexStart + 8).resolveBinding());
// Invalid argument (not null pointer constant)
assertTrue(collector.getName(callIndexStart + 9).resolveBinding() instanceof IProblemBinding);
assertEquals(funcDeclB, collector.getName(callIndexStart + 10).resolveBinding());
// Invalid argument (not null pointer constant)
assertTrue(collector.getName(callIndexStart + 11).resolveBinding() instanceof IProblemBinding);
// EvalConditional
assertEquals(funcDeclB, collector.getName(callIndexStart + 12).resolveBinding());
// Invalid argument (not null pointer constant)
assertTrue(collector.getName(callIndexStart + 13).resolveBinding() instanceof IProblemBinding);
}
} }

View file

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2 Bundle-ManifestVersion: 2
Bundle-Name: %pluginName Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.cdt.core; singleton:=true Bundle-SymbolicName: org.eclipse.cdt.core; singleton:=true
Bundle-Version: 7.2.100.qualifier Bundle-Version: 7.2.200.qualifier
Bundle-Activator: org.eclipse.cdt.core.CCorePlugin Bundle-Activator: org.eclipse.cdt.core.CCorePlugin
Bundle-Vendor: %providerName Bundle-Vendor: %providerName
Bundle-Localization: plugin Bundle-Localization: plugin

View file

@ -17,6 +17,9 @@ package org.eclipse.cdt.internal.core.dom.parser.cpp;
import static org.eclipse.cdt.core.dom.ast.cpp.ICPPParameter.EMPTY_CPPPARAMETER_ARRAY; import static org.eclipse.cdt.core.dom.ast.cpp.ICPPParameter.EMPTY_CPPPARAMETER_ARRAY;
import java.text.MessageFormat;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.ASTTypeUtil; import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression; import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression;
@ -58,10 +61,16 @@ public class CPPBasicType implements ICPPBasicType, ISerializableType {
public static final CPPBasicType CHAR = new CPPBasicType(Kind.eChar, 0); public static final CPPBasicType CHAR = new CPPBasicType(Kind.eChar, 0);
public static final CPPBasicType VOID = new CPPBasicType(Kind.eVoid, 0); public static final CPPBasicType VOID = new CPPBasicType(Kind.eVoid, 0);
private static final int FROM_STRING_LITERAL = 1 << 31; public static final int FROM_LITERAL = 1 << 30;
public static final int FROM_STRING_LITERAL = 1 << 31;
private static final short TYPE_BUFFER_KIND_OFFSET = ITypeMarshalBuffer.FIRST_FLAG;
private static final short TYPE_BUFFER_FROM_LITERAL_FLAG = ITypeMarshalBuffer.SECOND_LAST_FLAG / 2;
private static final short TYPE_BUFFER_FIRST_FLAG_AFTER_KIND = TYPE_BUFFER_FROM_LITERAL_FLAG;
private static final int MAX_KIND_INT_VALUE = (TYPE_BUFFER_FIRST_FLAG_AFTER_KIND - 1) / TYPE_BUFFER_KIND_OFFSET;
private final Kind fKind; private final Kind fKind;
private final int fModifiers; private int fModifiers;
private Long fAssociatedValue; private Long fAssociatedValue;
private ICPPFunction fPseudoDestructor; private ICPPFunction fPseudoDestructor;
@ -77,9 +86,11 @@ public class CPPBasicType implements ICPPBasicType, ISerializableType {
} else { } else {
fKind = kind; fKind = kind;
} }
if (expression instanceof IASTLiteralExpression if (expression instanceof IASTLiteralExpression) {
&& ((IASTLiteralExpression) expression).getKind() == IASTLiteralExpression.lk_string_literal) { qualifiers |= FROM_LITERAL;
qualifiers |= FROM_STRING_LITERAL; if (((IASTLiteralExpression) expression).getKind() == IASTLiteralExpression.lk_string_literal) {
qualifiers |= FROM_STRING_LITERAL;
}
} }
fModifiers = qualifiers; fModifiers = qualifiers;
if (expression instanceof ICPPASTInitializerClause) { if (expression instanceof ICPPASTInitializerClause) {
@ -209,9 +220,19 @@ public class CPPBasicType implements ICPPBasicType, ISerializableType {
@Override @Override
public CPPBasicType clone() { public CPPBasicType clone() {
return clone(~0);
}
/**
* Clone as normal but keep only requested flags.
*
* @param flagsMask The mask of flags to preserve during the clone.
*/
public CPPBasicType clone(int flagsMask) {
CPPBasicType t = null; CPPBasicType t = null;
try { try {
t = (CPPBasicType) super.clone(); t = (CPPBasicType) super.clone();
t.fModifiers &= flagsMask;
} catch (CloneNotSupportedException e) { } catch (CloneNotSupportedException e) {
// Not going to happen. // Not going to happen.
} }
@ -235,15 +256,22 @@ public class CPPBasicType implements ICPPBasicType, ISerializableType {
} }
/** /**
* Returns {@code true} if the type was created for a string literal. * Returns {@code true} if the type was created from a string literal.
*/ */
public final boolean isFromStringLiteral() { public final boolean isFromStringLiteral() {
return (fModifiers & FROM_STRING_LITERAL) != 0; return (fModifiers & FROM_STRING_LITERAL) != 0;
} }
/**
* Returns {@code true} if the type was created from a literal.
*/
public final boolean isFromLiteral() {
return (fModifiers & FROM_LITERAL) != 0;
}
@Override @Override
public final int getModifiers() { public final int getModifiers() {
return fModifiers & ~FROM_STRING_LITERAL; return fModifiers & ~FROM_STRING_LITERAL & ~FROM_LITERAL;
} }
@Override @Override
@ -254,27 +282,47 @@ public class CPPBasicType implements ICPPBasicType, ISerializableType {
@Override @Override
public void marshal(ITypeMarshalBuffer buffer) throws CoreException { public void marshal(ITypeMarshalBuffer buffer) throws CoreException {
final int kind = getKind().ordinal(); final int kind = getKind().ordinal();
final int shiftedKind = kind * ITypeMarshalBuffer.FIRST_FLAG; // 'kind' uses the space of the first few flags so make sure it doesn't overflow to the actual used flags further.
if (kind > MAX_KIND_INT_VALUE) {
throw new CoreException(CCorePlugin.createStatus(
MessageFormat.format("Cannot marshal a basic type, kind ''{0}'' would overflow following flags.", //$NON-NLS-1$
getKind().toString())));
}
final int shiftedKind = kind * TYPE_BUFFER_KIND_OFFSET;
final int modifiers = getModifiers(); final int modifiers = getModifiers();
short firstBytes = (short) (ITypeMarshalBuffer.BASIC_TYPE | shiftedKind); short firstBytes = (short) (ITypeMarshalBuffer.BASIC_TYPE | shiftedKind);
if (modifiers != 0) if (isFromLiteral())
firstBytes |= ITypeMarshalBuffer.LAST_FLAG; firstBytes = setFirstBytesFlag(firstBytes, TYPE_BUFFER_FROM_LITERAL_FLAG);
if (fAssociatedValue != null) if (fAssociatedValue != null)
firstBytes |= ITypeMarshalBuffer.SECOND_LAST_FLAG; firstBytes = setFirstBytesFlag(firstBytes, ITypeMarshalBuffer.SECOND_LAST_FLAG);
if (modifiers != 0)
firstBytes = setFirstBytesFlag(firstBytes, ITypeMarshalBuffer.LAST_FLAG);
buffer.putShort(firstBytes); buffer.putShort(firstBytes);
if (modifiers != 0) if (modifiers != 0)
buffer.putByte((byte) modifiers); buffer.putByte((byte) modifiers);
if (fAssociatedValue != null) if (fAssociatedValue != null)
buffer.putLong(getAssociatedNumericalValue()); buffer.putLong(getAssociatedNumericalValue());
}
private static short setFirstBytesFlag(short firstBytes, short flag) throws CoreException {
if (flag < TYPE_BUFFER_FIRST_FLAG_AFTER_KIND) {
throw new CoreException(CCorePlugin.createStatus(
MessageFormat.format("Cannot marshal a basic type, flag ''0x{0}'' overlaps ''kind'' bytes.", //$NON-NLS-1$
Integer.toHexString(flag))));
}
return (short) (firstBytes | flag);
} }
public static IType unmarshal(short firstBytes, ITypeMarshalBuffer buffer) throws CoreException { public static IType unmarshal(short firstBytes, ITypeMarshalBuffer buffer) throws CoreException {
final boolean haveModifiers = (firstBytes & ITypeMarshalBuffer.LAST_FLAG) != 0; final boolean haveModifiers = (firstBytes & ITypeMarshalBuffer.LAST_FLAG) != 0;
final boolean haveAssociatedNumericalValue = (firstBytes & ITypeMarshalBuffer.SECOND_LAST_FLAG) != 0; final boolean haveAssociatedNumericalValue = (firstBytes & ITypeMarshalBuffer.SECOND_LAST_FLAG) != 0;
int modifiers = 0; int modifiers = 0;
int kind = (firstBytes & (ITypeMarshalBuffer.SECOND_LAST_FLAG - 1)) / ITypeMarshalBuffer.FIRST_FLAG; int kind = (firstBytes & (TYPE_BUFFER_FIRST_FLAG_AFTER_KIND - 1)) / TYPE_BUFFER_KIND_OFFSET;
if (haveModifiers) if (haveModifiers)
modifiers = buffer.getByte(); modifiers = buffer.getByte();
if ((firstBytes & TYPE_BUFFER_FROM_LITERAL_FLAG) != 0)
modifiers |= FROM_LITERAL;
CPPBasicType result = new CPPBasicType(Kind.values()[kind], modifiers); CPPBasicType result = new CPPBasicType(Kind.values()[kind], modifiers);
if (haveAssociatedNumericalValue) if (haveAssociatedNumericalValue)
result.setAssociatedNumericalValue(buffer.getLong()); result.setAssociatedNumericalValue(buffer.getLong());

View file

@ -1168,9 +1168,13 @@ public class Conversions {
if (basicType.getKind() == Kind.eNullPtr) if (basicType.getKind() == Kind.eNullPtr)
return true; return true;
Long val = basicType.getAssociatedNumericalValue(); // Starting from C++11, the value is required to be a literal. This means pre-C++11 parsing will not be correct here.
if (val != null && val == 0) { // But we don't currently have a way to check the C++ version here and in semantics code in general.
return true; if (basicType.getKind() == Kind.eInt && basicType.isFromLiteral()) {
Long val = basicType.getAssociatedNumericalValue();
if (val != null && val == 0) {
return true;
}
} }
} }
return false; return false;

View file

@ -151,6 +151,11 @@ public class EvalBinary extends CPPDependentEvaluation {
} }
} }
} }
if (fType instanceof CPPBasicType) {
fType = ((CPPBasicType) fType).clone(~CPPBasicType.FROM_LITERAL);
}
return fType; return fType;
} }

View file

@ -28,6 +28,7 @@ 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.DependentValue;
import org.eclipse.cdt.internal.core.dom.parser.ITypeMarshalBuffer; 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.IntegralValue;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.InstantiationContext; import org.eclipse.cdt.internal.core.dom.parser.cpp.InstantiationContext;
import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.CoreException;
@ -145,6 +146,9 @@ public class EvalComma extends CPPDependentEvaluation {
public IType getType() { public IType getType() {
if (fType == null) { if (fType == null) {
fType = computeType(); fType = computeType();
if (fType instanceof CPPBasicType) {
fType = ((CPPBasicType) fType).clone(~CPPBasicType.FROM_LITERAL);
}
} }
return fType; return fType;
} }

View file

@ -39,6 +39,7 @@ 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.IntegralValue;
import org.eclipse.cdt.internal.core.dom.parser.ProblemType; import org.eclipse.cdt.internal.core.dom.parser.ProblemType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPArithmeticConversion; 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.CPPReferenceType; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPReferenceType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownType; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownType;
@ -180,6 +181,14 @@ public class EvalConditional extends CPPDependentEvaluation {
if (fValueCategory != null) if (fValueCategory != null)
return; return;
evaluateInternal();
if (fType instanceof CPPBasicType) {
fType = ((CPPBasicType) fType).clone(~CPPBasicType.FROM_LITERAL);
}
}
private void evaluateInternal() {
fValueCategory = PRVALUE; fValueCategory = PRVALUE;
final ICPPEvaluation positive = fPositive == null ? fCondition : fPositive; final ICPPEvaluation positive = fPositive == null ? fCondition : fPositive;

View file

@ -308,10 +308,13 @@ public class PDOM extends PlatformObject implements IPDOM {
* 217.0 - Added nodiscard class/struct information, bug 534420 * 217.0 - Added nodiscard class/struct information, bug 534420
* 218.0 - Added nodiscard enums information, bug 534420 * 218.0 - Added nodiscard enums information, bug 534420
* 219.0 - Fix enums nodiscard information in the index from 8 byte to 1 byte, bug 534420 * 219.0 - Fix enums nodiscard information in the index from 8 byte to 1 byte, bug 534420
*
* CDT 10.4 development
* 220.0 - Changed marshalling of CPPBasicType to store new "from literal" flag, bug 573764
*/ */
private static final int MIN_SUPPORTED_VERSION = version(219, 0); private static final int MIN_SUPPORTED_VERSION = version(220, 0);
private static final int MAX_SUPPORTED_VERSION = version(219, Short.MAX_VALUE); private static final int MAX_SUPPORTED_VERSION = version(220, Short.MAX_VALUE);
private static final int DEFAULT_VERSION = version(219, 0); private static final int DEFAULT_VERSION = version(220, 0);
private static int version(int major, int minor) { private static int version(int major, int minor) {
return (major << 16) + minor; return (major << 16) + minor;