Add context note to diagnostics that occur while declaring an implicit special member function.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@296020 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Richard Smith 2017-02-23 21:43:43 +00:00
parent 11e7407389
commit a793abf092
7 changed files with 128 additions and 55 deletions

View File

@ -1624,7 +1624,14 @@ def err_covariant_return_type_class_type_more_qualified : Error<
"return type of virtual function %0 is not covariant with the return type of "
"the function it overrides (class type %1 is more qualified than class "
"type %2">;
// C++ implicit special member functions
def note_in_declaration_of_implicit_special_member : Note<
"while declaring the implicit "
"%select{default constructor|copy constructor|move constructor|"
"copy assignment operator|move assignment operator|destructor}1"
" for %0">;
// C++ constructors
def err_constructor_cannot_be : Error<"constructor cannot be declared '%0'">;
def err_invalid_qualified_constructor : Error<

View File

@ -6881,28 +6881,42 @@ public:
/// We are instantiating the exception specification for a function
/// template which was deferred until it was needed.
ExceptionSpecInstantiation
ExceptionSpecInstantiation,
/// We are declaring an implicit special member function.
DeclaringSpecialMember,
} Kind;
/// \brief The point of instantiation within the source code.
/// \brief Was the enclosing context a non-instantiation SFINAE context?
bool SavedInNonInstantiationSFINAEContext;
/// \brief The point of instantiation or synthesis within the source code.
SourceLocation PointOfInstantiation;
/// \brief The entity that is being synthesized.
Decl *Entity;
/// \brief The template (or partial specialization) in which we are
/// performing the instantiation, for substitutions of prior template
/// arguments.
NamedDecl *Template;
/// \brief The entity that is being instantiated.
Decl *Entity;
/// \brief The list of template arguments we are substituting, if they
/// are not part of the entity.
const TemplateArgument *TemplateArgs;
/// \brief The number of template arguments in TemplateArgs.
unsigned NumTemplateArgs;
// FIXME: Wrap this union around more members, or perhaps store the
// kind-specific members in the RAII object owning the context.
union {
/// \brief The number of template arguments in TemplateArgs.
unsigned NumTemplateArgs;
/// \brief The special member being declared or defined.
CXXSpecialMember SpecialMember;
};
ArrayRef<TemplateArgument> template_arguments() const {
assert(Kind != DeclaringSpecialMember);
return {TemplateArgs, NumTemplateArgs};
}
@ -6916,7 +6930,7 @@ public:
SourceRange InstantiationRange;
CodeSynthesisContext()
: Kind(TemplateInstantiation), Template(nullptr), Entity(nullptr),
: Kind(TemplateInstantiation), Entity(nullptr), Template(nullptr),
TemplateArgs(nullptr), NumTemplateArgs(0), DeductionInfo(nullptr) {}
/// \brief Determines whether this template is an actual instantiation
@ -7134,7 +7148,6 @@ public:
Sema &SemaRef;
bool Invalid;
bool AlreadyInstantiating;
bool SavedInNonInstantiationSFINAEContext;
bool CheckInstantiationDepth(SourceLocation PointOfInstantiation,
SourceRange InstantiationRange);
@ -7151,6 +7164,9 @@ public:
operator=(const InstantiatingTemplate&) = delete;
};
void pushCodeSynthesisContext(CodeSynthesisContext Ctx);
void popCodeSynthesisContext();
/// Determine whether we are currently performing template instantiation.
bool inTemplateInstantiation() const {
return CodeSynthesisContexts.size() > NonInstantiationEntries;

View File

@ -10164,19 +10164,34 @@ struct DeclaringSpecialMember {
bool WasAlreadyBeingDeclared;
DeclaringSpecialMember(Sema &S, CXXRecordDecl *RD, Sema::CXXSpecialMember CSM)
: S(S), D(RD, CSM), SavedContext(S, RD) {
: S(S), D(RD, CSM), SavedContext(S, RD) {
WasAlreadyBeingDeclared = !S.SpecialMembersBeingDeclared.insert(D).second;
if (WasAlreadyBeingDeclared)
// This almost never happens, but if it does, ensure that our cache
// doesn't contain a stale result.
S.SpecialMemberCache.clear();
// FIXME: Register a note to be produced if we encounter an error while
// declaring the special member.
else {
// Register a note to be produced if we encounter an error while
// declaring the special member.
Sema::CodeSynthesisContext Ctx;
Ctx.Kind = Sema::CodeSynthesisContext::DeclaringSpecialMember;
// FIXME: We don't have a location to use here. Using the class's
// location maintains the fiction that we declare all special members
// with the class, but (1) it's not clear that lying about that helps our
// users understand what's going on, and (2) there may be outer contexts
// on the stack (some of which are relevant) and printing them exposes
// our lies.
Ctx.PointOfInstantiation = RD->getLocation();
Ctx.Entity = RD;
Ctx.SpecialMember = CSM;
S.pushCodeSynthesisContext(Ctx);
}
}
~DeclaringSpecialMember() {
if (!WasAlreadyBeingDeclared)
if (!WasAlreadyBeingDeclared) {
S.SpecialMembersBeingDeclared.erase(D);
S.popCodeSynthesisContext();
}
}
/// \brief Are we already trying to declare this special member?

View File

@ -196,6 +196,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
return true;
case DefaultTemplateArgumentChecking:
case DeclaringSpecialMember:
return false;
}
@ -207,8 +208,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
SourceLocation PointOfInstantiation, SourceRange InstantiationRange,
Decl *Entity, NamedDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
sema::TemplateDeductionInfo *DeductionInfo)
: SemaRef(SemaRef), SavedInNonInstantiationSFINAEContext(
SemaRef.InNonInstantiationSFINAEContext) {
: SemaRef(SemaRef) {
// Don't allow further instantiation if a fatal error and an uncompilable
// error have occurred. Any diagnostics we might have raised will not be
// visible, and we do not need to construct a correct AST.
@ -228,14 +228,12 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
Inst.NumTemplateArgs = TemplateArgs.size();
Inst.DeductionInfo = DeductionInfo;
Inst.InstantiationRange = InstantiationRange;
SemaRef.pushCodeSynthesisContext(Inst);
AlreadyInstantiating =
!SemaRef.InstantiatingSpecializations
.insert(std::make_pair(Inst.Entity->getCanonicalDecl(), Inst.Kind))
.second;
SemaRef.InNonInstantiationSFINAEContext = false;
SemaRef.CodeSynthesisContexts.push_back(Inst);
if (!Inst.isInstantiationRecord())
++SemaRef.NonInstantiationEntries;
}
}
@ -348,38 +346,55 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
PointOfInstantiation, InstantiationRange, Param, Template,
TemplateArgs) {}
void Sema::pushCodeSynthesisContext(CodeSynthesisContext Ctx) {
Ctx.SavedInNonInstantiationSFINAEContext = InNonInstantiationSFINAEContext;
InNonInstantiationSFINAEContext = false;
CodeSynthesisContexts.push_back(Ctx);
if (!Ctx.isInstantiationRecord())
++NonInstantiationEntries;
}
void Sema::popCodeSynthesisContext() {
auto &Active = CodeSynthesisContexts.back();
if (!Active.isInstantiationRecord()) {
assert(NonInstantiationEntries > 0);
--NonInstantiationEntries;
}
InNonInstantiationSFINAEContext = Active.SavedInNonInstantiationSFINAEContext;
// Name lookup no longer looks in this template's defining module.
assert(CodeSynthesisContexts.size() >=
CodeSynthesisContextLookupModules.size() &&
"forgot to remove a lookup module for a template instantiation");
if (CodeSynthesisContexts.size() ==
CodeSynthesisContextLookupModules.size()) {
if (Module *M = CodeSynthesisContextLookupModules.back())
LookupModulesCache.erase(M);
CodeSynthesisContextLookupModules.pop_back();
}
// If we've left the code synthesis context for the current context stack,
// stop remembering that we've emitted that stack.
if (CodeSynthesisContexts.size() ==
LastEmittedCodeSynthesisContextDepth)
LastEmittedCodeSynthesisContextDepth = 0;
CodeSynthesisContexts.pop_back();
}
void Sema::InstantiatingTemplate::Clear() {
if (!Invalid) {
auto &Active = SemaRef.CodeSynthesisContexts.back();
if (!Active.isInstantiationRecord()) {
assert(SemaRef.NonInstantiationEntries > 0);
--SemaRef.NonInstantiationEntries;
}
SemaRef.InNonInstantiationSFINAEContext
= SavedInNonInstantiationSFINAEContext;
// Name lookup no longer looks in this template's defining module.
assert(SemaRef.CodeSynthesisContexts.size() >=
SemaRef.CodeSynthesisContextLookupModules.size() &&
"forgot to remove a lookup module for a template instantiation");
if (SemaRef.CodeSynthesisContexts.size() ==
SemaRef.CodeSynthesisContextLookupModules.size()) {
if (Module *M = SemaRef.CodeSynthesisContextLookupModules.back())
SemaRef.LookupModulesCache.erase(M);
SemaRef.CodeSynthesisContextLookupModules.pop_back();
}
// If we've left the code synthesis context for the current context stack,
// stop remembering that we've emitted that stack.
if (SemaRef.CodeSynthesisContexts.size() ==
SemaRef.LastEmittedCodeSynthesisContextDepth)
SemaRef.LastEmittedCodeSynthesisContextDepth = 0;
if (!AlreadyInstantiating)
if (!AlreadyInstantiating) {
auto &Active = SemaRef.CodeSynthesisContexts.back();
SemaRef.InstantiatingSpecializations.erase(
std::make_pair(Active.Entity, Active.Kind));
}
SemaRef.popCodeSynthesisContext();
SemaRef.CodeSynthesisContexts.pop_back();
Invalid = true;
}
}
@ -603,6 +618,12 @@ void Sema::PrintInstantiationStack() {
<< cast<FunctionDecl>(Active->Entity)
<< Active->InstantiationRange;
break;
case CodeSynthesisContext::DeclaringSpecialMember:
Diags.Report(Active->PointOfInstantiation,
diag::note_in_declaration_of_implicit_special_member)
<< cast<CXXRecordDecl>(Active->Entity) << Active->SpecialMember;
break;
}
}
}
@ -617,7 +638,7 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
Active != ActiveEnd;
++Active)
{
switch(Active->Kind) {
switch (Active->Kind) {
case CodeSynthesisContext::TemplateInstantiation:
// An instantiation of an alias template may or may not be a SFINAE
// context, depending on what else is on the stack.
@ -643,7 +664,17 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
// or deduced template arguments, so SFINAE applies.
assert(Active->DeductionInfo && "Missing deduction info pointer");
return Active->DeductionInfo;
case CodeSynthesisContext::DeclaringSpecialMember:
// This happens in a context unrelated to template instantiation, so
// there is no SFINAE.
return None;
}
// The inner context was transparent for SFINAE. If it occurred within a
// non-instantiation SFINAE context, then SFINAE applies.
if (Active->SavedInNonInstantiationSFINAEContext)
return Optional<TemplateDeductionInfo *>(nullptr);
}
return None;

View File

@ -35,13 +35,15 @@ struct E : D {};
// expected-error@-1 {{deleted function '~E' cannot override a non-deleted function}}
// expected-note@-2 {{destructor of 'E' is implicitly deleted because base class 'D' has an inaccessible destructor}}
// expected-error@-3 {{deleted function 'operator=' cannot override a non-deleted function}}
// expected-note@-4 {{copy assignment operator of 'E' is implicitly deleted because base class 'D' has an inaccessible copy assignment operator}}
// expected-note@-4 {{while declaring the implicit copy assignment operator for 'E'}}
// expected-note@-5 {{copy assignment operator of 'E' is implicitly deleted because base class 'D' has an inaccessible copy assignment operator}}
struct F : D {};
struct G : D {};
// expected-error@-1 {{deleted function '~G' cannot override a non-deleted function}}
// expected-note@-2 {{move assignment operator of 'G' is implicitly deleted because base class 'D' has an inaccessible move assignment operator}}
// expected-note@-2 {{destructor of 'G' is implicitly deleted because base class 'D' has an inaccessible destructor}}
// expected-error@-3 {{deleted function 'operator=' cannot override a non-deleted function}}
// expected-note@-4 {{destructor of 'G' is implicitly deleted because base class 'D' has an inaccessible destructor}}
// expected-note@-4 {{while declaring the implicit move assignment operator for 'G'}}
// expected-note@-5 {{move assignment operator of 'G' is implicitly deleted because base class 'D' has an inaccessible move assignment operator}}
struct H : D {
H &operator=(H&&) = default;
// expected-error@-1 {{deleted function 'operator=' cannot override a non-deleted function}}

View File

@ -66,7 +66,8 @@ namespace Recursion {
A(const T &);
// expected-note@-1 {{in instantiation of default argument}}
};
struct B { // expected-note {{candidate constructor (the implicit move }}
struct B { // expected-note {{while declaring the implicit copy constructor for 'B'}}
// expected-note@-1 {{candidate constructor (the implicit move }}
B(); // expected-note {{candidate constructor not viable}}
A a;
};

View File

@ -105,9 +105,10 @@ struct F : public E {
#endif
#else
// expected-error@-7 {{non-deleted function '~F' cannot override a deleted function}}
// expected-note@-8 {{overridden virtual function is here}}
// expected-note@-8 {{while declaring the implicit destructor for 'F'}}
// expected-note@-9 {{overridden virtual function is here}}
#ifdef MSABI
// expected-note@-10 {{default constructor of 'F' is implicitly deleted because base class 'E' has a deleted default constructor}}
// expected-note@-11 {{default constructor of 'F' is implicitly deleted because base class 'E' has a deleted default constructor}}
#endif
#endif
};