diff --git a/build/clang-plugin/clang-plugin.cpp b/build/clang-plugin/clang-plugin.cpp index d5d8af26245..d7714df33e4 100644 --- a/build/clang-plugin/clang-plugin.cpp +++ b/build/clang-plugin/clang-plugin.cpp @@ -284,6 +284,8 @@ static CustomTypeAnnotation NonHeapClass = CustomTypeAnnotation("moz_nonheap_class", "non-heap"); static CustomTypeAnnotation HeapClass = CustomTypeAnnotation("moz_heap_class", "heap"); +static CustomTypeAnnotation NonTemporaryClass = + CustomTypeAnnotation("moz_non_temporary_class", "non-temporary"); static CustomTypeAnnotation MustUse = CustomTypeAnnotation("moz_must_use", "must-use"); static CustomTypeAnnotation NonMemMovable = @@ -845,6 +847,7 @@ DiagnosticsMatcher::DiagnosticsMatcher() { astMatcher.addMatcher( callExpr(callee(functionDecl(heapAllocator()))).bind("node"), &scopeChecker); + astMatcher.addMatcher(parmVarDecl().bind("parm_vardecl"), &scopeChecker); astMatcher.addMatcher( callExpr(allOf(hasDeclaration(noArithmeticExprInArgs()), @@ -973,6 +976,12 @@ enum AllocationVariety { AV_Heap, }; +// XXX Currently the Decl* in the AutomaticTemporaryMap is unused, but it +// probably will be used at some point in the future, in order to produce better +// error messages. +typedef DenseMap AutomaticTemporaryMap; +AutomaticTemporaryMap AutomaticTemporaries; + void DiagnosticsMatcher::ScopeChecker::run( const MatchFinder::MatchResult &Result) { DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); @@ -982,6 +991,20 @@ void DiagnosticsMatcher::ScopeChecker::run( SourceLocation Loc; QualType T; + if (const ParmVarDecl *D = Result.Nodes.getNodeAs("parm_vardecl")) { + if (const Expr *Default = D->getDefaultArg()) { + if (const MaterializeTemporaryExpr *E = dyn_cast(Default)) { + // We have just found a ParmVarDecl which has, as its default argument, + // a MaterializeTemporaryExpr. We mark that MaterializeTemporaryExpr as + // automatic, by adding it to the AutomaticTemporaryMap. + // Reporting on this type will occur when the MaterializeTemporaryExpr + // is matched against. + AutomaticTemporaries[E] = D; + } + } + return; + } + // Determine the type of allocation which we detected if (const VarDecl *D = Result.Nodes.getNodeAs("node")) { if (D->hasGlobalStorage()) { @@ -1000,9 +1023,39 @@ void DiagnosticsMatcher::ScopeChecker::run( T = E->getAllocatedType(); Loc = E->getLocStart(); } - } else if (const Expr *E = + } else if (const MaterializeTemporaryExpr *E = Result.Nodes.getNodeAs("node")) { - Variety = AV_Temporary; + // Temporaries can actually have varying storage durations, due to temporary + // lifetime extension. We consider the allocation variety of this temporary + // to be the same as the allocation variety of its lifetime. + + // XXX We maybe should mark these lifetimes as being due to a temporary + // which has had its lifetime extended, to improve the error messages. + switch (E->getStorageDuration()) { + case SD_FullExpression: + { + // Check if this temporary is allocated as a default argument! + // if it is, we want to pretend that it is automatic. + AutomaticTemporaryMap::iterator AutomaticTemporary = AutomaticTemporaries.find(E); + if (AutomaticTemporary != AutomaticTemporaries.end()) { + Variety = AV_Automatic; + } else { + Variety = AV_Temporary; + } + } + break; + case SD_Automatic: + Variety = AV_Automatic; + break; + case SD_Thread: + case SD_Static: + Variety = AV_Global; + break; + case SD_Dynamic: + assert(false && "I don't think that this ever should occur..."); + Variety = AV_Heap; + break; + } T = E->getType().getUnqualifiedType(); Loc = E->getLocStart(); } else if (const CallExpr *E = Result.Nodes.getNodeAs("node")) { @@ -1024,6 +1077,8 @@ void DiagnosticsMatcher::ScopeChecker::run( DiagnosticIDs::Error, "variable of type %0 only valid on the heap"); unsigned NonHeapID = Diag.getDiagnosticIDs()->getCustomDiagID( DiagnosticIDs::Error, "variable of type %0 is not valid on the heap"); + unsigned NonTemporaryID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "variable of type %0 is not valid in a temporary"); unsigned StackNoteID = Diag.getDiagnosticIDs()->getCustomDiagID( DiagnosticIDs::Note, @@ -1053,6 +1108,8 @@ void DiagnosticsMatcher::ScopeChecker::run( case AV_Temporary: GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, TemporaryNoteID); HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, TemporaryNoteID); + NonTemporaryClass.reportErrorIfPresent(Diag, T, Loc, + NonTemporaryID, TemporaryNoteID); break; case AV_Heap: diff --git a/build/clang-plugin/tests/TestNonTemporaryClass.cpp b/build/clang-plugin/tests/TestNonTemporaryClass.cpp new file mode 100644 index 00000000000..08b5e9a1f18 --- /dev/null +++ b/build/clang-plugin/tests/TestNonTemporaryClass.cpp @@ -0,0 +1,70 @@ +#define MOZ_NON_TEMPORARY_CLASS __attribute__((annotate("moz_non_temporary_class"))) +#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit"))) + +#include + +struct MOZ_NON_TEMPORARY_CLASS NonTemporary { + int i; + NonTemporary() {} + MOZ_IMPLICIT NonTemporary(int i) {} + NonTemporary(int i, int j) {} + void *operator new(size_t x) throw() { return 0; } + void *operator new(size_t blah, char *buffer) { return buffer; } +}; + +template +struct MOZ_NON_TEMPORARY_CLASS TemplateClass { + T i; +}; + +void gobble(void *) { } + +void gobbleref(const NonTemporary&) { } + +template +void gobbleanyref(const T&) { } + +void misuseNonTemporaryClass(int len) { + NonTemporary invalid; + NonTemporary alsoInvalid[2]; + static NonTemporary invalidStatic; + static NonTemporary alsoInvalidStatic[2]; + + gobble(&invalid); + gobble(&invalidStatic); + gobble(&alsoInvalid[0]); + + gobbleref(NonTemporary()); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(NonTemporary(10, 20)); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(NonTemporary(10)); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(10); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleanyref(TemplateClass()); // expected-error {{variable of type 'TemplateClass' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + + gobble(new NonTemporary); + gobble(new NonTemporary[10]); + gobble(new TemplateClass); + gobble(len <= 5 ? &invalid : new NonTemporary); + + char buffer[sizeof(NonTemporary)]; + gobble(new (buffer) NonTemporary); +} + +void defaultArg(const NonTemporary& arg = NonTemporary()) { +} + +NonTemporary invalidStatic; +struct RandomClass { + NonTemporary nonstaticMember; // expected-note {{'RandomClass' is a non-temporary type because member 'nonstaticMember' is a non-temporary type 'NonTemporary'}} + static NonTemporary staticMember; +}; +struct MOZ_NON_TEMPORARY_CLASS RandomNonTemporaryClass { + NonTemporary nonstaticMember; + static NonTemporary staticMember; +}; + +struct BadInherit : NonTemporary {}; // expected-note {{'BadInherit' is a non-temporary type because it inherits from a non-temporary type 'NonTemporary'}} + +void useStuffWrongly() { + gobbleanyref(BadInherit()); // expected-error {{variable of type 'BadInherit' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleanyref(RandomClass()); // expected-error {{variable of type 'RandomClass' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} +} diff --git a/build/clang-plugin/tests/moz.build b/build/clang-plugin/tests/moz.build index df553522faa..b1716786755 100644 --- a/build/clang-plugin/tests/moz.build +++ b/build/clang-plugin/tests/moz.build @@ -24,6 +24,7 @@ SOURCES += [ 'TestNoExplicitMoveConstructor.cpp', 'TestNonHeapClass.cpp', 'TestNonMemMovable.cpp', + 'TestNonTemporaryClass.cpp', 'TestNoRefcountedInsideLambdas.cpp', 'TestStackClass.cpp', 'TestTrivialCtorDtor.cpp',