there i fixed it
Increase robustness of the delegating constructor cycle detection mechanism. No more infinite loops on invalid or logic errors leading to false results. Ensure that this is maintained correctly accross serialization. llvm-svn: 130887
This commit is contained in:
parent
2159b8d246
commit
27a761d5bd
|
@ -362,7 +362,10 @@ namespace clang {
|
||||||
FP_PRAGMA_OPTIONS = 42,
|
FP_PRAGMA_OPTIONS = 42,
|
||||||
|
|
||||||
/// \brief Record code for enabled OpenCL extensions.
|
/// \brief Record code for enabled OpenCL extensions.
|
||||||
OPENCL_EXTENSIONS = 43
|
OPENCL_EXTENSIONS = 43,
|
||||||
|
|
||||||
|
/// \brief The list of delegating constructor declarations.
|
||||||
|
DELEGATING_CTORS = 44
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Record types used within a source manager block.
|
/// \brief Record types used within a source manager block.
|
||||||
|
|
|
@ -54,6 +54,7 @@ class Decl;
|
||||||
class DeclContext;
|
class DeclContext;
|
||||||
class NestedNameSpecifier;
|
class NestedNameSpecifier;
|
||||||
class CXXBaseSpecifier;
|
class CXXBaseSpecifier;
|
||||||
|
class CXXConstructorDecl;
|
||||||
class CXXCtorInitializer;
|
class CXXCtorInitializer;
|
||||||
class GotoStmt;
|
class GotoStmt;
|
||||||
class MacroDefinition;
|
class MacroDefinition;
|
||||||
|
@ -564,6 +565,10 @@ private:
|
||||||
/// generating warnings.
|
/// generating warnings.
|
||||||
llvm::SmallVector<uint64_t, 16> UnusedFileScopedDecls;
|
llvm::SmallVector<uint64_t, 16> UnusedFileScopedDecls;
|
||||||
|
|
||||||
|
/// \brief A list of all the delegating constructors we've seen, to diagnose
|
||||||
|
/// cycles.
|
||||||
|
llvm::SmallVector<uint64_t, 4> DelegatingCtorDecls;
|
||||||
|
|
||||||
/// \brief A snapshot of Sema's weak undeclared identifier tracking, for
|
/// \brief A snapshot of Sema's weak undeclared identifier tracking, for
|
||||||
/// generating warnings.
|
/// generating warnings.
|
||||||
llvm::SmallVector<uint64_t, 64> WeakUndeclaredIdentifiers;
|
llvm::SmallVector<uint64_t, 64> WeakUndeclaredIdentifiers;
|
||||||
|
|
|
@ -7945,52 +7945,85 @@ void Sema::SetIvarInitializers(ObjCImplementationDecl *ObjCImplementation) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
void DelegatingCycleHelper(CXXConstructorDecl* Ctor,
|
||||||
|
llvm::SmallSet<CXXConstructorDecl*, 4> &Valid,
|
||||||
|
llvm::SmallSet<CXXConstructorDecl*, 4> &Invalid,
|
||||||
|
llvm::SmallSet<CXXConstructorDecl*, 4> &Current,
|
||||||
|
Sema &S) {
|
||||||
|
llvm::SmallSet<CXXConstructorDecl*, 4>::iterator CI = Current.begin(),
|
||||||
|
CE = Current.end();
|
||||||
|
if (Ctor->isInvalidDecl())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const FunctionDecl *FNTarget = 0;
|
||||||
|
CXXConstructorDecl *Target;
|
||||||
|
|
||||||
|
// We ignore the result here since if we don't have a body, Target will be
|
||||||
|
// null below.
|
||||||
|
(void)Ctor->getTargetConstructor()->hasBody(FNTarget);
|
||||||
|
Target
|
||||||
|
= const_cast<CXXConstructorDecl*>(cast_or_null<CXXConstructorDecl>(FNTarget));
|
||||||
|
|
||||||
|
CXXConstructorDecl *Canonical = Ctor->getCanonicalDecl(),
|
||||||
|
// Avoid dereferencing a null pointer here.
|
||||||
|
*TCanonical = Target ? Target->getCanonicalDecl() : 0;
|
||||||
|
|
||||||
|
if (!Current.insert(Canonical))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// We know that beyond here, we aren't chaining into a cycle.
|
||||||
|
if (!Target || !Target->isDelegatingConstructor() ||
|
||||||
|
Target->isInvalidDecl() || Valid.count(TCanonical)) {
|
||||||
|
for (CI = Current.begin(), CE = Current.end(); CI != CE; ++CI)
|
||||||
|
Valid.insert(*CI);
|
||||||
|
Current.clear();
|
||||||
|
// We've hit a cycle.
|
||||||
|
} else if (TCanonical == Canonical || Invalid.count(TCanonical) ||
|
||||||
|
Current.count(TCanonical)) {
|
||||||
|
// If we haven't diagnosed this cycle yet, do so now.
|
||||||
|
if (!Invalid.count(TCanonical)) {
|
||||||
|
S.Diag((*Ctor->init_begin())->getSourceLocation(),
|
||||||
|
diag::err_delegating_ctor_cycle)
|
||||||
|
<< Ctor;
|
||||||
|
|
||||||
|
// Don't add a note for a function delegating directo to itself.
|
||||||
|
if (TCanonical != Canonical)
|
||||||
|
S.Diag(Target->getLocation(), diag::note_it_delegates_to);
|
||||||
|
|
||||||
|
CXXConstructorDecl *C = Target;
|
||||||
|
while (C->getCanonicalDecl() != Canonical) {
|
||||||
|
(void)C->getTargetConstructor()->hasBody(FNTarget);
|
||||||
|
assert(FNTarget && "Ctor cycle through bodiless function");
|
||||||
|
|
||||||
|
C
|
||||||
|
= const_cast<CXXConstructorDecl*>(cast<CXXConstructorDecl>(FNTarget));
|
||||||
|
S.Diag(C->getLocation(), diag::note_which_delegates_to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CI = Current.begin(), CE = Current.end(); CI != CE; ++CI)
|
||||||
|
Invalid.insert(*CI);
|
||||||
|
Current.clear();
|
||||||
|
} else {
|
||||||
|
DelegatingCycleHelper(Target, Valid, Invalid, Current, S);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Sema::CheckDelegatingCtorCycles() {
|
void Sema::CheckDelegatingCtorCycles() {
|
||||||
llvm::SmallSet<CXXConstructorDecl*, 4> Valid, Invalid, Current;
|
llvm::SmallSet<CXXConstructorDecl*, 4> Valid, Invalid, Current;
|
||||||
|
|
||||||
llvm::SmallSet<CXXConstructorDecl*, 4>::iterator ci = Current.begin(),
|
llvm::SmallSet<CXXConstructorDecl*, 4>::iterator CI = Current.begin(),
|
||||||
ce = Current.end();
|
CE = Current.end();
|
||||||
|
|
||||||
for (llvm::SmallVector<CXXConstructorDecl*, 4>::iterator
|
for (llvm::SmallVector<CXXConstructorDecl*, 4>::iterator
|
||||||
i = DelegatingCtorDecls.begin(),
|
I = DelegatingCtorDecls.begin(),
|
||||||
e = DelegatingCtorDecls.end();
|
E = DelegatingCtorDecls.end();
|
||||||
i != e; ++i) {
|
I != E; ++I) {
|
||||||
const FunctionDecl *FNTarget;
|
DelegatingCycleHelper(*I, Valid, Invalid, Current, *this);
|
||||||
CXXConstructorDecl *Target;
|
|
||||||
(*i)->getTargetConstructor()->hasBody(FNTarget);
|
|
||||||
Target
|
|
||||||
= const_cast<CXXConstructorDecl*>(cast<CXXConstructorDecl>(FNTarget));
|
|
||||||
|
|
||||||
if (!Target || !Target->isDelegatingConstructor() || Valid.count(Target)) {
|
|
||||||
Valid.insert(*i);
|
|
||||||
for (ci = Current.begin(), ce = Current.end(); ci != ce; ++ci)
|
|
||||||
Valid.insert(*ci);
|
|
||||||
Current.clear();
|
|
||||||
} else if (Target == *i || Invalid.count(Target) || Current.count(Target)) {
|
|
||||||
if (!Invalid.count(Target)) {
|
|
||||||
Diag((*(*i)->init_begin())->getSourceLocation(),
|
|
||||||
diag::err_delegating_ctor_cycle)
|
|
||||||
<< *i;
|
|
||||||
if (Target != *i)
|
|
||||||
Diag(Target->getLocation(), diag::note_it_delegates_to);
|
|
||||||
CXXConstructorDecl *Current = Target;
|
|
||||||
while (Current != *i) {
|
|
||||||
Current->getTargetConstructor()->hasBody(FNTarget);
|
|
||||||
Current
|
|
||||||
= const_cast<CXXConstructorDecl*>(cast<CXXConstructorDecl>(FNTarget));
|
|
||||||
Diag(Current->getLocation(), diag::note_which_delegates_to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(*i)->setInvalidDecl();
|
|
||||||
Invalid.insert(*i);
|
|
||||||
for (ci = Current.begin(), ce = Current.end(); ci != ce; ++ci) {
|
|
||||||
(*ci)->setInvalidDecl();
|
|
||||||
Invalid.insert(*i);
|
|
||||||
}
|
|
||||||
Current.clear();
|
|
||||||
} else {
|
|
||||||
Current.insert(*i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (CI = Invalid.begin(), CE = Invalid.end(); CI != CE; ++CI)
|
||||||
|
(*CI)->setInvalidDecl();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2114,15 +2114,6 @@ ASTReader::ReadASTBlock(PerFileData &F) {
|
||||||
TotalVisibleDeclContexts += Record[3];
|
TotalVisibleDeclContexts += Record[3];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TENTATIVE_DEFINITIONS:
|
|
||||||
// Optimization for the first block.
|
|
||||||
if (TentativeDefinitions.empty())
|
|
||||||
TentativeDefinitions.swap(Record);
|
|
||||||
else
|
|
||||||
TentativeDefinitions.insert(TentativeDefinitions.end(),
|
|
||||||
Record.begin(), Record.end());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UNUSED_FILESCOPED_DECLS:
|
case UNUSED_FILESCOPED_DECLS:
|
||||||
// Optimization for the first block.
|
// Optimization for the first block.
|
||||||
if (UnusedFileScopedDecls.empty())
|
if (UnusedFileScopedDecls.empty())
|
||||||
|
@ -2132,6 +2123,15 @@ ASTReader::ReadASTBlock(PerFileData &F) {
|
||||||
Record.begin(), Record.end());
|
Record.begin(), Record.end());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DELEGATING_CTORS:
|
||||||
|
// Optimization for the first block.
|
||||||
|
if (DelegatingCtorDecls.empty())
|
||||||
|
DelegatingCtorDecls.swap(Record);
|
||||||
|
else
|
||||||
|
DelegatingCtorDecls.insert(DelegatingCtorDecls.end(),
|
||||||
|
Record.begin(), Record.end());
|
||||||
|
break;
|
||||||
|
|
||||||
case WEAK_UNDECLARED_IDENTIFIERS:
|
case WEAK_UNDECLARED_IDENTIFIERS:
|
||||||
// Later blocks overwrite earlier ones.
|
// Later blocks overwrite earlier ones.
|
||||||
WeakUndeclaredIdentifiers.swap(Record);
|
WeakUndeclaredIdentifiers.swap(Record);
|
||||||
|
@ -2331,6 +2331,15 @@ ASTReader::ReadASTBlock(PerFileData &F) {
|
||||||
// Later tables overwrite earlier ones.
|
// Later tables overwrite earlier ones.
|
||||||
OpenCLExtensions.swap(Record);
|
OpenCLExtensions.swap(Record);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TENTATIVE_DEFINITIONS:
|
||||||
|
// Optimization for the first block.
|
||||||
|
if (TentativeDefinitions.empty())
|
||||||
|
TentativeDefinitions.swap(Record);
|
||||||
|
else
|
||||||
|
TentativeDefinitions.insert(TentativeDefinitions.end(),
|
||||||
|
Record.begin(), Record.end());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
First = false;
|
First = false;
|
||||||
}
|
}
|
||||||
|
@ -4100,6 +4109,13 @@ void ASTReader::InitializeSema(Sema &S) {
|
||||||
SemaObj->UnusedFileScopedDecls.push_back(D);
|
SemaObj->UnusedFileScopedDecls.push_back(D);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there were any delegating constructors, add them to Sema's list
|
||||||
|
for (unsigned I = 0, N = DelegatingCtorDecls.size(); I != N; ++I) {
|
||||||
|
CXXConstructorDecl *D
|
||||||
|
= cast<CXXConstructorDecl>(GetDecl(DelegatingCtorDecls[I]));
|
||||||
|
SemaObj->DelegatingCtorDecls.push_back(D);
|
||||||
|
}
|
||||||
|
|
||||||
// If there were any locally-scoped external declarations,
|
// If there were any locally-scoped external declarations,
|
||||||
// deserialize them and add them to Sema's table of locally-scoped
|
// deserialize them and add them to Sema's table of locally-scoped
|
||||||
// external declarations.
|
// external declarations.
|
||||||
|
|
|
@ -764,6 +764,7 @@ void ASTWriter::WriteBlockInfoBlock() {
|
||||||
RECORD(HEADER_SEARCH_TABLE);
|
RECORD(HEADER_SEARCH_TABLE);
|
||||||
RECORD(FP_PRAGMA_OPTIONS);
|
RECORD(FP_PRAGMA_OPTIONS);
|
||||||
RECORD(OPENCL_EXTENSIONS);
|
RECORD(OPENCL_EXTENSIONS);
|
||||||
|
RECORD(DELEGATING_CTORS);
|
||||||
|
|
||||||
// SourceManager Block.
|
// SourceManager Block.
|
||||||
BLOCK(SOURCE_MANAGER_BLOCK);
|
BLOCK(SOURCE_MANAGER_BLOCK);
|
||||||
|
@ -2723,6 +2724,10 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls,
|
||||||
for (unsigned i=0, e = SemaRef.UnusedFileScopedDecls.size(); i !=e; ++i)
|
for (unsigned i=0, e = SemaRef.UnusedFileScopedDecls.size(); i !=e; ++i)
|
||||||
AddDeclRef(SemaRef.UnusedFileScopedDecls[i], UnusedFileScopedDecls);
|
AddDeclRef(SemaRef.UnusedFileScopedDecls[i], UnusedFileScopedDecls);
|
||||||
|
|
||||||
|
RecordData DelegatingCtorDecls;
|
||||||
|
for (unsigned i=0, e = SemaRef.DelegatingCtorDecls.size(); i != e; ++i)
|
||||||
|
AddDeclRef(SemaRef.DelegatingCtorDecls[i], DelegatingCtorDecls);
|
||||||
|
|
||||||
RecordData WeakUndeclaredIdentifiers;
|
RecordData WeakUndeclaredIdentifiers;
|
||||||
if (!SemaRef.WeakUndeclaredIdentifiers.empty()) {
|
if (!SemaRef.WeakUndeclaredIdentifiers.empty()) {
|
||||||
WeakUndeclaredIdentifiers.push_back(
|
WeakUndeclaredIdentifiers.push_back(
|
||||||
|
@ -2897,6 +2902,10 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls,
|
||||||
// Write the record containing CUDA-specific declaration references.
|
// Write the record containing CUDA-specific declaration references.
|
||||||
if (!CUDASpecialDeclRefs.empty())
|
if (!CUDASpecialDeclRefs.empty())
|
||||||
Stream.EmitRecord(CUDA_SPECIAL_DECL_REFS, CUDASpecialDeclRefs);
|
Stream.EmitRecord(CUDA_SPECIAL_DECL_REFS, CUDASpecialDeclRefs);
|
||||||
|
|
||||||
|
// Write the delegating constructors.
|
||||||
|
if (!DelegatingCtorDecls.empty())
|
||||||
|
Stream.EmitRecord(DELEGATING_CTORS, DelegatingCtorDecls);
|
||||||
|
|
||||||
// Some simple statistics
|
// Some simple statistics
|
||||||
Record.clear();
|
Record.clear();
|
||||||
|
@ -2971,6 +2980,14 @@ void ASTWriter::WriteASTChain(Sema &SemaRef, MemorizeStatCalls *StatCalls,
|
||||||
AddDeclRef(SemaRef.UnusedFileScopedDecls[i], UnusedFileScopedDecls);
|
AddDeclRef(SemaRef.UnusedFileScopedDecls[i], UnusedFileScopedDecls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build a record containing all of the delegating constructor decls in this
|
||||||
|
// file.
|
||||||
|
RecordData DelegatingCtorDecls;
|
||||||
|
for (unsigned i=0, e = SemaRef.DelegatingCtorDecls.size(); i != e; ++i) {
|
||||||
|
if (SemaRef.DelegatingCtorDecls[i]->getPCHLevel() == 0)
|
||||||
|
AddDeclRef(SemaRef.DelegatingCtorDecls[i], DelegatingCtorDecls);
|
||||||
|
}
|
||||||
|
|
||||||
// We write the entire table, overwriting the tables from the chain.
|
// We write the entire table, overwriting the tables from the chain.
|
||||||
RecordData WeakUndeclaredIdentifiers;
|
RecordData WeakUndeclaredIdentifiers;
|
||||||
if (!SemaRef.WeakUndeclaredIdentifiers.empty()) {
|
if (!SemaRef.WeakUndeclaredIdentifiers.empty()) {
|
||||||
|
@ -3128,6 +3145,10 @@ void ASTWriter::WriteASTChain(Sema &SemaRef, MemorizeStatCalls *StatCalls,
|
||||||
// Write the record containing declaration references of Sema.
|
// Write the record containing declaration references of Sema.
|
||||||
if (!SemaDeclRefs.empty())
|
if (!SemaDeclRefs.empty())
|
||||||
Stream.EmitRecord(SEMA_DECL_REFS, SemaDeclRefs);
|
Stream.EmitRecord(SEMA_DECL_REFS, SemaDeclRefs);
|
||||||
|
|
||||||
|
// Write the delegating constructors.
|
||||||
|
if (!DelegatingCtorDecls.empty())
|
||||||
|
Stream.EmitRecord(DELEGATING_CTORS, DelegatingCtorDecls);
|
||||||
|
|
||||||
// Write the updates to DeclContexts.
|
// Write the updates to DeclContexts.
|
||||||
for (llvm::SmallPtrSet<const DeclContext *, 16>::iterator
|
for (llvm::SmallPtrSet<const DeclContext *, 16>::iterator
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
// Test this without pch.
|
// Test this without pch.
|
||||||
// RUN: %clang_cc1 -include %S/cxx0x-delegating-ctors.h -std=c++0x -fsyntax-only -verify %s
|
// RUN: %clang_cc1 -include %s -std=c++0x -fsyntax-only -verify %s
|
||||||
|
|
||||||
// Test with pch.
|
// Test with pch.
|
||||||
// RUN: %clang_cc1 -x c++-header -std=c++0x -emit-pch -o %t %S/cxx0x-delegating-ctors.h
|
// RUN: %clang_cc1 -x c++-header -std=c++0x -emit-pch -o %t %s
|
||||||
// RUN: %clang_cc1 -std=c++0x -include-pch %t -fsyntax-only -verify %s
|
// RUN: %clang_cc1 -std=c++0x -include-pch %t -fsyntax-only -verify %s
|
||||||
|
|
||||||
// Currently we can't deal with a note in the header
|
#ifndef PASS1
|
||||||
// XFAIL: *
|
#define PASS1
|
||||||
|
struct foo {
|
||||||
|
foo(int) : foo() { } // expected-note{{it delegates to}}
|
||||||
|
foo();
|
||||||
|
foo(bool) : foo('c') { } // expected-note{{it delegates to}}
|
||||||
|
foo(char) : foo(true) { } // expected-error{{creates a delegation cycle}} \
|
||||||
|
// expected-note{{which delegates to}}
|
||||||
|
};
|
||||||
|
#else
|
||||||
foo::foo() : foo(1) { } // expected-error{{creates a delegation cycle}} \
|
foo::foo() : foo(1) { } // expected-error{{creates a delegation cycle}} \
|
||||||
// expected-note{{which delegates to}}
|
// expected-note{{which delegates to}}
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
// Header for PCH test cxx0x-delegating-ctors.cpp
|
|
||||||
|
|
||||||
struct foo {
|
|
||||||
foo(int) : foo() { } // expected-note{{it delegates to}}
|
|
||||||
foo();
|
|
||||||
};
|
|
Loading…
Reference in New Issue