【发布时间】:2013-10-28 14:29:22
【问题描述】:
我一直在我的各种项目上运行clang 3.3 的静态分析器。除了一些是我自己的错的问题(这是意料之中的,否则我会非常悲伤和自鸣得意),除了以下关于std::function 的移动构造函数的问题是误报之外,一切都进行得很顺利。
在进一步讨论之前,这里有一个简单的测试用例:
int main() {
std::function<void ()> f1;
std::function<void ()> f2 = std::move(f1);
}
通过clang++ -std=c++11 --analyze -Xanalyzer -analyzer-output=text foo.cpp(它使用GCC的libstdc++——即4.8.1版本——而不是clang的libc++)运行它,你会得到以下跟踪:
In file included from foo.cpp:1:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/iostream:39:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/ostream:38:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/ios:40:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/char_traits.h:39:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_algobase.h:64:
In file included from /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_pair.h:59:
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:175:7: warning: Assigned value is garbage or undefined
_Tp __tmp = _GLIBCXX_MOVE(__a);
^~~~~~~~~ ~~~~~~~~~~~~~~~~~~
foo.cpp:30:31: note: Calling move constructor for 'function'
std::function<void ()> f2 = std::move(f1);
^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2232:2: note: Calling 'function::swap'
__x.swap(*this);
^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2359:2: note: Calling 'swap'
std::swap(_M_invoker, __x._M_invoker);
^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:175:19: note: Calling 'move'
_Tp __tmp = _GLIBCXX_MOVE(__a);
^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:142:30: note: expanded from macro '_GLIBCXX_MOVE'
#define _GLIBCXX_MOVE(__val) std::move(__val)
^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:175:19: note: Returning from 'move'
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:142:30: note: expanded from macro '_GLIBCXX_MOVE'
#define _GLIBCXX_MOVE(__val) std::move(__val)
^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/move.h:175:7: note: Assigned value is garbage or undefined
_Tp __tmp = _GLIBCXX_MOVE(__a);
^
1 warning generated.
如您所见,移动构造函数std::function(std::function&&) 是根据swap 实现的。整个操作的步骤是(如果我们相信clang):
-
f1构造正确 -
f2尚未构建,因此包含垃圾 -
f1被移动到f2但实际上它确实是swap -
f2现在包含旧的f1,但f1包含旧的f2即。垃圾 - 在某个时候,包含垃圾的
f1被破坏了......然后会发生什么?
理论说,这很糟糕。在实践中,查看实现,似乎std::function 私下继承自_Function_base,它负责将所有重要事项(即_M_manager)初始化为null,因此clang 对_M_invoker 的警告是毫无意义。
万一有人怀疑,移动一个对象应该让它处于不确定状态,你只能分配给它或破坏它。 GCC 的function 实现正是这样做的:就资源管理而言,只有_M_manager 很重要,其余的(包括_M_invoker)只是“方便”的指针。
我在 GCC 的 function 实现中挖掘得足够多,对于clang 的诊断结果的误报 状态毫无疑问。但由于我的代码中有数百个地方发生了这种情况,因此至少可以说,通过静态分析器的结果非常痛苦。
我如何指示 clang 不要报告这个问题?
同样,如果您错过了,我将使用 clang 3.3 和 GCC 的 libstdc++ 4.8.1。
注意:如果您正在运行clang 3.4 构建并且如果它不会触发此误报,请告诉我。据我尝试,我还无法在我的系统上运行 3.4(Debian Jessie),但如果它解决了这个问题,我会更加努力。
【问题讨论】:
-
Clang 的网站暗示 Clang 诊断中的误报应被视为要报告的错误。因此,您可以采取的一项措施是向 Clang 开发人员报告误报(如果您设法获得最新版本并且问题仍然存在)。 clang-analyzer.llvm.org
-
@PascalCuoq 是的,我一直在考虑提交错误报告,但我希望有一种最终用户机制来消除误报。你知道,与此同时。 :) 尤其是因为这个问题的性质非常依赖于析构函数或赋值中实际发生的事情,所以它可能不是很容易修复。
-
clang++3.4 (trunk 193040) 不会为您的示例发出任何诊断消息。使用 libstdc++4.8.1
-
@DyP 感谢您提供的信息,我会再试一试。
标签: c++ c++11 static-analysis clang++ false-positive