Avoid using rvalue references with trivially copyable types.

Summary:
When the dereference operator returns a value that is trivially
copyable (like a pointer), copy it. After this change, modernize-loop-convert
check can be applied to the whole llvm source code without breaking any build
or test.

Reviewers: alexfh, klimek

Subscribers: alexfh, cfe-commits

Differential Revision: http://reviews.llvm.org/D12675

llvm-svn: 246989
This commit is contained in:
Angel Garcia Gomez 2015-09-08 09:01:21 +00:00
parent f1044c044a
commit d930ef76ea
4 changed files with 76 additions and 44 deletions

View File

@ -375,7 +375,7 @@ static bool usagesAreConst(const UsageResult &Usages) {
/// by reference.
static bool usagesReturnRValues(const UsageResult &Usages) {
for (const auto &U : Usages) {
if (!U.Expression->isRValue())
if (U.Expression && !U.Expression->isRValue())
return false;
}
return true;
@ -400,8 +400,7 @@ void LoopConvertCheck::doConversion(
ASTContext *Context, const VarDecl *IndexVar, const VarDecl *MaybeContainer,
StringRef ContainerString, const UsageResult &Usages,
const DeclStmt *AliasDecl, bool AliasUseRequired, bool AliasFromForInit,
const ForStmt *TheLoop, bool ContainerNeedsDereference, bool DerefByValue,
bool DerefByConstRef) {
const ForStmt *TheLoop, RangeDescriptor Descriptor) {
auto Diag = diag(TheLoop->getForLoc(), "use range-based for loop instead");
std::string VarName;
@ -457,16 +456,17 @@ void LoopConvertCheck::doConversion(
// If an iterator's operator*() returns a 'T&' we can bind that to 'auto&'.
// If operator*() returns 'T' we can bind that to 'auto&&' which will deduce
// to 'T&&&'.
if (DerefByValue) {
AutoRefType = Context->getRValueReferenceType(AutoRefType);
if (Descriptor.DerefByValue) {
if (!Descriptor.IsTriviallyCopyable)
AutoRefType = Context->getRValueReferenceType(AutoRefType);
} else {
if (DerefByConstRef)
if (Descriptor.DerefByConstRef)
AutoRefType = Context->getConstType(AutoRefType);
AutoRefType = Context->getLValueReferenceType(AutoRefType);
}
}
StringRef MaybeDereference = ContainerNeedsDereference ? "*" : "";
StringRef MaybeDereference = Descriptor.ContainerNeedsDereference ? "*" : "";
std::string TypeString = AutoRefType.getAsString();
std::string Range = ("(" + TypeString + " " + VarName + " : " +
MaybeDereference + ContainerString + ")")
@ -518,11 +518,11 @@ StringRef LoopConvertCheck::checkRejections(ASTContext *Context,
/// of the index variable and convert the loop if possible.
void LoopConvertCheck::findAndVerifyUsages(
ASTContext *Context, const VarDecl *LoopVar, const VarDecl *EndVar,
const Expr *ContainerExpr, const Expr *BoundExpr,
bool ContainerNeedsDereference, bool DerefByValue, bool DerefByConstRef,
const ForStmt *TheLoop, LoopFixerKind FixerKind) {
const Expr *ContainerExpr, const Expr *BoundExpr, const ForStmt *TheLoop,
LoopFixerKind FixerKind, RangeDescriptor Descriptor) {
ForLoopIndexUseVisitor Finder(Context, LoopVar, EndVar, ContainerExpr,
BoundExpr, ContainerNeedsDereference);
BoundExpr,
Descriptor.ContainerNeedsDereference);
if (ContainerExpr) {
ComponentFinderASTVisitor ComponentFinder;
@ -544,15 +544,28 @@ void LoopConvertCheck::findAndVerifyUsages(
!isDirectMemberExpr(ContainerExpr))
ConfidenceLevel.lowerTo(Confidence::CL_Risky);
} else if (FixerKind == LFK_PseudoArray) {
if (!DerefByValue && !DerefByConstRef) {
if (!Descriptor.DerefByValue && !Descriptor.DerefByConstRef) {
const UsageResult &Usages = Finder.getUsages();
if (usagesAreConst(Usages)) {
// FIXME: check if the type is trivially copiable.
DerefByConstRef = true;
Descriptor.DerefByConstRef = true;
} else if (usagesReturnRValues(Usages)) {
// If the index usages (dereference, subscript, at) return RValues,
// then we should not use a non-const reference.
DerefByValue = true;
Descriptor.DerefByValue = true;
// Try to find the type of the elements on the container from the
// usages.
for (const Usage &U : Usages) {
if (!U.Expression || U.Expression->getType().isNull())
continue;
QualType Type = U.Expression->getType().getCanonicalType();
if (U.IsArrow) {
if (!Type->isPointerType())
continue;
Type = Type->getPointeeType();
}
Descriptor.IsTriviallyCopyable =
Type.isTriviallyCopyableType(*Context);
}
}
}
}
@ -565,7 +578,7 @@ void LoopConvertCheck::findAndVerifyUsages(
doConversion(Context, LoopVar, getReferencedVariable(ContainerExpr),
ContainerString, Finder.getUsages(), Finder.getAliasDecl(),
Finder.aliasUseRequired(), Finder.aliasFromForInit(), TheLoop,
ContainerNeedsDereference, DerefByValue, DerefByConstRef);
Descriptor);
}
void LoopConvertCheck::registerMatchers(MatchFinder *Finder) {
@ -618,15 +631,13 @@ void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) {
ConfidenceLevel.lowerTo(Confidence::CL_Reasonable);
const Expr *ContainerExpr = nullptr;
bool DerefByValue = false;
bool DerefByConstRef = false;
bool ContainerNeedsDereference = false;
RangeDescriptor Descriptor{false, false, false, false};
// FIXME: Try to put most of this logic inside a matcher. Currently, matchers
// don't allow the ight-recursive checks in digThroughConstructors.
if (FixerKind == LFK_Iterator) {
ContainerExpr = findContainer(Context, LoopVar->getInit(),
EndVar ? EndVar->getInit() : EndCall,
&ContainerNeedsDereference);
&Descriptor.ContainerNeedsDereference);
QualType InitVarType = InitVar->getType();
QualType CanonicalInitVarType = InitVarType.getCanonicalType();
@ -643,6 +654,8 @@ void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) {
// un-qualified pointee types match otherwise we don't use auto.
if (!Context->hasSameUnqualifiedType(InitPointeeType, BeginPointeeType))
return;
Descriptor.IsTriviallyCopyable =
BeginPointeeType.isTriviallyCopyableType(*Context);
} else {
// Check for qualified types to avoid conversions from non-const to const
// iterator types.
@ -650,17 +663,19 @@ void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) {
return;
}
DerefByValue = Nodes.getNodeAs<QualType>(DerefByValueResultName) != nullptr;
if (!DerefByValue) {
const auto *DerefByValueType =
Nodes.getNodeAs<QualType>(DerefByValueResultName);
Descriptor.DerefByValue = DerefByValueType;
if (!Descriptor.DerefByValue) {
if (const auto *DerefType =
Nodes.getNodeAs<QualType>(DerefByRefResultName)) {
// A node will only be bound with DerefByRefResultName if we're dealing
// with a user-defined iterator type. Test the const qualification of
// the reference type.
DerefByConstRef = (*DerefType)
->getAs<ReferenceType>()
->getPointeeType()
.isConstQualified();
Descriptor.DerefByConstRef = (*DerefType)
->getAs<ReferenceType>()
->getPointeeType()
.isConstQualified();
} else {
// By nature of the matcher this case is triggered only for built-in
// iterator types (i.e. pointers).
@ -671,12 +686,14 @@ void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) {
// If the initializer and variable have both the same type just use auto
// otherwise we test for const qualification of the pointed-at type.
if (!Context->hasSameType(InitPointeeType, BeginPointeeType))
DerefByConstRef = InitPointeeType.isConstQualified();
Descriptor.DerefByConstRef = InitPointeeType.isConstQualified();
}
} else {
// If the dereference operator returns by value then test for the
// canonical const qualification of the init variable type.
DerefByConstRef = CanonicalInitVarType.isConstQualified();
Descriptor.DerefByConstRef = CanonicalInitVarType.isConstQualified();
Descriptor.IsTriviallyCopyable =
DerefByValueType->isTriviallyCopyableType(*Context);
}
} else if (FixerKind == LFK_PseudoArray) {
if (!EndCall)
@ -685,7 +702,7 @@ void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Member = dyn_cast<MemberExpr>(EndCall->getCallee());
if (!Member)
return;
ContainerNeedsDereference = Member->isArrow();
Descriptor.ContainerNeedsDereference = Member->isArrow();
}
// We must know the container or an array length bound.
@ -696,8 +713,7 @@ void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) {
return;
findAndVerifyUsages(Context, LoopVar, EndVar, ContainerExpr, BoundExpr,
ContainerNeedsDereference, DerefByValue, DerefByConstRef,
TheLoop, FixerKind);
TheLoop, FixerKind, Descriptor);
}
} // namespace modernize

View File

@ -25,22 +25,27 @@ public:
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
private:
struct RangeDescriptor {
bool ContainerNeedsDereference;
bool DerefByValue;
bool IsTriviallyCopyable;
bool DerefByConstRef;
};
void doConversion(ASTContext *Context, const VarDecl *IndexVar,
const VarDecl *MaybeContainer, StringRef ContainerString,
const UsageResult &Usages, const DeclStmt *AliasDecl,
bool AliasUseRequired, bool AliasFromForInit,
const ForStmt *TheLoop, bool ContainerNeedsDereference,
bool DerefByValue, bool DerefByConstRef);
const ForStmt *TheLoop, RangeDescriptor Descriptor);
StringRef checkRejections(ASTContext *Context, const Expr *ContainerExpr,
const ForStmt *TheLoop);
void findAndVerifyUsages(ASTContext *Context, const VarDecl *LoopVar,
const VarDecl *EndVar, const Expr *ContainerExpr,
const Expr *BoundExpr,
bool ContainerNeedsDereference, bool DerefByValue,
bool DerefByConstRef, const ForStmt *TheLoop,
LoopFixerKind FixerKind);
const Expr *BoundExpr, const ForStmt *TheLoop,
LoopFixerKind FixerKind, RangeDescriptor Descriptor);
std::unique_ptr<TUTrackingInfo> TUInfo;
Confidence::Level MinConfidence;
};

View File

@ -349,11 +349,13 @@ static bool isAliasDecl(ASTContext *Context, const Decl *TheDecl,
// Check that the declared type is the same as (or a reference to) the
// container type.
QualType DeclarationType = VDecl->getType();
if (DeclarationType->isReferenceType())
DeclarationType = DeclarationType.getNonReferenceType();
QualType InitType = Init->getType();
if (!Context->hasSameUnqualifiedType(DeclarationType, InitType))
QualType DeclarationType = VDecl->getType();
if (!DeclarationType.isNull() && DeclarationType->isReferenceType())
DeclarationType = DeclarationType.getNonReferenceType();
if (InitType.isNull() || DeclarationType.isNull() ||
!Context->hasSameUnqualifiedType(DeclarationType, InitType))
return false;
switch (Init->getStmtClass()) {

View File

@ -291,7 +291,7 @@ void f() {
I != E; ++I) {
}
// CHECK-MESSAGES: :[[@LINE-4]]:5: warning: use range-based for loop instead
// CHECK-FIXES: for (auto && int_ptr : int_ptrs) {
// CHECK-FIXES: for (auto int_ptr : int_ptrs) {
}
// This container uses an iterator where the derefence type is a typedef of
@ -564,14 +564,23 @@ struct DerefByValue {
unsigned operator[](int);
};
void DerefByValueTest() {
void derefByValueTest() {
DerefByValue DBV;
for (unsigned i = 0, e = DBV.size(); i < e; ++i) {
printf("%d\n", DBV[i]);
}
// CHECK-MESSAGES: :[[@LINE-3]]:3: warning: use range-based for loop instead
// CHECK-FIXES: for (auto && elem : DBV) {
// CHECK-FIXES: for (auto elem : DBV) {
// CHECK-FIXES-NEXT: printf("%d\n", elem);
for (unsigned i = 0, e = DBV.size(); i < e; ++i) {
auto f = [DBV, i]() {};
printf("%d\n", DBV[i]);
}
// CHECK-MESSAGES: :[[@LINE-4]]:3: warning: use range-based for loop instead
// CHECK-FIXES: for (auto elem : DBV) {
// CHECK-FIXES-NEXT: auto f = [DBV, elem]() {};
// CHECK-FIXES-NEXT: printf("%d\n", elem);
}
} // namespace PseudoArray