1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-06 09:16:02 +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 * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * 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 k = g(f1()); // calls g(const int&&)
// int l = g(f2()); // 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(); BindingAssertionHelper bh= getAssertionHelper();
IFunction g1= bh.assertNonProblemOnFirstIdentifier("g(const int&)"); IFunction g1= bh.assertNonProblemOnFirstIdentifier("g(const int&)");
IFunction g2= bh.assertNonProblemOnFirstIdentifier("g(const int&&)"); IFunction g2= bh.assertNonProblemOnFirstIdentifier("g(const int&&)");
@ -9565,6 +9565,45 @@ public class AST2CPPTests extends AST2TestBase {
assertSame(g2, ref); 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 { // namespace std {
// template<typename T> class initializer_list; // 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 * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * 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) private static FunctionCost costForFunctionCall(ICPPFunction fn, boolean allowUDC, LookupData data)
throws DOMException { throws DOMException {
IType[] argTypes = data.getFunctionArgumentTypes(); IType[] argTypes= data.getFunctionArgumentTypes();
ValueCategory[] isLValue= data.getFunctionArgumentValueCategories(); ValueCategory[] argValueCategories= data.getFunctionArgumentValueCategories();
int skipArg= 0; int skipArg= 0;
final ICPPFunctionType ftype= fn.getType(); final ICPPFunctionType ftype= fn.getType();
if (ftype == null) if (ftype == null)
@ -2733,11 +2733,13 @@ public class CPPSemantics {
IType implicitParameterType= null; IType implicitParameterType= null;
IType impliedObjectType= null; IType impliedObjectType= null;
ValueCategory impliedObjectValueCategory= null;
final IType[] paramTypes= ftype.getParameterTypes(); final IType[] paramTypes= ftype.getParameterTypes();
if (fn instanceof ICPPMethod && !(fn instanceof ICPPConstructor)) { if (fn instanceof ICPPMethod && !(fn instanceof ICPPConstructor)) {
implicitParameterType = getImplicitParameterType((ICPPMethod) fn); implicitParameterType = getImplicitParameterType((ICPPMethod) fn);
if (data.argsContainImpliedObject) { if (data.argsContainImpliedObject) {
impliedObjectType= argTypes[0]; impliedObjectType= argTypes[0];
impliedObjectValueCategory= argValueCategories[0];
skipArg= 1; skipArg= 1;
} }
} }
@ -2751,10 +2753,15 @@ public class CPPSemantics {
} else { } else {
result= new FunctionCost(fn, sourceLen + 1, data.getLookupPoint()); result= new FunctionCost(fn, sourceLen + 1, data.getLookupPoint());
ValueCategory sourceIsLValue= LVALUE;
if (impliedObjectType == null) { if (impliedObjectType == null) {
impliedObjectType= data.getImpliedObjectType(); impliedObjectType= data.getImpliedObjectType();
} }
if (impliedObjectValueCategory == null) {
impliedObjectValueCategory= data.getImpliedObjectValueCategory();
if (impliedObjectValueCategory == null)
impliedObjectValueCategory= ValueCategory.LVALUE;
}
if (fn instanceof ICPPMethod && if (fn instanceof ICPPMethod &&
(((ICPPMethod) fn).isDestructor() || ASTInternal.isStatic(fn, false))) { (((ICPPMethod) fn).isDestructor() || ASTInternal.isStatic(fn, false))) {
// 13.3.1-4 for static member functions, the implicit object parameter always matches, no cost // 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 = new Cost(impliedObjectType, implicitParameterType, Rank.IDENTITY);
cost.setImpliedObject(); cost.setImpliedObject();
} else { } 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()) { if (cost.converts()) {
cost.setImpliedObject(); cost.setImpliedObject();
} else { } else {
@ -2783,7 +2794,7 @@ public class CPPSemantics {
if (!cost.converts()) if (!cost.converts())
return null; return null;
result.setCost(k++, cost, sourceIsLValue); result.setCost(k++, cost, impliedObjectValueCategory);
} }
final UDCMode udc = allowUDC ? UDCMode.DEFER : UDCMode.FORBIDDEN; final UDCMode udc = allowUDC ? UDCMode.DEFER : UDCMode.FORBIDDEN;
@ -2792,7 +2803,7 @@ public class CPPSemantics {
if (argType == null) if (argType == null)
return null; return null;
final ValueCategory sourceIsLValue = isLValue[j + skipArg]; final ValueCategory argValueCategory = argValueCategories[j + skipArg];
IType paramType; IType paramType;
if (j < paramTypes.length) { if (j < paramTypes.length) {
@ -2801,7 +2812,7 @@ public class CPPSemantics {
paramType= VOID_TYPE; paramType= VOID_TYPE;
} else { } else {
cost = new Cost(argType, null, Rank.ELLIPSIS_CONVERSION); cost = new Cost(argType, null, Rank.ELLIPSIS_CONVERSION);
result.setCost(k++, cost, sourceIsLValue); result.setCost(k++, cost, argValueCategory);
continue; continue;
} }
@ -2822,7 +2833,7 @@ public class CPPSemantics {
} }
} }
} }
cost = Conversions.checkImplicitConversionSequence(paramType, argType, sourceIsLValue, cost = Conversions.checkImplicitConversionSequence(paramType, argType, argValueCategory,
udc, ctx, data.getLookupPoint()); udc, ctx, data.getLookupPoint());
if (data.fNoNarrowing && cost.isNarrowingConversion(data.getLookupPoint())) { if (data.fNoNarrowing && cost.isNarrowingConversion(data.getLookupPoint())) {
cost= Cost.NO_CONVERSION; cost= Cost.NO_CONVERSION;
@ -2831,7 +2842,7 @@ public class CPPSemantics {
if (!cost.converts()) if (!cost.converts())
return null; return null;
result.setCost(k++, cost, sourceIsLValue); result.setCost(k++, cost, argValueCategory);
} }
return result; return result;
} }
@ -2844,7 +2855,7 @@ public class CPPSemantics {
} }
ICPPFunctionType ft= m.getType(); ICPPFunctionType ft= m.getType();
implicitType= SemanticUtil.addQualifiers(owner, ft.isConst(), ft.isVolatile(), false); 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) { 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 * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * 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 class Conversions {
public enum UDCMode { ALLOWED, FORBIDDEN, DEFER } 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[] INITIALIZER_LIST_NAME = "initializer_list".toCharArray(); //$NON-NLS-1$
private static final char[] STD_NAME = "std".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 * 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 * The semantics of the initialization is explained in 8.5-16.
*
* @param target the target (parameter) type * @param target the target (parameter) type
* @param exprType the source (argument) type * @param exprType the source (argument) type
* @param valueCat value category of the expression * @param valueCat value category of the expression
@ -89,7 +96,9 @@ public class Conversions {
*/ */
public static Cost checkImplicitConversionSequence(IType target, IType exprType, public static Cost checkImplicitConversionSequence(IType target, IType exprType,
ValueCategory valueCat, UDCMode udc, Context ctx, IASTNode point) throws DOMException { 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) if (isImpliedObject)
udc= UDCMode.FORBIDDEN; udc= UDCMode.FORBIDDEN;
@ -105,15 +114,7 @@ public class Conversions {
final IType cv2T2= exprType; final IType cv2T2= exprType;
final IType T2= getNestedType(cv2T2, TDEF | REF | ALLCVQ); final IType T2= getNestedType(cv2T2, TDEF | REF | ALLCVQ);
// mstodo: will change when implementing rvalue references on this pointer refBindingType= isLValueRef ? ReferenceBinding.LVALUE_REF : ReferenceBinding.RVALUE_REF_BINDS_RVALUE;
final boolean isImplicitWithoutRefQualifier = isImpliedObject;
if (!isImplicitWithoutRefQualifier) {
if (isLValueRef) {
refBindingType= ReferenceBinding.LVALUE_REF;
} else {
refBindingType= ReferenceBinding.RVALUE_REF_BINDS_RVALUE;
}
}
if (exprType instanceof InitializerListType) { if (exprType instanceof InitializerListType) {
if (isLValueRef && getCVQualifier(cv1T1) != CVQualifier.CONST) if (isLValueRef && getCVQualifier(cv1T1) != CVQualifier.CONST)
@ -130,7 +131,16 @@ public class Conversions {
if (isLValueRef) { if (isLValueRef) {
// ... the initializer expression is an lvalue (but is not a bit field) // ... 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] // [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" // ... and "cv1 T1" is reference-compatible with "cv2 T2"
Cost cost= isReferenceCompatible(cv1T1, cv2T2, isImpliedObject, point); Cost cost= isReferenceCompatible(cv1T1, cv2T2, isImpliedObject, point);
if (cost != null) { 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 * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * 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.ICPPASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTElaboratedTypeSpecifier; 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.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.ICPPASTFieldReference;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerClause; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTInitializerClause;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier;
@ -106,6 +107,7 @@ public class LookupData extends ScopeLookupData {
private IASTDeclarator fDeclarator; private IASTDeclarator fDeclarator;
private boolean fFunctionCall; private boolean fFunctionCall;
private IType fImpliedObjectType; private IType fImpliedObjectType;
private ValueCategory fImpliedObjectValueCategory;
private ICPPEvaluation[] functionArgs; private ICPPEvaluation[] functionArgs;
private IType[] functionArgTypes; private IType[] functionArgTypes;
private ValueCategory[] functionArgValueCategories; 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() { public IType getImpliedObjectType() {
if (fImpliedObjectType == null) { if (fImpliedObjectType == null) {
@ -365,9 +367,9 @@ public class LookupData extends ScopeLookupData {
return fImpliedObjectType; return fImpliedObjectType;
} }
/** /**
* Explicitly set the implied object type. * Explicitly sets the implied object type.
* This is for use in cases where implied object type cannot * This method is for use in cases where implied object type cannot
* be determined automatically because there is no lookup name. * be determined automatically because there is no lookup name.
*/ */
public void setImpliedObjectType(IType impliedObjectType) { public void setImpliedObjectType(IType impliedObjectType) {
@ -407,6 +409,48 @@ public class LookupData extends ScopeLookupData {
return null; 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() { public boolean forFriendship() {
IASTName lookupName= getLookupName(); IASTName lookupName= getLookupName();
if (lookupName == null) 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 * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -10,8 +10,6 @@
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.core.lrparser.tests; 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.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.lrparser.gnu.GCCLanguage; import org.eclipse.cdt.core.dom.lrparser.gnu.GCCLanguage;
import org.eclipse.cdt.core.dom.lrparser.gnu.GPPLanguage; 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.core.parser.tests.ast2.AST2CPPTests;
import org.eclipse.cdt.internal.core.parser.ParserException; import org.eclipse.cdt.internal.core.parser.ParserException;
import junit.framework.TestSuite;
@SuppressWarnings("restriction") @SuppressWarnings("restriction")
public class LRCPPTests extends AST2CPPTests { public class LRCPPTests extends AST2CPPTests {
public static TestSuite suite() { public static TestSuite suite() {
return suite(LRCPPTests.class); return suite(LRCPPTests.class);
} }
public LRCPPTests() { public LRCPPTests() {
} }
public LRCPPTests(String name) { public LRCPPTests(String name) {
@ -126,7 +124,9 @@ public class LRCPPTests extends AST2CPPTests {
@Override @Override
public void testXValueCategories() throws Exception {} public void testXValueCategories() throws Exception {}
@Override @Override
public void testRankingOfReferenceBindings() throws Exception {} public void testRankingOfReferenceBindings_1() throws Exception {}
@Override
public void testRankingOfReferenceBindings_2() throws Exception {}
@Override @Override
public void testInlineNamespaceLookup_324096() throws Exception {} public void testInlineNamespaceLookup_324096() throws Exception {}
@Override @Override