目标和动力
C/C++ 允许在类型之间转换值,而转换可能修改 例如由于上溢、下溢、符号变化 截断和损失精度。
这些转化可能是隐式发生的,在这种情况下,系统没有说明 如果作者希望转化发生,或者明确表示转化, 表明相应转化是有意为之,并且作者认可 后果。
默认情况下,Fucsia C/C++ 代码使用标志编译
-Wconversion
:禁止使用
可能会改变某个值。当这种新的默认行为在 2020 年推出时,来源
原先存在错误的代码未更改,并且关联的 build
定义遭到抑制。我们希望清除这一遗留信息
抑制此警告可能会隐藏代码中非常细微的 bug。
技术背景
可能改变值的隐式类型转换示例:
float f = sqrt(2.0);
int si = 0.5;
unsigned int ui = -1;
除非系统忽略此警告,否则这些都会导致编译器错误。
可能不会改变值的隐式类型转换示例:
double d = sqrtf(2.0f);
size_t size = 20;
int i = 4.0;
它们被编译器确定为安全,绝不会发出警告 。
使用隐式类型转换编译代码的 BUILD.gn
定义
抑制的警告可能如下所示:
source_set("foo") {
...
# TODO(https://fxbug.dev/42136089): delete the below and fix compiler warnings
configs += [ "//build/config:Wno-conversion" ]
}
在//build/config/BUILD.gn
的visibility
下
"Wno-conversion"
配置目标的列表,您可以找到所有
源代码树中存在此抑制的实例。
如何提供帮助
选择任务
选择符合以下条件的任何实例:Wno-conversion
或 bug 42136089
引用。浏览(可选)
//build/config/BUILD.gn
(适用于对任何代码的引用)
非常熟悉
您可以忽略第三方代码(目录路径中的 third_party
)。
Fuchsia 项目不拥有此代码,因此您无法对其进行修复。
也就是说,如果您愿意,可以前往上游代码位置贡献代码,
问题修复。尽管如此,上游维护人员也必须同意
来启用 -Wconversion
标志,否则它们会回归。
执行任务
移除 -Wno-conversion
抑制,重新构建并修复所有错误。
请注意,系统可能不会显示任何错误。这种情况通常发生在 其他人对过去具有隐式类型的源代码进行了更改 转化问题,但并未消除这种影响。如果没有任何 修正后,继续将更改送审。否则,请继续阅读。
大批量代码完全有可能 。如果您移除了大量禁止项,且您的更改符合 CQ 要求, 那你没错,只是幸运而已。
示例:
- 478402:[清理] 移除未使用的 -Wno-conversions
- 474400:[bt][common] 移除 Wno 转化抑制
- 487186:[连接] 清理 Wno-conversion
- 484416:[存储] 转化发生率下降
简单的向下投射
[5838/95510] CC
obj/src/graphics/lib/compute/spinel/ext/geometry/geometry.svg_arc.c.o
../../src/graphics/lib/compute/spinel/ext/geometry/svg_arc.c:96:57: error:
implicit conversion loses floating-point precision: 'double' to 'float'
[-Werror,-Wimplicit-float-conversion]
arc_params->phi = fmodf(x_axis_rotation_radians, M_PI * 2.0);
~~~~~ ~~~~~^~~~~
在这种情况下,您只需重新编写这行违规代码 显式转换。
arc_params->phi = fmodf(x_axis_rotation_radians, (float)(M_PI * 2.0));
这没关系,因为无需担心从双精度值到浮点数的精度损失 。其他更改可能更加复杂。例如,您可能希望 在进行转换前添加边界检查。
更改变量类型
在很多情况下,最好将代码 源变量类型进行明确转换。
../../garnet/bin/hwstress/memory_stress.cc:216:90: error: implicit conversion changes signedness: 'int' to 'uint64_t' (aka 'unsigned long') [-Werror,-Wsign-conversion]
MultiWordPattern(NegateWords((RotatePattern(every_sixth_bit, i))))));
~~~~~~~~~~~~~ ^
如果我们看更多代码:
std::vector<uint64_t> RotatePattern(std::vector<uint64_t> v, uint64_t n);
...
for (int i = 0; i < 6; i++) {
result.push_back(MakePatternWorkload(
fxl::StringPrintf("Single bit clear 6-bit (%d/6)", i + 1),
MultiWordPattern(NegateWords((RotatePattern(every_sixth_bit, i))))));
}
我们看到 i 只用于此循环。将它设计为一个 无符号类型:
for (uint32_t i = 0; i < 6; i++) {
result.push_back(MakePatternWorkload(
fxl::StringPrintf("Single bit clear 6-bit (%u/6)", i + 1),
MultiWordPattern(NegateWords((RotatePattern(every_sixth_bit, i))))));
}
三元表达式中的多个类型转换
如果编译器针对三元表达式中的操作数发出警告,从技术上讲, 为每个操作数向结果类型添加单独的类型转换, 可能会更简洁,以便将整个表达式封装在类型转换中。例如:
[5836/95510] CC obj/src/graphics/lib/compute/spinel/ext/geometry/geometry.arc.c.o
../../src/graphics/lib/compute/spinel/ext/geometry/arc.c:73:50: error: implicit conversion loses floating-point precision: 'double' to 'float' [-Werror,-Wimplicit-float-conversion]
float const theta_sweep = theta_delta > 0.0f ? SPN_SWEEP_RADIANS : -SPN_SWEEP_RADIANS;
~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~
../../src/graphics/lib/compute/spinel/ext/geometry/arc.c:28:39: note: expanded from macro 'SPN_SWEEP_RADIANS'
#define SPN_SWEEP_RADIANS (2.0 * M_PI / 3.0) // 120°
~~~~~~~~~~~^~~~~
../../src/graphics/lib/compute/spinel/ext/geometry/arc.c:73:70: error: implicit conversion loses floating-point precision: 'double' to 'float' [-Werror,-Wimplicit-float-conversion]
float const theta_sweep = theta_delta > 0.0f ? SPN_SWEEP_RADIANS : -SPN_SWEEP_RADIANS;
~~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~
条件表达式的两个操作数都会出现错误。Cast 可以 可以应用于表达式中的 SPN_SWEEP_RADIANS,或整个 SPN_SWEEP_RADIANS 条件表达式。
// Explicit casts at the error sites.
float const theta_sweep = theta_delta > 0.0f ? (float)(SPN_SWEEP_RADIANS) : (float)(-SPN_SWEEP_RADIANS);
// Different AST, but effectively cleaner.
float const theta_sweep = (float)(theta_delta > 0.0f ? SPN_SWEEP_RADIANS : -SPN_SWEEP_RADIANS);
从 AST 的角度来看,表达式是不同的,但其逻辑和 优化的 Codegen 仍然是相同的。
尽量不要在宏中应用类型转换,而应在其调用中应用
宏(尤其是标头中的宏)往往用于多个位置, 接受不同类型的参数。通常最好对宏进行类型转换 参数或宏本身周围,而不是在宏内部。例如:
../../src/virtualization/third_party/fdt/fdt.c:143:16: error: implicit conversion changes signedness: 'unsigned long' to 'int' [-Werror,-Wsign-conversion]
*nextoffset = FDT_TAGALIGN(offset);
~ ^~~~~~~~~~~~~~~~~~~~
../../src/virtualization/third_party/fdt/libfdt_internal.h:56:26: note: expanded from macro 'FDT_TAGALIGN'
#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
^~~~~~~~~~~~~~~~~~~~~~~~~~~
../../src/virtualization/third_party/fdt/libfdt_internal.h:55:40: note: expanded from macro 'FDT_ALIGN'
#define FDT_ALIGN(x, a) (((x) + (a)-1) & ~((a)-1))
~~~~~~~~~~~~~~^~~~~~~~~~
../../src/virtualization/third_party/fdt/fdt.c:143:29: error: implicit conversion changes signedness: 'int' to 'unsigned long' [-Werror,-Wsign-conversion]
*nextoffset = FDT_TAGALIGN(offset);
~~~~~~~~~~~~~^~~~~~~
../../src/virtualization/third_party/fdt/libfdt_internal.h:56:37: note: expanded from macro 'FDT_TAGALIGN'
#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
~~~~~~~~~~~^~~~~~~~~~~~~~~~
../../src/virtualization/third_party/fdt/libfdt_internal.h:55:28: note: expanded from macro 'FDT_ALIGN'
#define FDT_ALIGN(x, a) (((x) + (a)-1) & ~((a)-1))
^ ~
这两个错误都指向最内层的宏扩展。而不是应用类型转换 后,或出现第二个错误时,最好使用 而是将类型转换应用于宏参数偏移量,以及 FDT_TAGALIGN。
*nextoffset = (int)(FDT_TAGALIGN((unsigned)offset));
使用模板化类型
与宏类似,在模板化模板中应用类型转换时, 函数,而非警告中指定的类型。 其他代码可能会将此模板与其他类型一起使用。例如,在:
template <typename T>
constexpr typename std::make_signed<T>::type ConditionalNegate(
T x,
bool is_negative) {
static_assert(std::is_integral<T>::value, "Type must be integral");
using SignedT = typename std::make_signed<T>::type;
using UnsignedT = typename std::make_unsigned<T>::type;
return static_cast<SignedT>(
(static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
}
我们得到:
../../zircon/third_party/ulib/safemath/include/safemath/safe_conversions_impl.h:75:36: error: implicit conversion changes signedness: 'SignedT' (aka 'long') to 'unsigned long' [-Werror,-Wsign-conversion]
(static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
~ ^~~~~~~~~~~~~~~~~~~~~
我们应该将 static_cast 转换为 UnsignedT,而不是 unsigned long。
return static_cast<SignedT>(
(static_cast<UnsignedT>(x) ^ static_cast<UnsignedT>(-SignedT(is_negative))) + is_negative);
回调函数
这可能是一个特别欺骗性的错误,因为调用堆栈可能 必须指向确切问题使用 例如,不匹配的参数类型。例如,如果我们看看:
../../sdk/lib/fit/include/lib/fit/function_internal.h:70:19: error: implicit conversion changes signedness: 'long' to 'uint64_t' (aka 'unsigned long') [-Werror,-Wsign-conversion]
return target(std::forward<Args>(args)...);
~~~~~~ ^~~~~~~~~~~~~~~~~~~~~~~~
../../sdk/lib/fit/include/lib/fit/function_internal.h:91:60: note: in instantiation of member function 'fit::internal::target<(lambda at ../../examples/fidl/test/launcher.cc:67:7), true, false, void, long, fuchsia::sys::TerminationReason>::invoke' requested here
&unshared_target_type_id, &inline_target_get, &target::invoke, &target::move, &target::destroy};
^
../../sdk/lib/fit/include/lib/fit/function_internal.h:370:45: note: in instantiation of static data member 'fit::internal::target<(lambda at ../../examples/fidl/test/launcher.cc:67:7), true, false, void, long, fuchsia::sys::TerminationReason>::ops' requested here
ops_ = &target_type<DecayedCallable>::ops;
^
../../sdk/lib/fit/include/lib/fit/function_internal.h:297:5: note: in instantiation of function template specialization 'fit::internal::function_base<16, false, void (long, fuchsia::sys::TerminationReason)>::initialize_target<(lambda at ../../examples/fidl/test/launcher.cc:67:7)>' requested here
initialize_target(std::forward<Callable>(target));
^
../../sdk/lib/fit/include/lib/fit/function.h:253:11: note: in instantiation of function template specialization 'fit::internal::function_base<16, false, void (long, fuchsia::sys::TerminationReason)>::assign<(lambda at ../../examples/fidl/test/launcher.cc:67:7), void>' requested here
base::assign(std::forward<Callable>(target));
^
../../examples/fidl/test/launcher.cc:66:43: note: in instantiation of function template specialization 'fit::function_impl<16, false, void (long, fuchsia::sys::TerminationReason)>::operator=<(lambda at ../../examples/fidl/test/launcher.cc:67:7)>' requested here
client_controller.events().OnTerminated =
^
您不妨考虑通过 sdk/lib/fit/include/lib/fit/function_internal.h:70,但是 根本问题位于 examples/fidl/test/launcher.cc:67 中
client_controller.events().OnTerminated =
[&loop, &client_status](uint64_t code, fuchsia::sys::TerminationReason reason) {
...
通过查看 sdk/fidl/fuchsia.sys/component_controller.fidl 可见 OnTerminated 接受 int64 作为第一个参数,但我们为 lambda 分配了 lambda 它接受 uint64_t 作为第一个参数。解决办法是接受 int64_t 转换为 lambda 参数。
client_controller.events().OnTerminated =
[&loop, &client_status](int64_t code, fuchsia::sys::TerminationReason reason) {
...
无损向下投射
如果您想向下舍入某个变量,但不想在实验期间容忍丢失 safemath 库提供了一系列实用函数,包括 check_cast,它会断言在向下广播期间丢失数据的情况。
void fun(uint64_t block_number) {
// Downcast block_number because underlying layer expects uint32_t.
uint32_t block = safemath::checked_cast<uint32_t>(block_number);
....
}
如果高于 checked_cast
,则断言 block_number 大于
std::numeric_limits<uint32_t>::max()
。
如需使用 Safemath,请将 //zircon/third_party/ulib/safemath
添加为依赖项
BUILD.gn
文件。点击此处可查看 BUILD.gn 示例
如需查看使用示例,请点击此处。
完成任务
请在更改说明中标记封面 bug,如下所示:
Bug: b/42136089
移除您在其中清理的构建目标的所有引用
//build/config/BUILD.gn
(在 visibility
列表下)
(针对 "Wno-conversion"
配置目标)。
通过所有者查找审核者,然后合并您的更改。
示例
- 469454:[调试程序] 修复了 -Wconversion 问题
- 464514:[fit, fzl] 启用 -Wconversion 警告
- 463856:[kcounter] 启用 -Wconversion 警告
- 456573:[反馈] 修复 - Wconversion 错误
- 450719:[camera][lib] 修复了 -Wconversion 构建错误
赞助商
如有疑问或需要更新状态,欢迎与我们联系: