]> granicus.if.org Git - clang/commitdiff
Reapply Sema: allow imaginary constants via GNU extension if UDL overloads not present.
authorTim Northover <tnorthover@apple.com>
Wed, 9 Aug 2017 14:56:48 +0000 (14:56 +0000)
committerTim Northover <tnorthover@apple.com>
Wed, 9 Aug 2017 14:56:48 +0000 (14:56 +0000)
C++14 added user-defined literal support for complex numbers so that you
can write something like "complex<double> val = 2i". However, there is
an existing GNU extension supporting this syntax and interpreting the
result as a _Complex type.

This changes parsing so that such literals are interpreted in terms of
C++14's operators if an overload is present but otherwise falls back to
the original GNU extension.

(We now have more robust diagnostics for implicit conversions so the
libc++ test that caused the original revert still passes).

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@310478 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticLexKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
lib/Lex/LiteralSupport.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaLookup.cpp
test/SemaCXX/imaginary-constants.cpp [new file with mode: 0644]

index d6de5c04a74d7bb47d9f3385478cbf10f47965da..40bd06ef3ddb95ef3e8445740a3a567a331cc323 100644 (file)
@@ -173,8 +173,6 @@ def warn_char_constant_too_large : Warning<
 def err_multichar_utf_character_literal : Error<
   "Unicode character literals may not contain multiple characters">;
 def err_exponent_has_no_digits : Error<"exponent has no digits">;
-def ext_imaginary_constant : Extension<
-  "imaginary constants are a GNU extension">, InGroup<GNUImaginaryConstant>;
 def err_hex_constant_requires : Error<
   "hexadecimal floating %select{constant|literal}0 requires "
   "%select{an exponent|a significand}1">;
index 8a5c6458301f95ef88fbea67ae9309241f23345f..b251bd1967aaed68a6c59372a6ed3ff8ee7fb6d1 100644 (file)
@@ -194,6 +194,8 @@ def warn_duplicate_declspec : Warning<"duplicate '%0' declaration specifier">,
   InGroup<DuplicateDeclSpecifier>;
 def ext_plain_complex : ExtWarn<
   "plain '_Complex' requires a type specifier; assuming '_Complex double'">;
+def ext_imaginary_constant : Extension<
+  "imaginary constants are a GNU extension">, InGroup<GNUImaginaryConstant>;
 def ext_integer_complex : Extension<
   "complex integer types are a GNU extension">, InGroup<GNUComplexInteger>;
 
index 30fb56e338e88ffdda2b917f371fa13149b0f6f3..267b613acef7631bdc1a5d8c17c77d0c24c903e6 100644 (file)
@@ -3020,6 +3020,8 @@ public:
   enum LiteralOperatorLookupResult {
     /// \brief The lookup resulted in an error.
     LOLR_Error,
+    /// \brief The lookup found no match but no diagnostic was issued.
+    LOLR_ErrorNoDiagnostic,
     /// \brief The lookup found a single 'cooked' literal operator, which
     /// expects a normal literal to be built and passed to it.
     LOLR_Cooked,
@@ -3144,7 +3146,8 @@ public:
                                                     ArrayRef<QualType> ArgTys,
                                                     bool AllowRaw,
                                                     bool AllowTemplate,
-                                                    bool AllowStringTemplate);
+                                                    bool AllowStringTemplate,
+                                                    bool DiagnoseMissing);
   bool isKnownName(StringRef name);
 
   void ArgumentDependentLookup(DeclarationName Name, SourceLocation Loc,
index a598a467816a8e34fb63718a13934369b9832150..3cc45cea97aa9cad448a5773135203b08f5dfe72 100644 (file)
@@ -658,9 +658,6 @@ NumericLiteralParser::NumericLiteralParser(StringRef TokSpelling,
           break;
         }
       }
-      // "i", "if", and "il" are user-defined suffixes in C++1y.
-      if (*s == 'i' && PP.getLangOpts().CPlusPlus14)
-        break;
       // fall through.
     case 'j':
     case 'J':
@@ -672,35 +669,34 @@ NumericLiteralParser::NumericLiteralParser(StringRef TokSpelling,
     break;
   }
 
-  if (s != ThisTokEnd) {
+  // "i", "if", and "il" are user-defined suffixes in C++1y.
+  if (s != ThisTokEnd || isImaginary) {
     // FIXME: Don't bother expanding UCNs if !tok.hasUCN().
     expandUCNs(UDSuffixBuf, StringRef(SuffixBegin, ThisTokEnd - SuffixBegin));
     if (isValidUDSuffix(PP.getLangOpts(), UDSuffixBuf)) {
-      // Any suffix pieces we might have parsed are actually part of the
-      // ud-suffix.
-      isLong = false;
-      isUnsigned = false;
-      isLongLong = false;
-      isFloat = false;
-      isHalf = false;
-      isImaginary = false;
-      MicrosoftInteger = 0;
+      if (!isImaginary) {
+        // Any suffix pieces we might have parsed are actually part of the
+        // ud-suffix.
+        isLong = false;
+        isUnsigned = false;
+        isLongLong = false;
+        isFloat = false;
+        isHalf = false;
+        isImaginary = false;
+        MicrosoftInteger = 0;
+      }
 
       saw_ud_suffix = true;
       return;
     }
 
-    // Report an error if there are any.
-    PP.Diag(PP.AdvanceToTokenCharacter(TokLoc, SuffixBegin - ThisTokBegin),
-            diag::err_invalid_suffix_constant)
-      << StringRef(SuffixBegin, ThisTokEnd-SuffixBegin) << isFPConstant;
-    hadError = true;
-    return;
-  }
-
-  if (isImaginary) {
-    PP.Diag(PP.AdvanceToTokenCharacter(TokLoc, SuffixBegin - ThisTokBegin),
-            diag::ext_imaginary_constant);
+    if (s != ThisTokEnd) {
+      // Report an error if there are any.
+      PP.Diag(PP.AdvanceToTokenCharacter(TokLoc, SuffixBegin - ThisTokBegin),
+              diag::err_invalid_suffix_constant)
+          << StringRef(SuffixBegin, ThisTokEnd - SuffixBegin) << isFPConstant;
+      hadError = true;
+    }
   }
 }
 
index a1d3967244985b2ea3423dd69847af5443b5d217..7842f308c8014ef12c87370a004f6e8679752200 100644 (file)
@@ -1502,8 +1502,9 @@ static ExprResult BuildCookedLiteralOperatorCall(Sema &S, Scope *Scope,
 
   LookupResult R(S, OpName, UDSuffixLoc, Sema::LookupOrdinaryName);
   if (S.LookupLiteralOperator(Scope, R, llvm::makeArrayRef(ArgTy, Args.size()),
-                              /*AllowRaw*/false, /*AllowTemplate*/false,
-                              /*AllowStringTemplate*/false) == Sema::LOLR_Error)
+                              /*AllowRaw*/ false, /*AllowTemplate*/ false,
+                              /*AllowStringTemplate*/ false,
+                              /*DiagnoseMissing*/ true) == Sema::LOLR_Error)
     return ExprError();
 
   return S.BuildLiteralOperatorCall(R, OpNameInfo, Args, LitEndLoc);
@@ -1594,8 +1595,9 @@ Sema::ActOnStringLiteral(ArrayRef<Token> StringToks, Scope *UDLScope) {
 
   LookupResult R(*this, OpName, UDSuffixLoc, LookupOrdinaryName);
   switch (LookupLiteralOperator(UDLScope, R, ArgTy,
-                                /*AllowRaw*/false, /*AllowTemplate*/false,
-                                /*AllowStringTemplate*/true)) {
+                                /*AllowRaw*/ false, /*AllowTemplate*/ false,
+                                /*AllowStringTemplate*/ true,
+                                /*DiagnoseMissing*/ true)) {
 
   case LOLR_Cooked: {
     llvm::APInt Len(Context.getIntWidth(SizeType), Literal.GetNumStringChars());
@@ -1628,6 +1630,7 @@ Sema::ActOnStringLiteral(ArrayRef<Token> StringToks, Scope *UDLScope) {
   }
   case LOLR_Raw:
   case LOLR_Template:
+  case LOLR_ErrorNoDiagnostic:
     llvm_unreachable("unexpected literal operator lookup result");
   case LOLR_Error:
     return ExprError();
@@ -3250,11 +3253,15 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
     // literal or a cooked one.
     LookupResult R(*this, OpName, UDSuffixLoc, LookupOrdinaryName);
     switch (LookupLiteralOperator(UDLScope, R, CookedTy,
-                                  /*AllowRaw*/true, /*AllowTemplate*/true,
-                                  /*AllowStringTemplate*/false)) {
+                                  /*AllowRaw*/ true, /*AllowTemplate*/ true,
+                                  /*AllowStringTemplate*/ false,
+                                  /*DiagnoseMissing*/ !Literal.isImaginary)) {
+    case LOLR_ErrorNoDiagnostic:
+      // Lookup failure for imaginary constants isn't fatal, there's still the
+      // GNU extension producing _Complex types.
+      break;
     case LOLR_Error:
       return ExprError();
-
     case LOLR_Cooked: {
       Expr *Lit;
       if (Literal.isFloatingLiteral()) {
@@ -3470,10 +3477,12 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
   }
 
   // If this is an imaginary literal, create the ImaginaryLiteral wrapper.
-  if (Literal.isImaginary)
+  if (Literal.isImaginary) {
     Res = new (Context) ImaginaryLiteral(Res,
                                         Context.getComplexType(Res->getType()));
 
+    Diag(Tok.getLocation(), diag::ext_imaginary_constant);
+  }
   return Res;
 }
 
index 4f88edcb31f5767e281703e2ce584a06ab6b526d..a89d3ace2446daca8da360956016f9bb741ec454 100644 (file)
@@ -3122,7 +3122,7 @@ Sema::LiteralOperatorLookupResult
 Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
                             ArrayRef<QualType> ArgTys,
                             bool AllowRaw, bool AllowTemplate,
-                            bool AllowStringTemplate) {
+                            bool AllowStringTemplate, bool DiagnoseMissing) {
   LookupName(R, S);
   assert(R.getResultKind() != LookupResult::Ambiguous &&
          "literal operator lookup can't be ambiguous");
@@ -3223,11 +3223,15 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
     return LOLR_StringTemplate;
 
   // Didn't find anything we could use.
-  Diag(R.getNameLoc(), diag::err_ovl_no_viable_literal_operator)
-    << R.getLookupName() << (int)ArgTys.size() << ArgTys[0]
-    << (ArgTys.size() == 2 ? ArgTys[1] : QualType()) << AllowRaw
-    << (AllowTemplate || AllowStringTemplate);
-  return LOLR_Error;
+  if (DiagnoseMissing) {
+    Diag(R.getNameLoc(), diag::err_ovl_no_viable_literal_operator)
+        << R.getLookupName() << (int)ArgTys.size() << ArgTys[0]
+        << (ArgTys.size() == 2 ? ArgTys[1] : QualType()) << AllowRaw
+        << (AllowTemplate || AllowStringTemplate);
+    return LOLR_Error;
+  }
+
+  return LOLR_ErrorNoDiagnostic;
 }
 
 void ADLResult::insert(NamedDecl *New) {
diff --git a/test/SemaCXX/imaginary-constants.cpp b/test/SemaCXX/imaginary-constants.cpp
new file mode 100644 (file)
index 0000000..bbff927
--- /dev/null
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -include %s -std=gnu++98
+// RUN: %clang_cc1 -fsyntax-only -verify %s -include %s -std=c++11
+// RUN: %clang_cc1 -fsyntax-only -verify %s -include %s -std=c++14 -DCXX14=1
+
+// expected-no-diagnostics
+
+#ifndef HEADER
+#define HEADER
+
+_Complex int val1 = 2i;
+_Complex long val2 = 2il;
+_Complex long long val3 = 2ill;
+_Complex float val4 = 2.0if;
+_Complex double val5 = 2.0i;
+_Complex long double val6 = 2.0il;
+
+#if CXX14
+
+#pragma clang system_header
+
+namespace std {
+  template<typename T> struct complex {};
+  complex<float> operator""if(unsigned long long);
+  complex<float> operator""if(long double);
+
+  complex<double> operator"" i(unsigned long long);
+  complex<double> operator"" i(long double);
+
+  complex<long double> operator"" il(unsigned long long);
+  complex<long double> operator"" il(long double);
+}
+
+using namespace std;
+
+complex<float> f1 = 2.0if;
+complex<float> f2 = 2if;
+complex<double> d1 = 2.0i;
+complex<double> d2 = 2i;
+complex<long double> l1 = 2.0il;
+complex<long double> l2 = 2il;
+
+#endif
+
+#endif