Skip to content

Commit

Permalink
[Sema] Improve -Wrange-loop-analysis warnings.
Browse files Browse the repository at this point in the history
No longer generate a diagnostic when a small trivially copyable type is
used without a reference. Before the test looked for a POD type and had no
size restriction. Since the range-based for loop is only available in
C++11 and POD types are trivially copyable in C++11 it's not required to
test for a POD type.

Since copying a large object will be expensive its size has been
restricted. 64 bytes is a common size of a cache line and if the object is
aligned the copy will be cheap. No performance impact testing has been
done.

Differential Revision: https://reviews.llvm.org/D72212
  • Loading branch information
mordante committed Jan 11, 2020
1 parent 08275a5 commit 9c74fb4
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 4 deletions.
20 changes: 16 additions & 4 deletions clang/lib/Sema/SemaStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2779,6 +2779,15 @@ static void DiagnoseForRangeReferenceVariableCopies(Sema &SemaRef,
}
}

/// Determines whether the @p VariableType's declaration is a record with the
/// clang::trivial_abi attribute.
static bool hasTrivialABIAttr(QualType VariableType) {
if (CXXRecordDecl *RD = VariableType->getAsCXXRecordDecl())
return RD->hasAttr<TrivialABIAttr>();

return false;
}

// Warns when the loop variable can be changed to a reference type to
// prevent a copy. For instance, if given "for (const Foo x : Range)" suggest
// "for (const Foo &x : Range)" if this form does not make a copy.
Expand All @@ -2800,10 +2809,13 @@ static void DiagnoseForRangeConstVariableCopies(Sema &SemaRef,
return;
}

// TODO: Determine a maximum size that a POD type can be before a diagnostic
// should be emitted. Also, only ignore POD types with trivial copy
// constructors.
if (VariableType.isPODType(SemaRef.Context))
// Small trivially copyable types are cheap to copy. Do not emit the
// diagnostic for these instances. 64 bytes is a common size of a cache line.
// (The function `getTypeSize` returns the size in bits.)
ASTContext &Ctx = SemaRef.Context;
if (Ctx.getTypeSize(VariableType) <= 64 * 8 &&
(VariableType.isTriviallyCopyableType(Ctx) ||
hasTrivialABIAttr(VariableType)))
return;

// Suggest changing from a const variable to a const reference variable
Expand Down
89 changes: 89 additions & 0 deletions clang/test/SemaCXX/warn-range-loop-analysis-trivially-copyable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -Wloop-analysis -verify %s
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -Wrange-loop-analysis -verify %s

void test_POD_64_bytes() {
struct Record {
char a[64];
};

Record records[8];
for (const auto r : records)
(void)r;
}

void test_POD_65_bytes() {
struct Record {
char a[65];
};

// expected-warning@+3 {{loop variable 'r' of type 'const Record' creates a copy from type 'const Record'}}
// expected-note@+2 {{use reference type 'const Record &' to prevent copying}}
Record records[8];
for (const auto r : records)
(void)r;
}

void test_TriviallyCopyable_64_bytes() {
struct Record {
Record() {}
char a[64];
};

Record records[8];
for (const auto r : records)
(void)r;
}

void test_TriviallyCopyable_65_bytes() {
struct Record {
Record() {}
char a[65];
};

// expected-warning@+3 {{loop variable 'r' of type 'const Record' creates a copy from type 'const Record'}}
// expected-note@+2 {{use reference type 'const Record &' to prevent copying}}
Record records[8];
for (const auto r : records)
(void)r;
}

void test_NonTriviallyCopyable() {
struct Record {
Record() {}
~Record() {}
volatile int a;
int b;
};

// expected-warning@+3 {{loop variable 'r' of type 'const Record' creates a copy from type 'const Record'}}
// expected-note@+2 {{use reference type 'const Record &' to prevent copying}}
Record records[8];
for (const auto r : records)
(void)r;
}

void test_TrivialABI_64_bytes() {
struct [[clang::trivial_abi]] Record {
Record() {}
~Record() {}
char a[64];
};

Record records[8];
for (const auto r : records)
(void)r;
}

void test_TrivialABI_65_bytes() {
struct [[clang::trivial_abi]] Record {
Record() {}
~Record() {}
char a[65];
};

// expected-warning@+3 {{loop variable 'r' of type 'const Record' creates a copy from type 'const Record'}}
// expected-note@+2 {{use reference type 'const Record &' to prevent copying}}
Record records[8];
for (const auto r : records)
(void)r;
}
4 changes: 4 additions & 0 deletions clang/test/SemaCXX/warn-range-loop-analysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ struct Container {

struct Foo {};
struct Bar {
// Small trivially copyable types do not show a warning when copied in a
// range-based for loop. This size ensures the object is not considered
// small.
char s[128];
Bar(Foo);
Bar(int);
operator int();
Expand Down

0 comments on commit 9c74fb4

Please sign in to comment.