-Wc++98-compat: diagnose if a reference is bound to a prvalue which does not

have an unambiguous accessible copying constructor; this is ill-formed in C++98.

llvm-svn: 142533
This commit is contained in:
Richard Smith 2011-10-19 16:55:56 +00:00
parent 2a80858903
commit c620f554b9
3 changed files with 173 additions and 66 deletions

View File

@ -1095,6 +1095,14 @@ def err_temp_copy_deleted : Error<
"of type %1 invokes deleted constructor">;
def err_temp_copy_incomplete : Error<
"copying a temporary object of incomplete type %0">;
def warn_cxx98_compat_temp_copy : Warning<
"%select{copying variable|copying parameter|returning object|throwing "
"object|copying member subobject|copying array element|allocating object|"
"copying temporary|initializing base subobject|initializing vector element}1 "
"of type %2 when binding a reference to a temporary would %select{invoke "
"an inaccessible constructor|find no viable constructor|find ambiguous "
"constructors|invoke a deleted constructor}0 in C++98">,
InGroup<CXX98Compat>, DefaultIgnore;
// C++11 decltype
def err_cannot_determine_declared_type_of_overloaded_function : Error<

View File

@ -3016,6 +3016,10 @@ static OverloadingResult TryRefInitWithConversionFunction(Sema &S,
return OR_Success;
}
static void CheckCXX98CompatAccessibleCopy(Sema &S,
const InitializedEntity &Entity,
Expr *CurInitExpr);
/// \brief Attempt reference initialization (C++0x [dcl.init.ref])
static void TryReferenceInitialization(Sema &S,
const InitializedEntity &Entity,
@ -3168,6 +3172,8 @@ static void TryReferenceInitialization(Sema &S,
// be callable whether or not the copy is actually done.
if (!S.getLangOptions().CPlusPlus0x && !S.getLangOptions().MicrosoftExt)
Sequence.AddExtraneousCopyToTemporary(cv2T2);
else if (S.getLangOptions().CPlusPlus0x)
CheckCXX98CompatAccessibleCopy(S, Entity, Initializer);
}
if (DerivedToBase)
@ -4090,6 +4096,78 @@ static bool shouldDestroyTemporary(const InitializedEntity &Entity) {
llvm_unreachable("missed an InitializedEntity kind?");
}
/// \brief Look for copy and move constructors and constructor templates, for
/// copying an object via direct-initialization (per C++11 [dcl.init]p16).
static void LookupCopyAndMoveConstructors(Sema &S,
OverloadCandidateSet &CandidateSet,
CXXRecordDecl *Class,
Expr *CurInitExpr) {
DeclContext::lookup_iterator Con, ConEnd;
for (llvm::tie(Con, ConEnd) = S.LookupConstructors(Class);
Con != ConEnd; ++Con) {
CXXConstructorDecl *Constructor = 0;
if ((Constructor = dyn_cast<CXXConstructorDecl>(*Con))) {
// Handle copy/moveconstructors, only.
if (!Constructor || Constructor->isInvalidDecl() ||
!Constructor->isCopyOrMoveConstructor() ||
!Constructor->isConvertingConstructor(/*AllowExplicit=*/true))
continue;
DeclAccessPair FoundDecl
= DeclAccessPair::make(Constructor, Constructor->getAccess());
S.AddOverloadCandidate(Constructor, FoundDecl,
&CurInitExpr, 1, CandidateSet);
continue;
}
// Handle constructor templates.
FunctionTemplateDecl *ConstructorTmpl = cast<FunctionTemplateDecl>(*Con);
if (ConstructorTmpl->isInvalidDecl())
continue;
Constructor = cast<CXXConstructorDecl>(
ConstructorTmpl->getTemplatedDecl());
if (!Constructor->isConvertingConstructor(/*AllowExplicit=*/true))
continue;
// FIXME: Do we need to limit this to copy-constructor-like
// candidates?
DeclAccessPair FoundDecl
= DeclAccessPair::make(ConstructorTmpl, ConstructorTmpl->getAccess());
S.AddTemplateOverloadCandidate(ConstructorTmpl, FoundDecl, 0,
&CurInitExpr, 1, CandidateSet, true);
}
}
/// \brief Get the location at which initialization diagnostics should appear.
static SourceLocation getInitializationLoc(const InitializedEntity &Entity,
Expr *Initializer) {
switch (Entity.getKind()) {
case InitializedEntity::EK_Result:
return Entity.getReturnLoc();
case InitializedEntity::EK_Exception:
return Entity.getThrowLoc();
case InitializedEntity::EK_Variable:
return Entity.getDecl()->getLocation();
case InitializedEntity::EK_ArrayElement:
case InitializedEntity::EK_Member:
case InitializedEntity::EK_Parameter:
case InitializedEntity::EK_Temporary:
case InitializedEntity::EK_New:
case InitializedEntity::EK_Base:
case InitializedEntity::EK_Delegating:
case InitializedEntity::EK_VectorElement:
case InitializedEntity::EK_ComplexElement:
case InitializedEntity::EK_BlockElement:
return Initializer->getLocStart();
}
llvm_unreachable("missed an InitializedEntity kind?");
}
/// \brief Make a (potentially elidable) temporary copy of the object
/// provided by the given initializer by calling the appropriate copy
/// constructor.
@ -4139,79 +4217,18 @@ static ExprResult CopyObject(Sema &S,
// of constructor initialization, while copy elision for exception handlers
// is handled by the run-time.
bool Elidable = CurInitExpr->isTemporaryObject(S.Context, Class);
SourceLocation Loc;
switch (Entity.getKind()) {
case InitializedEntity::EK_Result:
Loc = Entity.getReturnLoc();
break;
case InitializedEntity::EK_Exception:
Loc = Entity.getThrowLoc();
break;
case InitializedEntity::EK_Variable:
Loc = Entity.getDecl()->getLocation();
break;
case InitializedEntity::EK_ArrayElement:
case InitializedEntity::EK_Member:
case InitializedEntity::EK_Parameter:
case InitializedEntity::EK_Temporary:
case InitializedEntity::EK_New:
case InitializedEntity::EK_Base:
case InitializedEntity::EK_Delegating:
case InitializedEntity::EK_VectorElement:
case InitializedEntity::EK_ComplexElement:
case InitializedEntity::EK_BlockElement:
Loc = CurInitExpr->getLocStart();
break;
}
SourceLocation Loc = getInitializationLoc(Entity, CurInit.get());
// Make sure that the type we are copying is complete.
if (S.RequireCompleteType(Loc, T, S.PDiag(diag::err_temp_copy_incomplete)))
return move(CurInit);
// Perform overload resolution using the class's copy/move constructors.
DeclContext::lookup_iterator Con, ConEnd;
// Only consider constructors and constructor templates. Per
// C++0x [dcl.init]p16, second bullet to class types, this initialization
// is direct-initialization.
OverloadCandidateSet CandidateSet(Loc);
for (llvm::tie(Con, ConEnd) = S.LookupConstructors(Class);
Con != ConEnd; ++Con) {
// Only consider copy/move constructors and constructor templates. Per
// C++0x [dcl.init]p16, second bullet to class types, this
// initialization is direct-initialization.
CXXConstructorDecl *Constructor = 0;
if ((Constructor = dyn_cast<CXXConstructorDecl>(*Con))) {
// Handle copy/moveconstructors, only.
if (!Constructor || Constructor->isInvalidDecl() ||
!Constructor->isCopyOrMoveConstructor() ||
!Constructor->isConvertingConstructor(/*AllowExplicit=*/true))
continue;
DeclAccessPair FoundDecl
= DeclAccessPair::make(Constructor, Constructor->getAccess());
S.AddOverloadCandidate(Constructor, FoundDecl,
&CurInitExpr, 1, CandidateSet);
continue;
}
// Handle constructor templates.
FunctionTemplateDecl *ConstructorTmpl = cast<FunctionTemplateDecl>(*Con);
if (ConstructorTmpl->isInvalidDecl())
continue;
Constructor = cast<CXXConstructorDecl>(
ConstructorTmpl->getTemplatedDecl());
if (!Constructor->isConvertingConstructor(/*AllowExplicit=*/true))
continue;
// FIXME: Do we need to limit this to copy-constructor-like
// candidates?
DeclAccessPair FoundDecl
= DeclAccessPair::make(ConstructorTmpl, ConstructorTmpl->getAccess());
S.AddTemplateOverloadCandidate(ConstructorTmpl, FoundDecl, 0,
&CurInitExpr, 1, CandidateSet, true);
}
LookupCopyAndMoveConstructors(S, CandidateSet, Class, CurInitExpr);
bool HadMultipleCandidates = (CandidateSet.size() > 1);
@ -4303,6 +4320,61 @@ static ExprResult CopyObject(Sema &S,
return move(CurInit);
}
/// \brief Check whether elidable copy construction for binding a reference to
/// a temporary would have succeeded if we were building in C++98 mode, for
/// -Wc++98-compat.
static void CheckCXX98CompatAccessibleCopy(Sema &S,
const InitializedEntity &Entity,
Expr *CurInitExpr) {
assert(S.getLangOptions().CPlusPlus0x);
const RecordType *Record = CurInitExpr->getType()->getAs<RecordType>();
if (!Record)
return;
SourceLocation Loc = getInitializationLoc(Entity, CurInitExpr);
if (S.Diags.getDiagnosticLevel(diag::warn_cxx98_compat_temp_copy, Loc)
== DiagnosticsEngine::Ignored)
return;
// Find constructors which would have been considered.
OverloadCandidateSet CandidateSet(Loc);
LookupCopyAndMoveConstructors(
S, CandidateSet, cast<CXXRecordDecl>(Record->getDecl()), CurInitExpr);
// Perform overload resolution.
OverloadCandidateSet::iterator Best;
OverloadingResult OR = CandidateSet.BestViableFunction(S, Loc, Best);
PartialDiagnostic Diag = S.PDiag(diag::warn_cxx98_compat_temp_copy)
<< OR << (int)Entity.getKind() << CurInitExpr->getType()
<< CurInitExpr->getSourceRange();
switch (OR) {
case OR_Success:
S.CheckConstructorAccess(Loc, cast<CXXConstructorDecl>(Best->Function),
Best->FoundDecl.getAccess(), Diag);
// FIXME: Check default arguments as far as that's possible.
break;
case OR_No_Viable_Function:
S.Diag(Loc, Diag);
CandidateSet.NoteCandidates(S, OCD_AllCandidates, &CurInitExpr, 1);
break;
case OR_Ambiguous:
S.Diag(Loc, Diag);
CandidateSet.NoteCandidates(S, OCD_ViableCandidates, &CurInitExpr, 1);
break;
case OR_Deleted:
S.Diag(Loc, Diag);
S.Diag(Best->Function->getLocation(), diag::note_unavailable_here)
<< 1 << Best->Function->isDeleted();
break;
}
}
void InitializationSequence::PrintInitLocationNote(Sema &S,
const InitializedEntity &Entity) {
if (Entity.getKind() == InitializedEntity::EK_Parameter && Entity.getDecl()) {

View File

@ -186,3 +186,30 @@ struct FriendRedefinition {
};
FriendRedefinition<int> FriendRedef1;
FriendRedefinition<char> FriendRedef2; // expected-note {{requested here}}
namespace CopyCtorIssues {
struct Private {
Private();
private:
Private(const Private&); // expected-note {{declared private here}}
};
struct NoViable {
NoViable();
NoViable(NoViable&); // expected-note {{not viable}}
};
struct Ambiguous {
Ambiguous();
Ambiguous(const Ambiguous &, int = 0); // expected-note {{candidate}}
Ambiguous(const Ambiguous &, double = 0); // expected-note {{candidate}}
};
struct Deleted { // expected-note {{here}}
// Copy ctor implicitly defined as deleted because Private's copy ctor is
// inaccessible.
Private p;
};
const Private &a = Private(); // expected-warning {{copying variable of type 'CopyCtorIssues::Private' when binding a reference to a temporary would invoke an inaccessible constructor in C++98}}
const NoViable &b = NoViable(); // expected-warning {{copying variable of type 'CopyCtorIssues::NoViable' when binding a reference to a temporary would find no viable constructor in C++98}}
const Ambiguous &c = Ambiguous(); // expected-warning {{copying variable of type 'CopyCtorIssues::Ambiguous' when binding a reference to a temporary would find ambiguous constructors in C++98}}
const Deleted &d = Deleted(); // expected-warning {{copying variable of type 'CopyCtorIssues::Deleted' when binding a reference to a temporary would invoke a deleted constructor in C++98}}
}