diff options
-rw-r--r-- | include/clang/AST/Expr.h | 3 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticKinds.def | 10 | ||||
-rw-r--r-- | include/clang/Parse/Action.h | 7 | ||||
-rw-r--r-- | lib/AST/Expr.cpp | 11 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 35 | ||||
-rw-r--r-- | lib/Sema/SemaChecking.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 50 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 190 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 11 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaInherit.cpp | 27 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 115 | ||||
-rw-r--r-- | lib/Sema/SemaStmt.cpp | 11 | ||||
-rw-r--r-- | test/Parser/cxx-reference.cpp | 8 | ||||
-rw-r--r-- | test/SemaCXX/overload-call.cpp | 11 | ||||
-rw-r--r-- | test/SemaCXX/references.cpp | 60 |
17 files changed, 483 insertions, 72 deletions
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 20f51761dc..d6aa5bfc54 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -105,6 +105,7 @@ public: isModifiableLvalueResult isModifiableLvalue(ASTContext &Ctx) const; bool isNullPointerConstant(ASTContext &Ctx) const; + bool isBitField(); /// getIntegerConstantExprValue() - Return the value of an integer /// constant expression. The expression must be a valid integer @@ -153,7 +154,7 @@ public: Expr* IgnoreParens(); /// IgnoreParenCasts - Ignore parentheses and casts. Strip off any ParenExpr - /// or CastExprs or ImplicitCastExprs, returning their operand. + /// or CastExprs, returning their operand. Expr *IgnoreParenCasts(); const Expr* IgnoreParens() const { diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 9f59f947c6..877452dc91 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -676,6 +676,16 @@ DIAG(err_not_integral_type_bitfield, ERROR, DIAG(err_member_initialization, ERROR, "'%0' can only be initialized if it is a static const integral data member") +// C++ initialization +DIAG(err_not_reference_to_const_init, ERROR, + "non-const reference to type '%0' cannot be initialized with a %1 of type '%2'") +DIAG(err_reference_init_drops_quals, ERROR, + "initialization of reference to type '%0' with a %1 of type '%2' drops qualifiers") +DIAG(err_reference_var_requires_init, ERROR, + "declaration of reference variable '%0' requires an initializer") +DIAG(err_const_var_requires_init, ERROR, + "declaration of const variable '%0' requires an initializer") + // Attributes DIAG(err_attribute_wrong_number_arguments, ERROR, "attribute requires %0 argument(s)") diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 8c414e49ae..7564aefdc8 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -130,6 +130,13 @@ public: virtual void AddInitializerToDecl(DeclTy *Dcl, ExprTy *Init) { return; } + + /// ActOnUninitializedDecl - This action is called immediately after + /// ActOnDeclarator (when an initializer is *not* present). + virtual void ActOnUninitializedDecl(DeclTy *Dcl) { + return; + } + /// FinalizeDeclaratorGroup - After a sequence of declarators are parsed, this /// gives the actions implementation a chance to process the group as a whole. virtual DeclTy *FinalizeDeclaratorGroup(Scope *S, DeclTy *Group) { diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 30e06a6de5..44d6ca1012 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -397,8 +397,7 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const { // C++ [expr.call]p10: // A function call is an lvalue if and only if the result type // is a reference. - QualType CalleeType - = cast<CallExpr>(this)->getCallee()->IgnoreParens()->getType(); + QualType CalleeType = cast<CallExpr>(this)->getCallee()->getType(); if (const PointerType *FnTypePtr = CalleeType->getAsPointerType()) if (const FunctionType *FnType = FnTypePtr->getPointeeType()->getAsFunctionType()) @@ -1106,6 +1105,14 @@ bool Expr::isNullPointerConstant(ASTContext &Ctx) const { return isIntegerConstantExpr(Val, Ctx, 0, true) && Val == 0; } +/// isBitField - Return true if this expression is a bit-field. +bool Expr::isBitField() { + Expr *E = this->IgnoreParenCasts(); + if (MemberExpr *MemRef = dyn_cast<MemberExpr>(E)) + return MemRef->getMemberDecl()->isBitField(); + return false; +} + unsigned ExtVectorElementExpr::getNumElements() const { if (const VectorType *VT = getType()->getAsVectorType()) return VT->getNumElements(); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 6bcb90b8ae..243a15f3db 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -310,6 +310,8 @@ ParseInitDeclaratorListAfterFirstDeclarator(Declarator &D) { &Exprs[0], Exprs.size(), &CommaLocs[0], RParenLoc); } + } else { + Actions.ActOnUninitializedDecl(LastDeclInGroup); } // If we don't have a comma, it is either the end of the list (a ';') or an diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index f84b5c5e71..87d7cd139f 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -279,6 +279,7 @@ private: SourceLocation EqualLoc, ExprTy *defarg); void AddInitializerToDecl(DeclTy *dcl, ExprTy *init); + void ActOnUninitializedDecl(DeclTy *dcl); virtual DeclTy *FinalizeDeclaratorGroup(Scope *S, DeclTy *Group); virtual DeclTy *ActOnStartOfFunctionDef(Scope *S, Declarator &D); @@ -369,7 +370,7 @@ private: /// C++ Overloading. bool IsOverload(FunctionDecl *New, Decl* OldD, OverloadedFunctionDecl::function_iterator &MatchedDecl); - ImplicitConversionSequence TryCopyInitialization(Expr* From, QualType ToType); + ImplicitConversionSequence TryImplicitConversion(Expr* From, QualType ToType); bool IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType); bool IsFloatingPointPromotion(QualType FromType, QualType ToType); bool IsPointerConversion(Expr *From, QualType FromType, QualType ToType, @@ -393,6 +394,10 @@ private: CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1, const StandardConversionSequence& SCS2); + ImplicitConversionSequence TryCopyInitialization(Expr* From, QualType ToType); + bool PerformCopyInitialization(Expr *&From, QualType ToType, + const char *Flavor); + /// OverloadingResult - Capture the result of performing overload /// resolution. enum OverloadingResult { @@ -1111,6 +1116,34 @@ private: StringLiteral *IsStringLiteralInit(Expr *Init, QualType DeclType); bool CheckStringLiteralInit(StringLiteral *strLiteral, QualType &DeclT); + // type checking C++ declaration initializers (C++ [dcl.init]). + + /// ReferenceCompareResult - Expresses the result of comparing two + /// types (cv1 T1 and cv2 T2) to determine their compatibility for the + /// purposes of initialization by reference (C++ [dcl.init.ref]p4). + enum ReferenceCompareResult { + /// Ref_Incompatible - The two types are incompatible, so direct + /// reference binding is not possible. + Ref_Incompatible = 0, + /// Ref_Related - The two types are reference-related, which means + /// that their unqualified forms (T1 and T2) are either the same + /// or T1 is a base class of T2. + Ref_Related, + /// Ref_Compatible_With_Added_Qualification - The two types are + /// reference-compatible with added qualification, meaning that + /// they are reference-compatible and the qualifiers on T1 (cv1) + /// are greater than the qualifiers on T2 (cv2). + Ref_Compatible_With_Added_Qualification, + /// Ref_Compatible - The two types are reference-compatible and + /// have equivalent qualifiers (cv1 == cv2). + Ref_Compatible + }; + + ReferenceCompareResult CompareReferenceRelationship(QualType T1, QualType T2); + + bool CheckReferenceInit(Expr *&simpleInit_or_initList, QualType &declType, + bool Complain = true); + /// CheckCastTypes - Check type constraints for casting between types. bool CheckCastTypes(SourceRange TyRange, QualType CastTy, Expr *&CastExpr); diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index ac41bfc431..816d6fd05d 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -886,7 +886,7 @@ static DeclRefExpr* EvalVal(Expr *E) { DeclRefExpr *DR = cast<DeclRefExpr>(E); if (VarDecl *V = dyn_cast<VarDecl>(DR->getDecl())) - if(V->hasLocalStorage()) return DR; + if(V->hasLocalStorage() && !V->getType()->isReferenceType()) return DR; return NULL; } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 71c0ceb57f..78f0d8f8be 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -628,6 +628,13 @@ StringLiteral *Sema::IsStringLiteralInit(Expr *Init, QualType DeclType) { } bool Sema::CheckInitializerTypes(Expr *&Init, QualType &DeclType) { + // C++ [dcl.init.ref]p1: + // A variable declared to be a T&, that is “reference to type T” + // (8.3.2), shall be initialized by an object, or function, of + // type T or by an object that can be converted into a T. + if (DeclType->isReferenceType()) + return CheckReferenceInit(Init, DeclType); + // C99 6.7.8p3: The type of the entity to be initialized shall be an array // of unknown size ("[]") or an object type that is not a variable array type. if (const VariableArrayType *VAT = Context.getAsVariableArrayType(DeclType)) @@ -1516,6 +1523,49 @@ void Sema::AddInitializerToDecl(DeclTy *dcl, ExprTy *init) { return; } +void Sema::ActOnUninitializedDecl(DeclTy *dcl) { + Decl *RealDecl = static_cast<Decl *>(dcl); + + if (VarDecl *Var = dyn_cast<VarDecl>(RealDecl)) { + QualType Type = Var->getType(); + // C++ [dcl.init.ref]p3: + // The initializer can be omitted for a reference only in a + // parameter declaration (8.3.5), in the declaration of a + // function return type, in the declaration of a class member + // within its class declaration (9.2), and where the extern + // specifier is explicitly used. + if (Type->isReferenceType() && Var->getStorageClass() != VarDecl::Extern) + Diag(Var->getLocation(), + diag::err_reference_var_requires_init, + Var->getName(), + SourceRange(Var->getLocation(), Var->getLocation())); + + // C++ [dcl.init]p9: + // + // If no initializer is specified for an object, and the + // object is of (possibly cv-qualified) non-POD class type (or + // array thereof), the object shall be default-initialized; if + // the object is of const-qualified type, the underlying class + // type shall have a user-declared default + // constructor. Otherwise, if no initializer is specified for + // an object, the object and its subobjects, if any, have an + // indeterminate initial value; if the object or any of its + // subobjects are of const-qualified type, the program is + // ill-formed. + // + // This isn't technically an error in C, so we don't diagnose it. + // + // FIXME: Actually perform the POD/user-defined default + // constructor check. + if (getLangOptions().CPlusPlus && + Context.getCanonicalType(Type).isConstQualified()) + Diag(Var->getLocation(), + diag::err_const_var_requires_init, + Var->getName(), + SourceRange(Var->getLocation(), Var->getLocation())); + } +} + /// The declarators are chained together backwards, reverse the list. Sema::DeclTy *Sema::FinalizeDeclaratorGroup(Scope *S, DeclTy *group) { // Often we have single declarators, handle them quickly. diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 729bd7c35c..7e35ff4df3 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -679,3 +679,193 @@ void Sema::AddCXXDirectInitializerToDecl(DeclTy *Dcl, SourceLocation LParenLoc, // 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); +} diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index a4fae9117e..831765376b 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1174,14 +1174,11 @@ ActOnCallExpr(ExprTy *fn, SourceLocation LParenLoc, Arg = new CXXDefaultArgExpr(FDecl->getParamDecl(i)); QualType ArgType = Arg->getType(); - // Compute implicit casts from the operand to the formal argument type. - AssignConvertType ConvTy = - CheckSingleAssignmentConstraints(ProtoArgType, Arg); - TheCall->setArg(i, Arg); - - if (DiagnoseAssignmentResult(ConvTy, Arg->getLocStart(), ProtoArgType, - ArgType, Arg, "passing")) + // Pass the argument. + if (PerformCopyInitialization(Arg, ProtoArgType, "passing")) return true; + + TheCall->setArg(i, Arg); } // If this is a variadic call, handle args passed through "...". diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 7a5e2dd4e9..209914b45c 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -567,7 +567,7 @@ Sema::IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType) { bool Sema::PerformImplicitConversion(Expr *&From, QualType ToType) { - ImplicitConversionSequence ICS = TryCopyInitialization(From, ToType); + ImplicitConversionSequence ICS = TryImplicitConversion(From, ToType); switch (ICS.ConversionKind) { case ImplicitConversionSequence::StandardConversion: if (PerformImplicitConversion(From, ToType, ICS.Standard)) diff --git a/lib/Sema/SemaInherit.cpp b/lib/Sema/SemaInherit.cpp index a676b64ff8..ce48a43111 100644 --- a/lib/Sema/SemaInherit.cpp +++ b/lib/Sema/SemaInherit.cpp @@ -44,33 +44,32 @@ void BasePaths::clear() { ScratchPath.clear(); } -/// IsDerivedFrom - Determine whether the class type Derived is -/// derived from the class type Base, ignoring qualifiers on Base and -/// Derived. This routine does not assess whether an actual conversion -/// from a Derived* to a Base* is legal, because it does not account -/// for ambiguous conversions or conversions to private/protected bases. +/// IsDerivedFrom - Determine whether the type Derived is derived from +/// the type Base, ignoring qualifiers on Base and Derived. This +/// routine does not assess whether an actual conversion from a +/// Derived* to a Base* is legal, because it does not account for +/// ambiguous conversions or conversions to private/protected bases. bool Sema::IsDerivedFrom(QualType Derived, QualType Base) { BasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/false); return IsDerivedFrom(Derived, Base, Paths); } -/// IsDerivedFrom - Determine whether the class type Derived is -/// derived from the class type Base, ignoring qualifiers on Base and -/// Derived. This routine does not assess whether an actual conversion -/// from a Derived* to a Base* is legal, because it does not account -/// for ambiguous conversions or conversions to private/protected +/// IsDerivedFrom - Determine whether the type Derived is derived from +/// the type Base, ignoring qualifiers on Base and Derived. This +/// routine does not assess whether an actual conversion from a +/// Derived* to a Base* is legal, because it does not account for +/// ambiguous conversions or conversions to private/protected /// bases. This routine will use Paths to determine if there are /// ambiguous paths (if @c Paths.isFindingAmbiguities()) and record -/// information about all of the paths (if -/// @c Paths.isRecordingPaths()). +/// information about all of the paths (if @c Paths.isRecordingPaths()). bool Sema::IsDerivedFrom(QualType Derived, QualType Base, BasePaths &Paths) { bool FoundPath = false; Derived = Context.getCanonicalType(Derived).getUnqualifiedType(); Base = Context.getCanonicalType(Base).getUnqualifiedType(); - assert(Derived->isRecordType() && "IsDerivedFrom requires a class type"); - assert(Base->isRecordType() && "IsDerivedFrom requires a class type"); + if (!Derived->isRecordType() || !Base->isRecordType()) + return false; if (Derived == Base) return false; diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 7c78365686..8e765485b1 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -312,11 +312,10 @@ Sema::IsOverload(FunctionDecl *New, Decl* OldD, } } -/// TryCopyInitialization - Attempt to copy-initialize a value of the -/// given type (ToType) from the given expression (Expr), as one would -/// do when copy-initializing a function parameter. This function -/// returns an implicit conversion sequence that can be used to -/// perform the initialization. Given +/// TryImplicitConversion - Attempt to perform an implicit conversion +/// from the given expression (Expr) to the given type (ToType). This +/// function returns an implicit conversion sequence that can be used +/// to perform the initialization. Given /// /// void f(float f); /// void g(int i) { f(i); } @@ -332,7 +331,7 @@ Sema::IsOverload(FunctionDecl *New, Decl* OldD, /// but will instead return an implicit conversion sequence of kind /// "BadConversion". ImplicitConversionSequence -Sema::TryCopyInitialization(Expr* From, QualType ToType) +Sema::TryImplicitConversion(Expr* From, QualType ToType) { ImplicitConversionSequence ICS; @@ -343,32 +342,6 @@ Sema::TryCopyInitialization(Expr* From, QualType ToType) ICS.Standard.Deprecated = false; ICS.Standard.FromTypePtr = FromType.getAsOpaquePtr(); - if (const ReferenceType *ToTypeRef = ToType->getAsReferenceType()) { - // FIXME: This is a hack to deal with the initialization of - // references the way that the C-centric code elsewhere deals with - // references, by only allowing them if the referred-to type is - // exactly the same. This means that we're only handling the - // direct-binding case. The code will be replaced by an - // implementation of C++ 13.3.3.1.4 once we have the - // initialization of references implemented. - QualType ToPointee = Context.getCanonicalType(ToTypeRef->getPointeeType()); - - // Get down to the canonical type that we're converting from. - if (const ReferenceType *FromTypeRef = FromType->getAsReferenceType()) - FromType = FromTypeRef->getPointeeType(); - FromType = Context.getCanonicalType(FromType); - - ICS.Standard.First = ICK_Identity; - ICS.Standard.Second = ICK_Identity; - ICS.Standard.Third = ICK_Identity; - ICS.Standard.ToTypePtr = ToType.getAsOpaquePtr(); - - if (FromType != ToPointee) - ICS.ConversionKind = ImplicitConversionSequence::BadConversion; - - return ICS; - } - // The first conversion can be an lvalue-to-rvalue conversion, // array-to-pointer conversion, or function-to-pointer conversion // (C++ 4p1). @@ -482,18 +455,37 @@ Sema::TryCopyInitialization(Expr* From, QualType ToType) ICS.Standard.Second = ICK_Identity; } + QualType CanonFrom; + QualType CanonTo; // The third conversion can be a qualification conversion (C++ 4p1). if (IsQualificationConversion(FromType, ToType)) { ICS.Standard.Third = ICK_Qualification; FromType = ToType; + CanonFrom = Context.getCanonicalType(FromType); + CanonTo = Context.getCanonicalType(ToType); } else { // No conversion required ICS.Standard.Third = ICK_Identity; + + // C++ [dcl.init]p14 last bullet: + // Note: an expression of type "cv1 T" can initialize an object + // of type “cv2 T” independently of the cv-qualifiers cv1 and + // cv2. -- end note] + // + // FIXME: Where is the normative text? + CanonFrom = Context.getCanonicalType(FromType); + CanonTo = Context.getCanonicalType(ToType); + if (!FromType->isRecordType() && + CanonFrom.getUnqualifiedType() == CanonTo.getUnqualifiedType() && + CanonFrom.getCVRQualifiers() != CanonTo.getCVRQualifiers()) { + FromType = ToType; + CanonFrom = CanonTo; + } } // If we have not converted the argument type to the parameter type, // this is a bad conversion sequence. - if (Context.getCanonicalType(FromType) != Context.getCanonicalType(ToType)) + if (CanonFrom != CanonTo) ICS.ConversionKind = ImplicitConversionSequence::BadConversion; ICS.Standard.ToTypePtr = FromType.getAsOpaquePtr(); @@ -1054,6 +1046,63 @@ Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1, return ImplicitConversionSequence::Indistinguishable; } +/// TryCopyInitialization - Try to copy-initialize a value of type +/// ToType from the expression From. Return the implicit conversion +/// sequence required to pass this argument, which may be a bad +/// conversion sequence (meaning that the argument cannot be passed to +/// a parameter of this type). This is user for argument passing, +ImplicitConversionSequence +Sema::TryCopyInitialization(Expr *From, QualType ToType) { + if (!getLangOptions().CPlusPlus) { + // In C, argument passing is the same as performing an assignment. + AssignConvertType ConvTy = + CheckSingleAssignmentConstraints(ToType, From); + ImplicitConversionSequence ICS; + if (getLangOptions().NoExtensions? ConvTy != Compatible + : ConvTy == Incompatible) + ICS.ConversionKind = ImplicitConversionSequence::BadConversion; + else + ICS.ConversionKind = ImplicitConversionSequence::StandardConversion; + return ICS; + } else if (ToType->isReferenceType()) { + ImplicitConversionSequence ICS; + if (CheckReferenceInit(From, ToType, /*Complain=*/false)) + ICS.ConversionKind = ImplicitConversionSequence::BadConversion; + else + ICS.ConversionKind = ImplicitConversionSequence::StandardConversion; + return ICS; + } else { + return TryImplicitConversion(From, ToType); + } +} + +/// PerformArgumentPassing - Pass the argument Arg into a parameter of +/// type ToType. Returns true (and emits a diagnostic) if there was +/// an error, returns false if the initialization succeeded. +bool Sema::PerformCopyInitialization(Expr *&From, QualType ToType, + const char* Flavor) { + if (!getLangOptions().CPlusPlus) { + // In C, argument passing is the same as performing an assignment. + QualType FromType = From->getType(); + AssignConvertType ConvTy = + CheckSingleAssignmentConstraints(ToType, From); + + return DiagnoseAssignmentResult(ConvTy, From->getLocStart(), ToType, + FromType, From, Flavor); + } else if (ToType->isReferenceType()) { + return CheckReferenceInit(From, ToType); + } else { + if (PerformImplicitConversion(From, ToType)) + return Diag(From->getSourceRange().getBegin(), + diag::err_typecheck_convert_incompatible, + ToType.getAsString(), From->getType().getAsString(), + Flavor, + From->getSourceRange()); + else + return false; + } +} + /// AddOverloadCandidate - Adds the given function to the set of /// candidate functions, using the given function call arguments. void diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index f75758ebb0..e7ccdda272 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -693,7 +693,7 @@ Sema::ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope) { return new BreakStmt(BreakLoc); } -/// ActOnBlockReturnStmt - Utilty routine to figure out block's return type. +/// ActOnBlockReturnStmt - Utility routine to figure out block's return type. /// Action::StmtResult Sema::ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { @@ -782,10 +782,11 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprTy *rex) { // C99 6.8.6.4p3(136): The return statement is not an assignment. The // overlap restriction of subclause 6.5.16.1 does not apply to the case of // function return. - AssignConvertType ConvTy = CheckSingleAssignmentConstraints(FnRetType, - RetValExp); - if (DiagnoseAssignmentResult(ConvTy, ReturnLoc, FnRetType, - RetValType, RetValExp, "returning")) + + // In C++ the return statement is handled via a copy initialization. + // the C version of which boils down to + // CheckSingleAssignmentConstraints. + if (PerformCopyInitialization(RetValExp, FnRetType, "returning")) return true; if (RetValExp) CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc); diff --git a/test/Parser/cxx-reference.cpp b/test/Parser/cxx-reference.cpp index 4f3b58c5d5..8d65defe7d 100644 --- a/test/Parser/cxx-reference.cpp +++ b/test/Parser/cxx-reference.cpp @@ -3,6 +3,8 @@ extern char *bork; char *& bar = bork; +int val; + void foo(int &a) { } @@ -11,7 +13,7 @@ typedef int & A; void g(const A aref) { } -int & const X; // expected-error {{'const' qualifier may not be applied to a reference}} -int & volatile Y; // expected-error {{'volatile' qualifier may not be applied to a reference}} -int & const volatile Z; /* expected-error {{'const' qualifier may not be applied}} \ +int & const X = val; // expected-error {{'const' qualifier may not be applied to a reference}} +int & volatile Y = val; // expected-error {{'volatile' qualifier may not be applied to a reference}} +int & const volatile Z = val; /* expected-error {{'const' qualifier may not be applied}} \ expected-error {{'volatile' qualifier may not be applied}} */ diff --git a/test/SemaCXX/overload-call.cpp b/test/SemaCXX/overload-call.cpp index 1bd6496612..663474d328 100644 --- a/test/SemaCXX/overload-call.cpp +++ b/test/SemaCXX/overload-call.cpp @@ -214,3 +214,14 @@ void test_derived(B* b, B const* bc, C* c, const C* cc, void* v, D* d) { char* d8 = derived3(d); } + +// Test overloading of references. < |