Index: tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
diff --git a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
index 0ad2ff6b7d4d621852ea716fcd0897bf42ba5a9a..3791a0f820ae9adb6d710bf5b82f2cbfc0265fe3 100644 |
--- a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
+++ b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
@@ -129,6 +129,14 @@ const char kClassDeclaresPureVirtualTrace[] = |
"[blink-gc] Garbage collected class %0" |
" is not permitted to declare a pure-virtual trace method."; |
+const char kLeftMostBaseMustBePolymorphic[] = |
+ "[blink-gc] Left-most base class %0 of derived class %1" |
+ " must be polymorphic."; |
+ |
+const char kBaseClassMustDeclareVirtualTrace[] = |
+ "[blink-gc] Left-most base class %0 of derived class %1" |
+ " must define a virtual trace method."; |
+ |
struct BlinkGCPluginOptions { |
BlinkGCPluginOptions() : enable_oilpan(false), dump_graph(false) {} |
bool enable_oilpan; |
@@ -771,6 +779,10 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
diagnostic_.getCustomDiagID(getErrorLevel(), kClassOverridesNew); |
diag_class_declares_pure_virtual_trace_ = diagnostic_.getCustomDiagID( |
getErrorLevel(), kClassDeclaresPureVirtualTrace); |
+ diag_left_most_base_must_be_polymorphic_ = diagnostic_.getCustomDiagID( |
+ getErrorLevel(), kLeftMostBaseMustBePolymorphic); |
+ diag_base_class_must_declare_virtual_trace_ = diagnostic_.getCustomDiagID( |
+ getErrorLevel(), kBaseClassMustDeclareVirtualTrace); |
// Register note messages. |
diag_base_requires_tracing_note_ = diagnostic_.getCustomDiagID( |
@@ -900,6 +912,8 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
if (CXXMethodDecl* trace = info->GetTraceMethod()) { |
if (trace->isPure()) |
ReportClassDeclaresPureVirtualTrace(info, trace); |
+ if (info->record()->isPolymorphic()) |
+ CheckPolymorphicClass(info, trace); |
} else if (info->RequiresTraceMethod()) { |
ReportClassRequiresTraceMethod(info); |
} |
@@ -932,6 +946,121 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
DumpClass(info); |
} |
+ CXXRecordDecl* GetDependentTemplatedDecl(const Type& type) { |
+ const TemplateSpecializationType* tmpl_type = |
+ type.getAs<TemplateSpecializationType>(); |
+ if (!tmpl_type) |
+ return 0; |
+ |
+ TemplateDecl* tmpl_decl = tmpl_type->getTemplateName().getAsTemplateDecl(); |
+ if (!tmpl_decl) |
+ return 0; |
+ |
+ return dyn_cast<CXXRecordDecl>(tmpl_decl->getTemplatedDecl()); |
+ } |
+ |
+ // The GC infrastructure assumes that if the vtable of a polymorphic |
+ // base-class is not initialized for a given object (ie, it is partially |
+ // initialized) then the object does not need to be traced. Thus, we must |
+ // ensure that any polymorphic class with a trace method does not have any |
+ // tractable fields that are initialized before we are sure that the vtable |
+ // and the trace method are both defined. There are two cases that need to |
+ // hold to satisfy that assumption: |
+ // |
+ // 1. If trace is virtual, then it must be defined in the left-most base. |
+ // This ensures that if the vtable is initialized and it contains a pointer to |
+ // the trace method. |
+ // |
+ // 2. If trace is non-virtual, then the trace method is defined and we must |
+ // ensure that the left-most base defines a vtable. This ensures that the |
+ // first thing to be initialized when constructing the object is the vtable |
+ // itself. |
+ void CheckPolymorphicClass(RecordInfo* info, CXXMethodDecl* trace) { |
+ CXXRecordDecl* left_most = info->record(); |
+ CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); |
+ CXXRecordDecl* left_most_base = 0; |
+ while (it != left_most->bases_end()) { |
+ left_most_base = it->getType()->getAsCXXRecordDecl(); |
+ if (!left_most_base && it->getType()->isDependentType()) |
+ left_most_base = GetDependentTemplatedDecl(*it->getType()); |
+ |
+ // TODO: Find a way to correctly check actual instantiations |
+ // for dependent types. The escape below will be hit, eg, when |
+ // we have a primary template with no definition and |
+ // specializations for each case (such as SupplementBase) in |
+ // which case we don't succeed in checking the required |
+ // properties. |
+ if (!left_most_base || !left_most_base->hasDefinition()) |
+ return; |
+ |
+ StringRef name = left_most_base->getName(); |
+ // We know GCMixin base defines virtual trace. |
+ if (Config::IsGCMixinBase(name)) |
+ return; |
+ |
+ // Stop with the left-most prior to a safe polymorphic base (a safe base |
+ // is non-polymorphic and contains no fields that need tracing). |
+ if (Config::IsSafePolymorphicBase(name)) |
+ break; |
+ |
+ left_most = left_most_base; |
+ it = left_most->bases_begin(); |
+ } |
+ |
+ if (RecordInfo* left_most_info = cache_.Lookup(left_most)) { |
+ |
+ // Check condition (1): |
+ if (trace->isVirtual()) { |
+ if (CXXMethodDecl* trace = left_most_info->GetTraceMethod()) { |
+ if (trace->isVirtual()) |
+ return; |
+ } |
+ ReportBaseClassMustDeclareVirtualTrace(info, left_most); |
+ return; |
+ } |
+ |
+ // Check condition (2): |
+ if (DeclaresVirtualMethods(info->record())) |
+ return; |
+ if (left_most_base) { |
+ ++it; // Get the base next to the "safe polymorphic base" |
+ if (it != left_most->bases_end()) { |
+ if (CXXRecordDecl* next_base = it->getType()->getAsCXXRecordDecl()) { |
+ if (CXXRecordDecl* next_left_most = GetLeftMostBase(next_base)) { |
+ if (DeclaresVirtualMethods(next_left_most)) |
+ return; |
+ ReportLeftMostBaseMustBePolymorphic(info, next_left_most); |
+ return; |
+ } |
+ } |
+ } |
+ } |
+ ReportLeftMostBaseMustBePolymorphic(info, left_most); |
+ } |
+ } |
+ |
+ CXXRecordDecl* GetLeftMostBase(CXXRecordDecl* left_most) { |
+ CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); |
+ while (it != left_most->bases_end()) { |
+ if (it->getType()->isDependentType()) |
+ left_most = GetDependentTemplatedDecl(*it->getType()); |
+ else |
+ left_most = it->getType()->getAsCXXRecordDecl(); |
+ if (!left_most || !left_most->hasDefinition()) |
+ return 0; |
+ it = left_most->bases_begin(); |
+ } |
+ return left_most; |
+ } |
+ |
+ bool DeclaresVirtualMethods(CXXRecordDecl* decl) { |
+ CXXRecordDecl::method_iterator it = decl->method_begin(); |
+ for (; it != decl->method_end(); ++it) |
+ if (it->isVirtual() && !it->isPure()) |
+ return true; |
+ return false; |
+ } |
+ |
void CheckLeftMostDerived(RecordInfo* info) { |
CXXRecordDecl* left_most = info->record(); |
CXXRecordDecl::base_class_iterator it = left_most->bases_begin(); |
@@ -1074,16 +1203,12 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
void CheckTraceMethod(RecordInfo* parent, |
CXXMethodDecl* trace, |
bool isTraceAfterDispatch) { |
- // A non-virtual trace method must not override another trace. |
- if (!isTraceAfterDispatch && !trace->isVirtual()) { |
+ // A trace method must not override any non-virtual trace methods. |
+ if (!isTraceAfterDispatch) { |
for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
it != parent->GetBases().end(); |
++it) { |
RecordInfo* base = it->second.info(); |
- // We allow mixin bases to contain a non-virtual trace since it will |
- // never be used for dispatching. |
- if (base->IsGCMixin()) |
- continue; |
if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) |
ReportOverriddenNonVirtualTrace(parent, trace, other); |
} |
@@ -1484,6 +1609,24 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
<< info->record(); |
} |
+ void ReportLeftMostBaseMustBePolymorphic(RecordInfo* derived, |
+ CXXRecordDecl* base) { |
+ SourceLocation loc = base->getLocStart(); |
+ SourceManager& manager = instance_.getSourceManager(); |
+ FullSourceLoc full_loc(loc, manager); |
+ diagnostic_.Report(full_loc, diag_left_most_base_must_be_polymorphic_) |
+ << base << derived->record(); |
+ } |
+ |
+ void ReportBaseClassMustDeclareVirtualTrace(RecordInfo* derived, |
+ CXXRecordDecl* base) { |
+ SourceLocation loc = base->getLocStart(); |
+ SourceManager& manager = instance_.getSourceManager(); |
+ FullSourceLoc full_loc(loc, manager); |
+ diagnostic_.Report(full_loc, diag_base_class_must_declare_virtual_trace_) |
+ << base << derived->record(); |
+ } |
+ |
void NoteManualDispatchMethod(CXXMethodDecl* dispatch) { |
SourceLocation loc = dispatch->getLocStart(); |
SourceManager& manager = instance_.getSourceManager(); |
@@ -1574,6 +1717,8 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
unsigned diag_derives_non_stack_allocated_; |
unsigned diag_class_overrides_new_; |
unsigned diag_class_declares_pure_virtual_trace_; |
+ unsigned diag_left_most_base_must_be_polymorphic_; |
+ unsigned diag_base_class_must_declare_virtual_trace_; |
unsigned diag_base_requires_tracing_note_; |
unsigned diag_field_requires_tracing_note_; |