1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-12 18:55:38 +02:00

Bug 470943 - Binding of rvalue reference to temporary (core issue 1138)

Change-Id: I9524816b279e3f791535b11b54d475cf657fe64b
Signed-off-by: Nathan Ridge <zeratul976@hotmail.com>
This commit is contained in:
Nathan Ridge 2015-07-04 03:27:18 -04:00 committed by Sergey Prigogin
parent 6730366662
commit 4e90a96767
3 changed files with 164 additions and 119 deletions

View file

@ -2389,39 +2389,95 @@ public class AST2CPPSpecTest extends AST2SpecTestBase {
parse(getAboveComment(), ParserLanguage.CPP, true, 0); parse(getAboveComment(), ParserLanguage.CPP, true, 0);
} }
// double d = 2.0; // Note: the examples for 8.5.3/5 are written slightly differently
// double& rd = d; // rd refers to d // than they appear in the standard. In the standard, the examples
// const double& rcd = d; // rcd refers to d // demonstrate the rules with variable initialiation, but CDT doesn't
// struct A { }; // currently issue problem bindings for variable initialization,
// struct B : public A { } b; // so in the tests the examples are rewritten to use function calls.
// A& ra = b; // ra refers to A subobject in b
// const A& rca = b; // rca refers to A subobject in b // void f1(double);
// void f2(double&);
// void f3(const double&);
// struct A { };
// struct B : public A { operator int&(); } b;
// void f4(A&);
// void f5(const A&);
// void f6(int&);
// int main() {
// f1(2.0);
// double d;
// f2(d);
// f3(d);
// f4(b);
// f5(b);
// f6(B());
// }
public void test8_5_3s5a() throws Exception { public void test8_5_3s5a() throws Exception {
parse(getAboveComment(), ParserLanguage.CPP, true, 0); parse(getAboveComment(), ParserLanguage.CPP, true, 0);
} }
// double& rd2 = 2.0; // error: not an lvalue and reference not const // void f1(double&);
// int i = 2; // void f2(double&);
// double& rd3 = i; // error: type mismatch and reference not const // int main() {
// f1(2.0); // error: not an lvalue and reference not const
// int i = 2;
// f2(i); // error: type mismatch and reference not const
// }
public void test8_5_3s5b() throws Exception { public void test8_5_3s5b() throws Exception {
parse(getAboveComment(), ParserLanguage.CPP, true, 0); BindingAssertionHelper helper = getAssertionHelper(ParserLanguage.CPP);
helper.assertProblem("f1(2", "f1");
helper.assertProblem("f2(i", "f2");
} }
// struct A { }; // struct A { };
// struct B : public A { } b; // struct B : public A { } b;
// extern B f(); // extern B f();
// const A& rca = f(); // Either bound to the A subobject of the B rvalue, // void f1(const A&);
// // or the entire B object is copied and the reference // void f2(A&&);
// // is bound to the A subobject of the copy // struct X {
// operator B();
// operator int&();
// } x;
// void f3(int&&);
// void f4(B&&);
// int main() {
// f1(f()); // bound to the A subobject of the B rvalue
// f2(f()); // same as above
// f1(x); // bound to the A subobject of the result of the conversion
// f3(static_cast<int&&>(i)); // bound directly to i
// f4(x); // bound directly to the result of operator B
// f3(X()); // error: lvalue-to-rvalue conversion applied to result of operator int&
// }
public void test8_5_3s5c() throws Exception { public void test8_5_3s5c() throws Exception {
parse(getAboveComment(), ParserLanguage.CPP, true, 0); BindingAssertionHelper helper = getAssertionHelper(ParserLanguage.CPP);
helper.assertNonProblem("f1(f", "f1");
helper.assertNonProblem("f2(f", "f2");
helper.assertNonProblem("f1(x", "f1");
helper.assertNonProblem("f3(s", "f3");
helper.assertNonProblem("f4(x", "f4");
helper.assertProblem("f3(X", "f3");
} }
// const double& rcd2 = 2; // rcd2 refers to temporary with value 2.0 // void f1(const double&);
// const volatile int cvi = 1; // void f2(double&&);
// const int& r = cvi; // error: type qualifiers dropped // void f3(const int&);
// int main() {
// f1(2);
// f2(2);
// const volatile int cvi = 1;
// f3(cvi); // error: type qualifiers dropped
// double d;
// int i;
// f2(d); // error: copying lvalue of related type
// f2(i);
// }
public void test8_5_3s5d() throws Exception { public void test8_5_3s5d() throws Exception {
parse(getAboveComment(), ParserLanguage.CPP, true, 0); BindingAssertionHelper helper = getAssertionHelper(ParserLanguage.CPP);
helper.assertNonProblem("f1(2", "f1");
helper.assertNonProblem("f2(2", "f2");
helper.assertProblem("f3(cv", "f3");
helper.assertProblem("f2(d)", "f2");
helper.assertNonProblem("f2(i", "f2");
} }
// struct X { int a; }; // struct X { int a; };

View file

@ -8281,7 +8281,7 @@ public class AST2CPPTests extends AST2TestBase {
// caref(b); // caref(b);
// dref(2.0); // error: not an lvalue and reference not const // dref(2.0); // error: not an lvalue and reference not const
// dref(i); // error: type mismatch and reference not const // dref(i); // error: type mismatch and reference not const
// drref(i); // error: rvalue reference cannot bind to lvalue // drref(i); // bound to temporary double object
// caref(f()); // bound to the A subobject of the B rvalue. // caref(f()); // bound to the A subobject of the B rvalue.
// carref(f()); // same as above // carref(f()); // same as above
// caref(x); // bound to the A subobject of the result of the conversion // caref(x); // bound to the A subobject of the result of the conversion
@ -8298,7 +8298,7 @@ public class AST2CPPTests extends AST2TestBase {
bh.assertNonProblem("caref(b)", 5); bh.assertNonProblem("caref(b)", 5);
bh.assertProblem("dref(2.0)", 4); bh.assertProblem("dref(2.0)", 4);
bh.assertProblem("dref(i)", 4); bh.assertProblem("dref(i)", 4);
bh.assertProblem("drref(i)", 5); bh.assertNonProblem("drref(i)", 5);
bh.assertNonProblem("caref(f())", 5); bh.assertNonProblem("caref(f())", 5);
bh.assertNonProblem("carref(f())", 6); bh.assertNonProblem("carref(f())", 6);
bh.assertNonProblem("caref(x)", 5); bh.assertNonProblem("caref(x)", 5);
@ -11102,6 +11102,16 @@ public class AST2CPPTests extends AST2TestBase {
isParameterSignatureEqual(sd.getDeclarators()[0], "(int&&)"); isParameterSignatureEqual(sd.getDeclarators()[0], "(int&&)");
} }
// struct S { S(int); };
// void find(S&&);
// int main() {
// int waldo = 42;
// find(waldo);
// }
public void testRValueReferenceBindingToTemporary_470943() throws Exception {
parseAndCheckBindings();
}
// constexpr int waldo1 = 42; // constexpr int waldo1 = 42;
// constexpr auto waldo2 = 43; // constexpr auto waldo2 = 43;
public void testConstexprVariableIsConst_451091() throws Exception { public void testConstexprVariableIsConst_451091() throws Exception {

View file

@ -153,7 +153,7 @@ public class Conversions {
// 'cv3 T3' (this conversion is selected by enumerating the applicable conversion functions (13.3.1.6) // 'cv3 T3' (this conversion is selected by enumerating the applicable conversion functions (13.3.1.6)
// and choosing the best one through overload resolution (13.3)), // and choosing the best one through overload resolution (13.3)),
if (T2 instanceof ICPPClassType && udc != UDCMode.FORBIDDEN && isReferenceRelated(T1, T2, point) < 0) { if (T2 instanceof ICPPClassType && udc != UDCMode.FORBIDDEN && isReferenceRelated(T1, T2, point) < 0) {
Cost cost= initializationByConversionForDirectReference(cv1T1, cv2T2, (ICPPClassType) T2, true, ctx, point); Cost cost= initializationByConversionForDirectReference(cv1T1, cv2T2, (ICPPClassType) T2, true, false, ctx, point);
if (cost != null) { if (cost != null) {
cost.setReferenceBinding(refBindingType); cost.setReferenceBinding(refBindingType);
return cost; return cost;
@ -162,97 +162,65 @@ public class Conversions {
} }
// Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 // Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1
// shall be const), or the reference shall be an rvalue reference and the initializer expression // shall be const), or the reference shall be an rvalue reference.
// shall be an rvalue or have function type. if (isLValueRef && getCVQualifier(cv1T1) != CVQualifier.CONST) {
boolean ok;
if (isLValueRef) {
ok = getCVQualifier(cv1T1) == CVQualifier.CONST;
} else {
ok= valueCat.isRValue() || T2 instanceof IFunctionType;
}
if (!ok) {
return Cost.NO_CONVERSION; return Cost.NO_CONVERSION;
} }
// If T1 is a function type, then // If the initializer expression is an xvalue, class prvalue, array prvalue, or function lvalue
if (T1 instanceof IFunctionType) { // and 'cv1 T1' is reference-compatible with 'cv2 T2', then the reference is bound to the value
// if T2 is the same type as T1, the reference is bound to the initializer expression lvalue; // of the initializer expression (or the appropriate base class subobject).
if (T2.isSameType(T1)) { if (valueCat == ValueCategory.XVALUE
Cost cost= new Cost(T1, T2, Rank.IDENTITY); || (valueCat == ValueCategory.PRVALUE && (T2 instanceof ICPPClassType || T2 instanceof IArrayType))
cost.setReferenceBinding(refBindingType); || (valueCat == ValueCategory.LVALUE && T2 instanceof ICPPFunctionType)) {
return cost; Cost cost = isReferenceCompatible(cv1T1, cv2T2, isImpliedObject, point);
}
// if T2 is a class type and the initializer expression can be implicitly converted to an lvalue of
// type T1 (this conversion is selected by enumerating the applicable conversion functions (13.3.1.6)
// and choosing the best one through overload resolution (13.3)), the reference is bound to the
// function lvalue that is the result of the conversion;
if (T2 instanceof ICPPClassType) {
Cost cost= initializationByConversionForDirectReference(cv1T1, cv2T2, (ICPPClassType) T2, true, ctx, point);
if (cost != null) {
cost.setReferenceBinding(refBindingType);
return cost;
}
}
// otherwise, the program is ill-formed.
return Cost.NO_CONVERSION;
}
// Otherwise, if T2 is a class type and
if (T2 instanceof ICPPClassType) {
// ... the initializer expression is an rvalue and 'cv1 T1' is reference-compatible with 'cv2 T2'
// ..., then the reference is bound to the initializer expression rvalue in the first case
if (valueCat.isRValue()) {
Cost cost= isReferenceCompatible(cv1T1, cv2T2, isImpliedObject, point);
if (cost != null) {
// [13.3.3.1.4-1] direct binding has either identity or conversion rank.
if (cost.getInheritanceDistance() > 0) {
cost.setRank(Rank.CONVERSION);
}
cost.setReferenceBinding(refBindingType);
return cost;
}
}
// or T1 is not reference-related to T2 and the initializer expression can be implicitly
// converted to an rvalue of type 'cv3 T3' (this conversion is selected by enumerating the
// applicable conversion functions (13.3.1.6) and choosing the best one through overload
// resolution (13.3)), then the reference is bound to the initializer expression rvalue in the
// first case and to the object that is the result of the conversion in the second case (or,
// in either case, to the appropriate base class sub-object of the object).
if (udc != UDCMode.FORBIDDEN && isReferenceRelated(T1, T2, point) < 0) {
Cost cost= initializationByConversionForDirectReference(cv1T1, cv2T2, (ICPPClassType) T2, false, ctx, point);
if (cost != null) {
cost.setReferenceBinding(refBindingType);
return cost;
}
}
}
// If the initializer expression is an rvalue, with T2 an array type, and 'cv1 T1' is
// reference-compatible with 'cv2 T2' the reference is bound to the object represented by the
// rvalue (see 3.10).
if (T2 instanceof IArrayType && valueCat.isRValue()) {
Cost cost= isReferenceCompatible(cv1T1, cv2T2, isImpliedObject, point);
if (cost != null) { if (cost != null) {
cost.setReferenceBinding(refBindingType); cost.setReferenceBinding(refBindingType);
return cost; return cost;
} }
} }
// If the initializer expression has class type (i.e. T2 is a class type), where T1 is not
// reference-related to T2, and can be implicitly converted to an xvalue, class prvalue,
// or function lvalue of type 'cv3 T3', where 'cv1 T1' is reference-compatible with 'cv3 T3',
// then the reference is bound to the result of the conversion (or the appropriate base class
// subobject). If the reference is an rvalue reference and the second standard conversion
// sequence of the user-defined conversion sequence includes an lvalue-to-rvalue
// conversion, the program is ill-formed [this is why we pass illFormedIfLValue = true].
if (T2 instanceof ICPPClassType) {
if (udc != UDCMode.FORBIDDEN && isReferenceRelated(T1, T2, point) < 0) {
Cost cost= initializationByConversionForDirectReference(cv1T1, cv2T2, (ICPPClassType) T2, false, true, ctx, point);
if (cost != null) {
if (cost != Cost.NO_CONVERSION) {
cost.setReferenceBinding(refBindingType);
}
return cost;
}
}
}
// Otherwise, a temporary of type 'cv1 T1' is created and initialized from the initializer // Otherwise, a temporary of type 'cv1 T1' is created and initialized from the initializer
// expression using the rules for a non-reference copy initialization (8.5). The reference is then // expression using the rules for a non-reference copy initialization (8.5). The reference is then
// bound to the temporary. If T1 is reference-related to T2, cv1 must be the same cv-qualification // bound to the temporary.
// as, or greater cv-qualification than, cv2; otherwise, the program is ill-formed.
// 13.3.3.1.7 no temporary object when converting the implicit object parameter // 13.3.3.1.7 no temporary object when converting the implicit object parameter
if (!isImpliedObject && ctx != Context.REQUIRE_DIRECT_BINDING) { if (!isImpliedObject && ctx != Context.REQUIRE_DIRECT_BINDING) {
if (isReferenceRelated(T1, T2, point) < 0 || compareQualifications(cv1T1, cv2T2) >= 0) { Cost cost= nonReferenceConversion(valueCat, cv2T2, T1, udc, point);
Cost cost= nonReferenceConversion(valueCat, cv2T2, T1, udc, point); if (cost.converts()) {
if (cost.converts()) { cost.setReferenceBinding(refBindingType);
cost.setReferenceBinding(refBindingType);
}
return cost;
} }
boolean referenceRelated = isReferenceRelated(T1, T2, point) >= 0;
// If T1 is reference-related to T2, cv1 shall be the same cv-qualification as,
// or greater cv-qualification than, cv2.
if (referenceRelated && compareQualifications(cv1T1, cv2T2) < 0) {
return Cost.NO_CONVERSION;
}
// if T1 is reference-related to T2 and the reference is an rvalue reference,
// the initializer expression shall not be an lvalue.
if (referenceRelated && !isLValueRef && valueCat == ValueCategory.LVALUE) {
return Cost.NO_CONVERSION;
}
return cost;
} }
return Cost.NO_CONVERSION; return Cost.NO_CONVERSION;
} }
@ -263,9 +231,15 @@ public class Conversions {
/** /**
* C++0x: 13.3.1.6 Initialization by conversion function for direct reference binding * C++0x: 13.3.1.6 Initialization by conversion function for direct reference binding
* @param point * @param needLValue don't consider conversion functions that return rvalue references
* @param illFormedIfLValue make the conversion ill-formed (by returning Cost.NO_CONVERSION)
* if the best match is a conversion function that returns an
* lvalue reference
* Note that there's a difference between returning null and returning Cost.NO_CONVERSION:
* in the former case, the caller will continue trying other conversion methods.
*/ */
private static Cost initializationByConversionForDirectReference(final IType cv1T1, final IType cv2T2, final ICPPClassType T2, boolean needLValue, Context ctx, IASTNode point) private static Cost initializationByConversionForDirectReference(final IType cv1T1, final IType cv2T2, final ICPPClassType T2,
boolean needLValue, boolean illFormedIfLValue, Context ctx, IASTNode point)
throws DOMException { throws DOMException {
ICPPMethod[] fcns= SemanticUtil.getConversionOperators(T2, point); ICPPMethod[] fcns= SemanticUtil.getConversionOperators(T2, point);
Cost operatorCost= null; Cost operatorCost= null;
@ -281,21 +255,26 @@ public class Conversions {
final ICPPFunctionType ft = op.getType(); final ICPPFunctionType ft = op.getType();
IType t= getNestedType(ft.getReturnType(), TDEF); IType t= getNestedType(ft.getReturnType(), TDEF);
final boolean isLValueRef= t instanceof ICPPReferenceType && !((ICPPReferenceType) t).isRValueReference(); final boolean isLValueRef= t instanceof ICPPReferenceType && !((ICPPReferenceType) t).isRValueReference();
if (isLValueRef == needLValue) { // require an lvalue or rvalue if (needLValue && !isLValueRef) {
IType implicitParameterType= CPPSemantics.getImplicitParameterType(op); continue;
Cost udcCost= isReferenceCompatible(getNestedType(implicitParameterType, TDEF | REF), cv2T2, true, point); // expression type to implicit object type }
if (udcCost != null) { IType implicitParameterType= CPPSemantics.getImplicitParameterType(op);
// Make sure top-level cv-qualifiers are compared Cost udcCost= isReferenceCompatible(getNestedType(implicitParameterType, TDEF | REF), cv2T2, true, point); // expression type to implicit object type
udcCost.setReferenceBinding(ReferenceBinding.LVALUE_REF); if (udcCost != null) {
FunctionCost udcFuncCost= new FunctionCost(op, udcCost, point); // Make sure top-level cv-qualifiers are compared
int cmp= udcFuncCost.compareTo(null, bestUdcCost); udcCost.setReferenceBinding(ReferenceBinding.LVALUE_REF);
if (cmp <= 0) { FunctionCost udcFuncCost= new FunctionCost(op, udcCost, point);
Cost cost= isReferenceCompatible(cv1T1, getNestedType(t, TDEF | REF), false, point); // converted to target int cmp= udcFuncCost.compareTo(null, bestUdcCost);
if (cost != null) { if (cmp <= 0) {
bestUdcCost= udcFuncCost; Cost cost= isReferenceCompatible(cv1T1, getNestedType(t, TDEF | REF), false, point); // converted to target
ambiguousConversionOperator= cmp == 0; if (cost != null) {
operatorCost= cost; bestUdcCost= udcFuncCost;
operatorCost.setUserDefinedConversion(op); ambiguousConversionOperator= cmp == 0;
operatorCost= cost;
operatorCost.setUserDefinedConversion(op);
if (illFormedIfLValue && isLValueRef) {
operatorCost = Cost.NO_CONVERSION;
} }
} }
} }