This is an archive of the discontinued LLVM Phabricator instance.

[Sema] tolerate more promotion matches in format string checking
ClosedPublic

Authored by fcloutier on Aug 18 2023, 2:58 PM.

Details

Summary

It's been reported that when using attribute((format)) on non-variadic functions, certain values that normally get promoted when passed as variadic arguments now unconditionally emit a diagnostic:

c
void foo(const char *fmt, float f) __attribute__((format(printf, 1, 2)));
void bar(void) {
	foo("%g", 123.f);
	//   ^ format specifies type 'double' but the argument has type 'float'
}

This is normally not an issue because float values get promoted to doubles when passed as variadic arguments, but needless to say, variadic argument promotion does not apply to non-variadic arguments.

While this can be fixed by adjusting the prototype of foo, this is sometimes undesirable in C (for instance, if foo is ABI). In C++, using variadic templates, this might instead require call-site fixing, which is tedious and arguably needless work:

c++
template<typename... Args>
void foo(const char *fmt, Args &&...args) __attribute__((format(printf, 1, 2)));
void bar(void) {
	foo("%g", 123.f);
	//   ^ format specifies type 'double' but the argument has type 'float'
}

To address this issue, we teach FormatString about a few promotions that have always been around but that have never been exercised in the direction that FormatString checks for:

  • char, unsigned char -> int, unsigned
  • half, float16, float -> double

This addresses issue https://github.com/llvm/llvm-project/issues/59824.

Diff Detail

Event Timeline

fcloutier created this revision.Aug 18 2023, 2:58 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 18 2023, 2:58 PM
fcloutier requested review of this revision.Aug 18 2023, 2:58 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 18 2023, 2:58 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript

Thank you for this! The changes should come with a release note (in clang/docs/ReleaseNotes.rst). Also, you should upload the patch with -U999 amount of context so that reviewers can see the bigger picture a bit more easily.

clang/lib/AST/FormatString.cpp
468–474

bool types?

480–481

Should these be checking for T == C.FloatTy to return NoMatchPromotionTypeConfusion?

fcloutier updated this revision to Diff 552579.Aug 22 2023, 7:53 PM
fcloutier marked an inline comment as done.

Add release note, ensure bool as a formatted formal parameter is accepted.

clang/lib/AST/FormatString.cpp
480–481

I don't think it's necessary. T is the format specifier's expected type, and no format specifier expects a float (due to floating-point types being promoted to double by default argument promotion), so there's never a case where T is FloatTy.

I just realized that we may need some additional test coverage for scanf, as that interface also uses ArgType::matchesType(): https://github.com/llvm/llvm-project/blob/5686f06d7fc02b7e2ab1eceb56f3830b6fdf7301/clang/lib/AST/ScanfFormatString.cpp#L511 but I think that's the last thing remaining on the patch.

clang/lib/AST/FormatString.cpp
480–481

Ah good point!

fcloutier updated this revision to Diff 553271.Aug 24 2023, 2:51 PM

It seems that clang allows char specifiers to match bool in scanf today, without my change (https://godbolt.org/z/e8PrjY65h). I think that this is a mistake, but that's almost certainly up for debate and I'd like to avoid scope creep on this change.

The new format-strings-scanf.cpp file covers the correct and "barely wrong" cases that I could think of.

aaron.ballman accepted this revision.Aug 25 2023, 5:34 AM

It seems that clang allows char specifiers to match bool in scanf today, without my change (https://godbolt.org/z/e8PrjY65h). I think that this is a mistake, but that's almost certainly up for debate and I'd like to avoid scope creep on this change.

Agreed! Probably worth filing an issue over it so we don't lose track of it, so: https://github.com/llvm/llvm-project/issues/64987

The new format-strings-scanf.cpp file covers the correct and "barely wrong" cases that I could think of.

Thank you, LGTM!

This revision is now accepted and ready to land.Aug 25 2023, 5:34 AM
fcloutier closed this revision.Aug 25 2023, 10:17 AM

Apologies, I landed the change but forgot to update the commit message to include the "Differential Revision:" link. -_- I'm closing this change and I'll update the GitHub issue, which is linked.