Implement initialization of a reference (C++ [dcl.init.ref]) as part
of copy initialization. Other pieces of the puzzle:
- Try/Perform-ImplicitConversion now handles implicit conversions
that don't involve references.
- Try/Perform-CopyInitialization uses
CheckSingleAssignmentConstraints for C. PerformCopyInitialization
is now used for all argument passing and returning values from a
function.
- Diagnose errors with declaring references and const values without
an initializer. (Uses a new Action callback, ActOnUninitializedDecl).
We do not yet have implicit conversion sequences for reference
binding, which means that we don't have any overloading support for
reference parameters yet.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58353 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 729bd7c..7e35ff4 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -679,3 +679,193 @@
// Set the init expression, handles conversions.
AddInitializerToDecl(Dcl, ExprTys[0]);
}
+
+/// CompareReferenceRelationship - Compare the two types T1 and T2 to
+/// determine whether they are reference-related,
+/// reference-compatible, reference-compatible with added
+/// qualification, or incompatible, for use in C++ initialization by
+/// reference (C++ [dcl.ref.init]p4). Neither type can be a reference
+/// type, and the first type (T1) is the pointee type of the reference
+/// type being initialized.
+Sema::ReferenceCompareResult
+Sema::CompareReferenceRelationship(QualType T1, QualType T2) {
+ assert(!T1->isReferenceType() && "T1 must be the pointee type of the reference type");
+ assert(!T2->isReferenceType() && "T2 cannot be a reference type");
+
+ T1 = Context.getCanonicalType(T1);
+ T2 = Context.getCanonicalType(T2);
+ QualType UnqualT1 = T1.getUnqualifiedType();
+ QualType UnqualT2 = T2.getUnqualifiedType();
+
+ // C++ [dcl.init.ref]p4:
+ // Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is
+ // reference-related to “cv2 T2” if T1 is the same type as T2, or
+ // T1 is a base class of T2.
+ //
+ // If neither of these conditions is met, the two types are not
+ // reference related at all.
+ if (UnqualT1 != UnqualT2 && !IsDerivedFrom(UnqualT2, UnqualT1))
+ return Ref_Incompatible;
+
+ // At this point, we know that T1 and T2 are reference-related (at
+ // least).
+
+ // C++ [dcl.init.ref]p4:
+ // "cv1 T1” is reference-compatible with “cv2 T2” if T1 is
+ // reference-related to T2 and cv1 is the same cv-qualification
+ // as, or greater cv-qualification than, cv2. For purposes of
+ // overload resolution, cases for which cv1 is greater
+ // cv-qualification than cv2 are identified as
+ // reference-compatible with added qualification (see 13.3.3.2).
+ if (T1.getCVRQualifiers() == T2.getCVRQualifiers())
+ return Ref_Compatible;
+ else if (T1.isMoreQualifiedThan(T2))
+ return Ref_Compatible_With_Added_Qualification;
+ else
+ return Ref_Related;
+}
+
+/// CheckReferenceInit - Check the initialization of a reference
+/// variable with the given initializer (C++ [dcl.init.ref]). Init is
+/// the initializer (either a simple initializer or an initializer
+/// list), and DeclType is the type of the declaration. When Complain
+/// is true, this routine will produce diagnostics (and return true)
+/// when the declaration cannot be initialized with the given
+/// initializer. When Complain is false, this routine will return true
+/// when the initialization cannot be performed, but will not produce
+/// any diagnostics or alter Init.
+bool Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, bool Complain) {
+ assert(DeclType->isReferenceType() && "Reference init needs a reference");
+
+ QualType T1 = DeclType->getAsReferenceType()->getPointeeType();
+ QualType T2 = Init->getType();
+
+ Expr::isLvalueResult InitLvalue = Init->isLvalue(Context);
+ ReferenceCompareResult RefRelationship = CompareReferenceRelationship(T1, T2);
+
+ // C++ [dcl.init.ref]p5:
+ // A reference to type “cv1 T1” is initialized by an expression
+ // of type “cv2 T2” as follows:
+
+ // -- If the initializer expression
+
+ bool BindsDirectly = false;
+ // -- is an lvalue (but is not a bit-field), and “cv1 T1” is
+ // reference-compatible with “cv2 T2,” or
+ if (InitLvalue == Expr::LV_Valid && !Init->isBitField() &&
+ RefRelationship >= Ref_Compatible) {
+ BindsDirectly = true;
+
+ if (!Complain) {
+ // FIXME: Binding to a subobject of the lvalue is going to require
+ // more AST annotation than this.
+ ImpCastExprToType(Init, T1);
+ }
+ }
+
+ // -- has a class type (i.e., T2 is a class type) and can be
+ // implicitly converted to an lvalue of type “cv3 T3,”
+ // where “cv1 T1” is reference-compatible with “cv3 T3”
+ // 92) (this conversion is selected by enumerating the
+ // applicable conversion functions (13.3.1.6) and choosing
+ // the best one through overload resolution (13.3)),
+ // FIXME: Implement this second bullet, once we have conversion
+ // functions.
+
+ if (BindsDirectly) {
+ // C++ [dcl.init.ref]p4:
+ // [...] In all cases where the reference-related or
+ // reference-compatible relationship of two types is used to
+ // establish the validity of a reference binding, and T1 is a
+ // base class of T2, a program that necessitates such a binding
+ // is ill-formed if T1 is an inaccessible (clause 11) or
+ // ambiguous (10.2) base class of T2.
+ //
+ // Note that we only check this condition when we're allowed to
+ // complain about errors, because we should not be checking for
+ // ambiguity (or inaccessibility) unless the reference binding
+ // actually happens.
+ if (Complain &&
+ (Context.getCanonicalType(T1).getUnqualifiedType()
+ != Context.getCanonicalType(T2).getUnqualifiedType()) &&
+ CheckDerivedToBaseConversion(T2, T1, Init->getSourceRange().getBegin(),
+ Init->getSourceRange()))
+ return true;
+
+ return false;
+ }
+
+ // -- Otherwise, the reference shall be to a non-volatile const
+ // type (i.e., cv1 shall be const).
+ if (T1.getCVRQualifiers() != QualType::Const) {
+ if (Complain)
+ Diag(Init->getSourceRange().getBegin(),
+ diag::err_not_reference_to_const_init,
+ T1.getAsString(),
+ InitLvalue != Expr::LV_Valid? "temporary" : "value",
+ T2.getAsString(), Init->getSourceRange());
+ return true;
+ }
+
+ // -- If the initializer expression is an rvalue, with T2 a
+ // class type, and “cv1 T1” is reference-compatible with
+ // “cv2 T2,” the reference is bound in one of the
+ // following ways (the choice is implementation-defined):
+ //
+ // -- The reference is bound to the object represented by
+ // the rvalue (see 3.10) or to a sub-object within that
+ // object.
+ //
+ // -- A temporary of type “cv1 T2” [sic] is created, and
+ // a constructor is called to copy the entire rvalue
+ // object into the temporary. The reference is bound to
+ // the temporary or to a sub-object within the
+ // temporary.
+ //
+ //
+ // The constructor that would be used to make the copy
+ // shall be callable whether or not the copy is actually
+ // done.
+ //
+ // Note that C++0x [dcl.ref.init]p5 takes away this implementation
+ // freedom, so we will always take the first option and never build
+ // a temporary in this case. FIXME: We will, however, have to check
+ // for the presence of a copy constructor in C++98/03 mode.
+ if (InitLvalue != Expr::LV_Valid && T2->isRecordType() &&
+ RefRelationship >= Ref_Compatible) {
+ if (!Complain) {
+ // FIXME: Binding to a subobject of the rvalue is going to require
+ // more AST annotation than this.
+ ImpCastExprToType(Init, T1);
+ }
+ return false;
+ }
+
+ // -- 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.
+ if (RefRelationship == Ref_Related) {
+ // If cv1 == cv2 or cv1 is a greater cv-qualified than cv2, then
+ // we would be reference-compatible or reference-compatible with
+ // added qualification. But that wasn't the case, so the reference
+ // initialization fails.
+ if (Complain)
+ Diag(Init->getSourceRange().getBegin(),
+ diag::err_reference_init_drops_quals,
+ T1.getAsString(),
+ InitLvalue != Expr::LV_Valid? "temporary" : "value",
+ T2.getAsString(), Init->getSourceRange());
+ return true;
+ }
+
+ // Actually try to convert the initializer to T1.
+ if (Complain)
+ return PerformImplicitConversion(Init, T1);
+ else
+ return (TryImplicitConversion(Init, T1).ConversionKind
+ == ImplicitConversionSequence::BadConversion);
+}