1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-29 19:45:01 +02:00

Bug 470014 - Name resolution problem with ref-qualified methods

Change-Id: I2baeee442adffefb1df2c217ed91c5ff58a430ae
This commit is contained in:
Sergey Prigogin 2015-06-16 15:18:50 -07:00
parent a0fe9152df
commit 81d40de462
5 changed files with 144 additions and 40 deletions

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2014 IBM Corporation and others.
* Copyright (c) 2004, 2015 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
@ -9551,7 +9551,7 @@ public class AST2CPPTests extends AST2TestBase {
// int k = g(f1()); // calls g(const int&&)
// int l = g(f2()); // calls g(const int&&)
// }
public void testRankingOfReferenceBindings() throws Exception {
public void testRankingOfReferenceBindings_1() throws Exception {
BindingAssertionHelper bh= getAssertionHelper();
IFunction g1= bh.assertNonProblemOnFirstIdentifier("g(const int&)");
IFunction g2= bh.assertNonProblemOnFirstIdentifier("g(const int&&)");
@ -9564,7 +9564,46 @@ public class AST2CPPTests extends AST2TestBase {
ref= bh.assertNonProblemOnFirstIdentifier("g(f2());");
assertSame(g2, ref);
}
// struct A {
// A& operator<<(int);
// void p() &;
// void p() &&;
// };
// A& operator<<(A&&, char);
//
// void test() {
// A a;
// A() << 1;//1 // calls A::operator<<(int)
// A() << 'c';//2 // calls operator<<(A&&, char)
// a << 1;//3 // calls A::operator<<(int)
// a << 'c';//4 // calls A::operator<<(int)
// A().p();//5 // calls A::p()&&
// a.p();//6 // calls A::p()&
// }
public void testRankingOfReferenceBindings_2() throws Exception {
BindingAssertionHelper bh= getAssertionHelper();
ICPPMethod s1= bh.assertNonProblem("operator<<(int)", 10);
ICPPFunction s2= bh.assertNonProblem("operator<<(A&&, char)", 10);
ICPPMethod p1= bh.assertNonProblemOnFirstIdentifier("p() &;");
ICPPMethod p2= bh.assertNonProblemOnFirstIdentifier("p() &&;");
IASTImplicitName name;
name= bh.assertImplicitName("<< 1;//1", 2, ICPPMethod.class);
assertSame(s1, name.getBinding());
name= bh.assertImplicitName("<< 'c';//2", 2, ICPPFunction.class);
assertSame(s2, name.getBinding());
name= bh.assertImplicitName("<< 1;//3", 2, ICPPMethod.class);
assertSame(s1, name.getBinding());
name= bh.assertImplicitName("<< 'c';//4", 2, ICPPMethod.class);
assertSame(s1, name.getBinding());
ICPPMethod ref;
ref= bh.assertNonProblemOnFirstIdentifier("p();//5");
assertSame(p2, ref);
ref= bh.assertNonProblemOnFirstIdentifier("p();//6");
assertSame(p1, ref);
}
// namespace std {
// template<typename T> class initializer_list;
// }

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2014 IBM Corporation and others.
* Copyright (c) 2004, 2015 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
@ -2724,8 +2724,8 @@ public class CPPSemantics {
private static FunctionCost costForFunctionCall(ICPPFunction fn, boolean allowUDC, LookupData data)
throws DOMException {
IType[] argTypes = data.getFunctionArgumentTypes();
ValueCategory[] isLValue= data.getFunctionArgumentValueCategories();
IType[] argTypes= data.getFunctionArgumentTypes();
ValueCategory[] argValueCategories= data.getFunctionArgumentValueCategories();
int skipArg= 0;
final ICPPFunctionType ftype= fn.getType();
if (ftype == null)
@ -2733,11 +2733,13 @@ public class CPPSemantics {
IType implicitParameterType= null;
IType impliedObjectType= null;
ValueCategory impliedObjectValueCategory= null;
final IType[] paramTypes= ftype.getParameterTypes();
if (fn instanceof ICPPMethod && !(fn instanceof ICPPConstructor)) {
implicitParameterType = getImplicitParameterType((ICPPMethod) fn);
if (data.argsContainImpliedObject) {
impliedObjectType= argTypes[0];
impliedObjectValueCategory= argValueCategories[0];
skipArg= 1;
}
}
@ -2751,10 +2753,15 @@ public class CPPSemantics {
} else {
result= new FunctionCost(fn, sourceLen + 1, data.getLookupPoint());
ValueCategory sourceIsLValue= LVALUE;
if (impliedObjectType == null) {
impliedObjectType= data.getImpliedObjectType();
}
if (impliedObjectValueCategory == null) {
impliedObjectValueCategory= data.getImpliedObjectValueCategory();
if (impliedObjectValueCategory == null)
impliedObjectValueCategory= ValueCategory.LVALUE;
}
if (fn instanceof ICPPMethod &&
(((ICPPMethod) fn).isDestructor() || ASTInternal.isStatic(fn, false))) {
// 13.3.1-4 for static member functions, the implicit object parameter always matches, no cost
@ -2766,7 +2773,11 @@ public class CPPSemantics {
cost = new Cost(impliedObjectType, implicitParameterType, Rank.IDENTITY);
cost.setImpliedObject();
} else {
cost = Conversions.checkImplicitConversionSequence(implicitParameterType, impliedObjectType, sourceIsLValue, UDCMode.FORBIDDEN, Context.IMPLICIT_OBJECT, data.getLookupPoint());
Context context = ftype.hasRefQualifier() ?
Context.IMPLICIT_OBJECT_FOR_METHOD_WITH_REF_QUALIFIER :
Context.IMPLICIT_OBJECT_FOR_METHOD_WITHOUT_REF_QUALIFIER;
cost = Conversions.checkImplicitConversionSequence(implicitParameterType, impliedObjectType,
impliedObjectValueCategory, UDCMode.FORBIDDEN, context, data.getLookupPoint());
if (cost.converts()) {
cost.setImpliedObject();
} else {
@ -2783,7 +2794,7 @@ public class CPPSemantics {
if (!cost.converts())
return null;
result.setCost(k++, cost, sourceIsLValue);
result.setCost(k++, cost, impliedObjectValueCategory);
}
final UDCMode udc = allowUDC ? UDCMode.DEFER : UDCMode.FORBIDDEN;
@ -2792,7 +2803,7 @@ public class CPPSemantics {
if (argType == null)
return null;
final ValueCategory sourceIsLValue = isLValue[j + skipArg];
final ValueCategory argValueCategory = argValueCategories[j + skipArg];
IType paramType;
if (j < paramTypes.length) {
@ -2801,7 +2812,7 @@ public class CPPSemantics {
paramType= VOID_TYPE;
} else {
cost = new Cost(argType, null, Rank.ELLIPSIS_CONVERSION);
result.setCost(k++, cost, sourceIsLValue);
result.setCost(k++, cost, argValueCategory);
continue;
}
@ -2822,7 +2833,7 @@ public class CPPSemantics {
}
}
}
cost = Conversions.checkImplicitConversionSequence(paramType, argType, sourceIsLValue,
cost = Conversions.checkImplicitConversionSequence(paramType, argType, argValueCategory,
udc, ctx, data.getLookupPoint());
if (data.fNoNarrowing && cost.isNarrowingConversion(data.getLookupPoint())) {
cost= Cost.NO_CONVERSION;
@ -2831,7 +2842,7 @@ public class CPPSemantics {
if (!cost.converts())
return null;
result.setCost(k++, cost, sourceIsLValue);
result.setCost(k++, cost, argValueCategory);
}
return result;
}
@ -2844,7 +2855,7 @@ public class CPPSemantics {
}
ICPPFunctionType ft= m.getType();
implicitType= SemanticUtil.addQualifiers(owner, ft.isConst(), ft.isVolatile(), false);
return new CPPReferenceType(implicitType, false);
return new CPPReferenceType(implicitType, ft.isRValueReference());
}
private static IBinding resolveUserDefinedConversion(LookupData data, ICPPFunction[] fns) {

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2012 IBM Corporation and others.
* Copyright (c) 2004, 2015 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
@ -73,14 +73,21 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.Cost.ReferenceBind
*/
public class Conversions {
public enum UDCMode { ALLOWED, FORBIDDEN, DEFER }
public enum Context { ORDINARY, IMPLICIT_OBJECT, FIRST_PARAM_OF_DIRECT_COPY_CTOR, REQUIRE_DIRECT_BINDING }
public enum Context {
ORDINARY,
IMPLICIT_OBJECT_FOR_METHOD_WITHOUT_REF_QUALIFIER,
IMPLICIT_OBJECT_FOR_METHOD_WITH_REF_QUALIFIER,
FIRST_PARAM_OF_DIRECT_COPY_CTOR,
REQUIRE_DIRECT_BINDING
}
private static final char[] INITIALIZER_LIST_NAME = "initializer_list".toCharArray(); //$NON-NLS-1$
private static final char[] STD_NAME = "std".toCharArray(); //$NON-NLS-1$
/**
* Computes the cost of an implicit conversion sequence [over.best.ics] 13.3.3.1
* The semantics of the initialization is explained in 8.5-16
* Computes the cost of an implicit conversion sequence [over.best.ics] 13.3.3.1.
* The semantics of the initialization is explained in 8.5-16.
*
* @param target the target (parameter) type
* @param exprType the source (argument) type
* @param valueCat value category of the expression
@ -89,7 +96,9 @@ public class Conversions {
*/
public static Cost checkImplicitConversionSequence(IType target, IType exprType,
ValueCategory valueCat, UDCMode udc, Context ctx, IASTNode point) throws DOMException {
final boolean isImpliedObject= ctx == Context.IMPLICIT_OBJECT;
final boolean isImpliedObject=
ctx == Context.IMPLICIT_OBJECT_FOR_METHOD_WITHOUT_REF_QUALIFIER ||
ctx == Context.IMPLICIT_OBJECT_FOR_METHOD_WITH_REF_QUALIFIER;
if (isImpliedObject)
udc= UDCMode.FORBIDDEN;
@ -105,15 +114,7 @@ public class Conversions {
final IType cv2T2= exprType;
final IType T2= getNestedType(cv2T2, TDEF | REF | ALLCVQ);
// mstodo: will change when implementing rvalue references on this pointer
final boolean isImplicitWithoutRefQualifier = isImpliedObject;
if (!isImplicitWithoutRefQualifier) {
if (isLValueRef) {
refBindingType= ReferenceBinding.LVALUE_REF;
} else {
refBindingType= ReferenceBinding.RVALUE_REF_BINDS_RVALUE;
}
}
refBindingType= isLValueRef ? ReferenceBinding.LVALUE_REF : ReferenceBinding.RVALUE_REF_BINDS_RVALUE;
if (exprType instanceof InitializerListType) {
if (isLValueRef && getCVQualifier(cv1T1) != CVQualifier.CONST)
@ -130,7 +131,16 @@ public class Conversions {
if (isLValueRef) {
// ... the initializer expression is an lvalue (but is not a bit field)
// [for overload resolution bit-fields are treated the same, error if selected as best match]
if (valueCat == LVALUE) {
if (valueCat == LVALUE || ctx == Context.IMPLICIT_OBJECT_FOR_METHOD_WITHOUT_REF_QUALIFIER) {
// 13.3.3.5: For non-static member functions declared without a ref-qualifier,
// an additional rule applies:
// even if the implicit object parameter is not const-qualified, an rvalue can be
// bound to the parameter as long as in all other respects the argument can be
// converted to the type of the implicit object parameter.
// [Note: The fact that such an argument is an rvalue does not affect the ranking of
// implicit conversion sequences (13.3.3.2). end note]
if (valueCat != LVALUE)
refBindingType= ReferenceBinding.RVALUE_REF_BINDS_RVALUE;
// ... and "cv1 T1" is reference-compatible with "cv2 T2"
Cost cost= isReferenceCompatible(cv1T1, cv2T2, isImpliedObject, point);
if (cost != null) {

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2011 IBM Corporation and others.
* Copyright (c) 2004, 2015 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
@ -53,6 +53,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTElaboratedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExplicitTemplateInstantiation;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFieldReference;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier;
@ -106,6 +107,7 @@ public class LookupData extends ScopeLookupData {
private IASTDeclarator fDeclarator;
private boolean fFunctionCall;
private IType fImpliedObjectType;
private ValueCategory fImpliedObjectValueCategory;
private ICPPEvaluation[] functionArgs;
private IType[] functionArgTypes;
private ValueCategory[] functionArgValueCategories;
@ -356,7 +358,7 @@ public class LookupData extends ScopeLookupData {
}
/**
* Returns the implied object type, or <code>null</code>.
* Returns the implied object type, or {@code null} if there is no implied object.
*/
public IType getImpliedObjectType() {
if (fImpliedObjectType == null) {
@ -365,9 +367,9 @@ public class LookupData extends ScopeLookupData {
return fImpliedObjectType;
}
/**
* Explicitly set the implied object type.
* This is for use in cases where implied object type cannot
/**
* Explicitly sets the implied object type.
* This method is for use in cases where implied object type cannot
* be determined automatically because there is no lookup name.
*/
public void setImpliedObjectType(IType impliedObjectType) {
@ -407,6 +409,48 @@ public class LookupData extends ScopeLookupData {
return null;
}
/**
* Returns the category of the implied object, or {@code null} if there is no implied object.
* @see ValueCategory
*/
public ValueCategory getImpliedObjectValueCategory() {
if (fImpliedObjectValueCategory == null) {
fImpliedObjectValueCategory= determineImpliedObjectValueCategory();
}
return fImpliedObjectValueCategory;
}
private ValueCategory determineImpliedObjectValueCategory() {
IASTName tn = getLookupName();
if (tn == null)
return null;
if (tn.getPropertyInParent() == ICPPASTTemplateId.TEMPLATE_NAME) {
tn= (IASTName) tn.getParent();
}
IASTNode parent = tn.getParent();
IASTNode nameParent= parent;
if (parent instanceof ICPPASTQualifiedName) {
final ICPPASTQualifiedName qn = (ICPPASTQualifiedName) parent;
if (qn.getLastName() == tn) {
nameParent= parent.getParent();
}
}
if (nameParent instanceof IASTFieldReference) {
ICPPASTFieldReference fieldReference = (ICPPASTFieldReference) nameParent;
ICPPASTExpression owner = fieldReference.getFieldOwner();
if (fieldReference.isPointerDereference()) {
return ValueCategory.LVALUE;
}
return owner.getValueCategory();
} else if (nameParent instanceof IASTIdExpression) {
return ValueCategory.LVALUE;
}
return null;
}
public boolean forFriendship() {
IASTName lookupName= getLookupName();
if (lookupName == null)

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2006, 2010 IBM Corporation and others.
* Copyright (c) 2006, 2015 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
@ -10,8 +10,6 @@
*******************************************************************************/
package org.eclipse.cdt.core.lrparser.tests;
import junit.framework.TestSuite;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.lrparser.gnu.GCCLanguage;
import org.eclipse.cdt.core.dom.lrparser.gnu.GPPLanguage;
@ -20,16 +18,16 @@ import org.eclipse.cdt.core.parser.ParserLanguage;
import org.eclipse.cdt.core.parser.tests.ast2.AST2CPPTests;
import org.eclipse.cdt.internal.core.parser.ParserException;
import junit.framework.TestSuite;
@SuppressWarnings("restriction")
public class LRCPPTests extends AST2CPPTests {
public static TestSuite suite() {
return suite(LRCPPTests.class);
}
public LRCPPTests() {
}
public LRCPPTests(String name) {
@ -126,7 +124,9 @@ public class LRCPPTests extends AST2CPPTests {
@Override
public void testXValueCategories() throws Exception {}
@Override
public void testRankingOfReferenceBindings() throws Exception {}
public void testRankingOfReferenceBindings_1() throws Exception {}
@Override
public void testRankingOfReferenceBindings_2() throws Exception {}
@Override
public void testInlineNamespaceLookup_324096() throws Exception {}
@Override