This is an archive of the discontinued LLVM Phabricator instance.

C++11: Reject string literal to non-const char * conversion
ClosedPublic

Authored by ismailp on Oct 17 2013, 3:49 PM.

Details

Reviewers
rsmith
Summary

Conversion of string literals to non-const char* is deprecated in C++03, and is ill-formed in C++11.

This patch also fixes PR16314.

Diff Detail

Event Timeline

Is there any way we can continue to accept this as an extension in C++11 mode (at least in easy cases, outside of SFINAE contexts)? I'm concerned that this may break a lot of existing code.

unittests/ASTMatchers/ASTMatchersTest.cpp
2611–2612

Can you change this to take const char* instead?

ismailp updated this revision to Unknown Object (????).Nov 2 2013, 2:49 PM

We decided to limit this patch to overload resolution, addressing only PR16314.

When comparing candidate functions, this conversion is considered worse than other conversions to prevent the candidate function to be the best. If this conversion happens in the best viable function, we will accept it with a warning (current behavior).

ismailp updated this revision to Unknown Object (????).Nov 5 2013, 10:31 AM

Compared to the previous revision, this revision issues a new ExtWarn instead of the existing Warning.

rsmith added inline comments.Nov 5 2013, 1:08 PM
include/clang/Basic/DiagnosticSemaKinds.td
4526–4527

We spell these as ext_deprecrated_... (drop the warn_).

lib/Sema/SemaExprCXX.cpp
3067–3070

You need to issue an Error here, not just an ExtWarn, if we're in a SFINAE context.

lib/Sema/SemaOverload.cpp
1187–1189

Do you really need to check these things? I would have thought checking DeprecatedStirngLiteralToCharPtr would be enough.

3348–3352

This looks suspicious: is it not possible for two conversions involving a string-literal-to-char* conversion to have different ranks? For instance, what if one is a user-defined conversion sequence that converts string literal -> char* -> class type, and the other just converts string literal -> char*?

I think what you want is to start this function with something like:

if (getLangOpts().CPlusPlus11 && !getLangOpts().WritableStrings &&
    ICS1.DeprecatedStringLiteralToCharPtr != ICS2.DeprecatedStringLiteralToCharPtr)
  return ICS1.DeprecatedStringLiteralToCharPtr ? ICS::Worse : ICS::Better;

(That is, any ill-formed conversion is worse than any non-ill-formed conversion.)

ismailp added inline comments.Nov 13 2013, 5:25 PM
lib/Sema/SemaOverload.cpp
1187–1189

This function is removed in the new patch (coming soon). I have checked the full sequence, because the member DeprecatedStringLiteralToCharPtr was not initialized in other cases. I have fixed this in new patch. Instead of checking entire sequence, we check only DeprecatedStringLiteralToCharPtr. I thought it is better to initialize DeprecatedStringLiteralToCharPtr in all cases.

3348–3352

Thank you for the heads up. I forgot user defined conversion sequence completely. I have used your suggestion almost exactly, with minor change. A new test added for user defined conversion sequence vs. ill-formed conversion ranking.

ismailp updated this revision to Unknown Object (????).Nov 13 2013, 5:34 PM

Added new test for user defined conversion sequence.
User defined conversions were handled incorrectly in previous revision.
Added DeprecatedStringLiteralToCharPtr initializations, so that we don't have to check standard conversion sequence has Array to pointer->identity->qualification conversions (previously needed for checking DeprecatedStringLiteralToCharPtr).
Added checks to ensure we pick the correct overloads.

rsmith added inline comments.Nov 13 2013, 6:07 PM
include/clang/Basic/DiagnosticSemaKinds.td
4526–4529

Since the last revision of your patch we've added a new way to do this. Add SFINAEFailure to the ext_ diagnostic and drop the err_ one:

def ext_deprecated_string_literal_conversion : ExtWarn<
  "conversion from string literal to %0 is ill-formed in C++11">,
  InGroup<DeprecatedWritableStr>, SFINAEFailure;
4527

We don't phrase warnings this way elsewhere in the compiler. For consistency, word this as:

"ISO C++11 does not allow conversion from string literal to %0"
lib/Sema/SemaExprCXX.cpp
3069

Please add a test for the case where this happens in a SFINAE context. For instance:

int f(int, char*);
template<typename T> void g(T, decltype(f(T{}, "foo")));
template<typename T> int &g(...);
int &r = g(0, 0); // ok in c++11, picks g(...) because g<int> has a substitution failure
lib/Sema/SemaOverload.cpp
1152

This is redundant; StandardConversionSequence::setAsIdentityConversion already does this.

1244

Likewise.

2986

Likewise.

2989

Likewise.

3180

Likewise.

3194

Likewise.

3323

Please add a brief comment before this explaining that the deprecated conversion is ill-formed in c++11, so we rank any conversion sequence that uses it as worse than any sequence that does not.

4430

This looks wrong: if TryImplicitConversion performed a deprecated conversion to char*, we still have one in this conversion sequence. This might matter for a case like binding a 'char *const &' to a string literal.

4556

This is redundant.

4560

Likewise.

4652

Likewise.

4814

Likewise.

test/SemaCXX/overload-0x.cpp
22–24

Please use a syntax-only test for this, rather than an IR generation test. For instance:

  int &r = f("foo");
#if __cplusplus < 201103L
  // expected-warning@-2 {{deprecated}}
  // expected-error@-3 {{cannot bind}}
#endif
42

This won't work: "warning: " is not part of the string that -verify checks.

ismailp updated this revision to Unknown Object (????).Nov 19 2013, 8:23 AM

Changes since last revision:

  • Changed diagnostics message.
  • Removed superfluous DeprecatedStringLiteralToCharPtr = false.
  • Added comments in CompareImplicitConversionSequences function.
  • Added test for SFINAE failure.
  • Changed tests to check syntax instead of IR

Sorry, forgot to Clowncopterize =(

I think this is basically ready to commit, just a couple of tiny comments.

lib/Sema/SemaOverload.cpp
3295–3304

Now DeprecatedStringLiteralToCharPtr is always initialized, do you need this complexity? Would this work:

return (ICS.isStandard() && ICS.Standard.DeprecatedStringLiteralToCharPtr) ||
       (ICS.isUserDefined() && ICS.UserDefined.Before.DeprecatedStringLiteralToCharPtr);
test/SemaCXX/overload-0x.cpp
1–6

Do you really need all three of these and the macro? Since you're not testing the generated IR any more, I think you can drop the #ifndef and go down to two RUN: lines (one for c++11 and one for c++98).

ismailp updated this revision to Unknown Object (????).Jan 17 2014, 9:02 AM

Added missing initializations for StandardConversionSequence::DeprecatedStringLiteralToCharPtr.
This enables us to check fewer members of ImplicitConversionSequence to decide whether this
use of conversion is deprecated in C++11.

Sorry, this is the first time I have tried arcanist (I thought arc diff would give me the diff :) ), and it seems like patch is posted before removing macros from the test case, and responding to your last feedback.

Nevertheless, yes, the condition can be simplified, as long as DeprecatedStringLiteralToCharPtr is initialized. It turned out it wasn't always initialized. CHECK_BEST seems like irrelevant, and will be removed. If the rest seems ready, I will remove them before commit, instead of posting another patch.

rsmith accepted this revision.Jan 17 2014, 12:23 PM

LGTM with the test case cleaned up to remove CHECK_BEST and the extra RUN line. Thanks!

ismailp closed this revision.Aug 10 2014, 3:00 PM

Commited in r199513, r199997 , and r199988.