1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-10 09:45:39 +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);
}
// double d = 2.0;
// double& rd = d; // rd refers to d
// const double& rcd = d; // rcd refers to d
// struct A { };
// struct B : public A { } b;
// A& ra = b; // ra refers to A subobject in b
// const A& rca = b; // rca refers to A subobject in b
// Note: the examples for 8.5.3/5 are written slightly differently
// than they appear in the standard. In the standard, the examples
// demonstrate the rules with variable initialiation, but CDT doesn't
// currently issue problem bindings for variable initialization,
// so in the tests the examples are rewritten to use function calls.
// 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 {
parse(getAboveComment(), ParserLanguage.CPP, true, 0);
}
// double& rd2 = 2.0; // error: not an lvalue and reference not const
// int i = 2;
// double& rd3 = i; // error: type mismatch and reference not const
// void f1(double&);
// void f2(double&);
// 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 {
parse(getAboveComment(), ParserLanguage.CPP, true, 0);
BindingAssertionHelper helper = getAssertionHelper(ParserLanguage.CPP);
helper.assertProblem("f1(2", "f1");
helper.assertProblem("f2(i", "f2");
}
// struct A { };
// struct B : public A { } b;
// extern B f();
// const A& rca = f(); // Either bound to the A subobject of the B rvalue,
// // or the entire B object is copied and the reference
// // is bound to the A subobject of the copy
// struct A { };
// struct B : public A { } b;
// extern B f();
// void f1(const A&);
// void f2(A&&);
// 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 {
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
// const volatile int cvi = 1;
// const int& r = cvi; // error: type qualifiers dropped
// void f1(const double&);
// void f2(double&&);
// 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 {
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; };

View file

@ -8281,7 +8281,7 @@ public class AST2CPPTests extends AST2TestBase {
// caref(b);
// dref(2.0); // error: not an lvalue 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.
// carref(f()); // same as above
// 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.assertProblem("dref(2.0)", 4);
bh.assertProblem("dref(i)", 4);
bh.assertProblem("drref(i)", 5);
bh.assertNonProblem("drref(i)", 5);
bh.assertNonProblem("caref(f())", 5);
bh.assertNonProblem("carref(f())", 6);
bh.assertNonProblem("caref(x)", 5);
@ -11102,6 +11102,16 @@ public class AST2CPPTests extends AST2TestBase {
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 auto waldo2 = 43;
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)
// and choosing the best one through overload resolution (13.3)),
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) {
cost.setReferenceBinding(refBindingType);
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
// shall be const), or the reference shall be an rvalue reference and the initializer expression
// shall be an rvalue or have function type.
boolean ok;
if (isLValueRef) {
ok = getCVQualifier(cv1T1) == CVQualifier.CONST;
} else {
ok= valueCat.isRValue() || T2 instanceof IFunctionType;
}
if (!ok) {
// shall be const), or the reference shall be an rvalue reference.
if (isLValueRef && getCVQualifier(cv1T1) != CVQualifier.CONST) {
return Cost.NO_CONVERSION;
}
// If T1 is a function type, then
if (T1 instanceof IFunctionType) {
// if T2 is the same type as T1, the reference is bound to the initializer expression lvalue;
if (T2.isSameType(T1)) {
Cost cost= new Cost(T1, T2, Rank.IDENTITY);
cost.setReferenceBinding(refBindingType);
return cost;
}
// 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 the initializer expression is an xvalue, class prvalue, array prvalue, or function lvalue
// and 'cv1 T1' is reference-compatible with 'cv2 T2', then the reference is bound to the value
// of the initializer expression (or the appropriate base class subobject).
if (valueCat == ValueCategory.XVALUE
|| (valueCat == ValueCategory.PRVALUE && (T2 instanceof ICPPClassType || T2 instanceof IArrayType))
|| (valueCat == ValueCategory.LVALUE && T2 instanceof ICPPFunctionType)) {
Cost cost = isReferenceCompatible(cv1T1, cv2T2, isImpliedObject, point);
if (cost != null) {
cost.setReferenceBinding(refBindingType);
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
// 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
// as, or greater cv-qualification than, cv2; otherwise, the program is ill-formed.
// bound to the temporary.
// 13.3.3.1.7 no temporary object when converting the implicit object parameter
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);
if (cost.converts()) {
cost.setReferenceBinding(refBindingType);
}
return cost;
Cost cost= nonReferenceConversion(valueCat, cv2T2, T1, udc, point);
if (cost.converts()) {
cost.setReferenceBinding(refBindingType);
}
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;
}
@ -263,9 +231,15 @@ public class Conversions {
/**
* 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 {
ICPPMethod[] fcns= SemanticUtil.getConversionOperators(T2, point);
Cost operatorCost= null;
@ -281,21 +255,26 @@ public class Conversions {
final ICPPFunctionType ft = op.getType();
IType t= getNestedType(ft.getReturnType(), TDEF);
final boolean isLValueRef= t instanceof ICPPReferenceType && !((ICPPReferenceType) t).isRValueReference();
if (isLValueRef == needLValue) { // require an lvalue or rvalue
IType implicitParameterType= CPPSemantics.getImplicitParameterType(op);
Cost udcCost= isReferenceCompatible(getNestedType(implicitParameterType, TDEF | REF), cv2T2, true, point); // expression type to implicit object type
if (udcCost != null) {
// Make sure top-level cv-qualifiers are compared
udcCost.setReferenceBinding(ReferenceBinding.LVALUE_REF);
FunctionCost udcFuncCost= new FunctionCost(op, udcCost, point);
int cmp= udcFuncCost.compareTo(null, bestUdcCost);
if (cmp <= 0) {
Cost cost= isReferenceCompatible(cv1T1, getNestedType(t, TDEF | REF), false, point); // converted to target
if (cost != null) {
bestUdcCost= udcFuncCost;
ambiguousConversionOperator= cmp == 0;
operatorCost= cost;
operatorCost.setUserDefinedConversion(op);
if (needLValue && !isLValueRef) {
continue;
}
IType implicitParameterType= CPPSemantics.getImplicitParameterType(op);
Cost udcCost= isReferenceCompatible(getNestedType(implicitParameterType, TDEF | REF), cv2T2, true, point); // expression type to implicit object type
if (udcCost != null) {
// Make sure top-level cv-qualifiers are compared
udcCost.setReferenceBinding(ReferenceBinding.LVALUE_REF);
FunctionCost udcFuncCost= new FunctionCost(op, udcCost, point);
int cmp= udcFuncCost.compareTo(null, bestUdcCost);
if (cmp <= 0) {
Cost cost= isReferenceCompatible(cv1T1, getNestedType(t, TDEF | REF), false, point); // converted to target
if (cost != null) {
bestUdcCost= udcFuncCost;
ambiguousConversionOperator= cmp == 0;
operatorCost= cost;
operatorCost.setUserDefinedConversion(op);
if (illFormedIfLValue && isLValueRef) {
operatorCost = Cost.NO_CONVERSION;
}
}
}