]> granicus.if.org Git - clang/commitdiff
Implement the C++0x "trailing return type" feature, e.g.,
authorDouglas Gregor <dgregor@apple.com>
Fri, 1 Oct 2010 18:44:50 +0000 (18:44 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 1 Oct 2010 18:44:50 +0000 (18:44 +0000)
  auto f(int) -> int

from Daniel Wallin!

(With a few minor bug fixes from me).

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

15 files changed:
docs/LanguageExtensions.html
include/clang/AST/TypeLoc.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Parse/Parser.h
include/clang/Sema/DeclSpec.h
lib/AST/ASTContext.cpp
lib/Lex/PPMacroExpansion.cpp
lib/Parse/ParseDecl.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Sema/DeclSpec.cpp
lib/Sema/SemaType.cpp
lib/Sema/TreeTransform.h
lib/Serialization/ASTReader.cpp
lib/Serialization/ASTWriter.cpp
test/SemaCXX/trailing-return-0x.cpp [new file with mode: 0644]

index 75a4608993e9efbf9ea46c390abc86325a477759..48d5aa64a1d047a7892e8b1910b871041a5c0255 100644 (file)
@@ -41,6 +41,7 @@ td {
   <li><a href="#cxx_auto_type">C++0x type inference</a></li>
   <li><a href="#cxx_variadic_templates">C++0x variadic templates</a></li>
   <li><a href="#cxx_inline_namespaces">C++0x inline namespaces</a></li>
+  <li><a href="#cxx_trailing_return">C++0x trailing return type</a></li>
   </ul>
 <li><a href="#blocks">Blocks</a></li>
 <li><a href="#overloading-in-c">Function Overloading in C</a></li>
@@ -353,6 +354,11 @@ enabled. clang does not yet fully implement this feature.</p>
 <p>Use <tt>__has_feature(cxx_inline_namespaces)</tt> to determine if support for
 inline namespaces is enabled.</p>
 
+<h3 id="cxx_trailing_return">C++0x trailing return type</h3>
+
+<p>Use <tt>__has_feature(cxx_trailing_return)</tt> to determine if support for
+the alternate function declaration syntax with trailing return type is enabled.</p>
+
 <!-- ======================================================================= -->
 <h2 id="blocks">Blocks</h2>
 <!-- ======================================================================= -->
index 2323580174920ee2017ebd5dc632089ff4d325be..35bba03d77357d6ff375e2094dd6977a7c821bf6 100644 (file)
@@ -812,6 +812,7 @@ public:
 
 struct FunctionLocInfo {
   SourceLocation LParenLoc, RParenLoc;
+  bool TrailingReturn;
 };
 
 /// \brief Wrapper for source info for functions.
@@ -839,6 +840,13 @@ public:
     getLocalData()->RParenLoc = Loc;
   }
 
+  bool getTrailingReturn() const {
+    return getLocalData()->TrailingReturn;
+  }
+  void setTrailingReturn(bool Trailing) {
+    getLocalData()->TrailingReturn = Trailing;
+  }
+
   unsigned getNumArgs() const {
     if (isa<FunctionNoProtoType>(getTypePtr()))
       return 0;
@@ -858,6 +866,7 @@ public:
   void initializeLocal(SourceLocation Loc) {
     setLParenLoc(Loc);
     setRParenLoc(Loc);
+    setTrailingReturn(false);
     for (unsigned i = 0, e = getNumArgs(); i != e; ++i)
       setArg(i, NULL);
   }
index 9a2576208fe32f631289e54391e84f9fe1203ce2..e6bcd86609d1c168cba931fad35ca433dd6d55c0 100644 (file)
@@ -811,6 +811,10 @@ def err_auto_not_allowed : Error<
   "|class member|exception declaration|template parameter|block literal}0">;
 def err_auto_var_requires_init : Error<
   "declaration of variable %0 with type %1 requires an initializer">;
+def err_auto_missing_trailing_return : Error<
+  "'auto' return without trailing return type">;
+def err_trailing_return_without_auto : Error<
+  "trailing return type without 'auto' return">;
   
 // C++0x attributes
 def err_repeat_attribute : Error<"'%0' attribute cannot be repeated">;
index f8db30af5bbd50198b619ada28111c7bbd2f2a85..1cdeef727e01cbb139ee7de04583eecdf74b2319 100644 (file)
@@ -1061,6 +1061,10 @@ private:
                                    llvm::SmallVectorImpl<SourceRange> &Ranges,
                                    bool &hasAnyExceptionSpec);
 
+  //===--------------------------------------------------------------------===//
+  // C++0x 8: Function declaration trailing-return-type
+  TypeResult ParseTrailingReturnType();
+
   //===--------------------------------------------------------------------===//
   // C++ 2.13.5: C++ Boolean Literals
   ExprResult ParseCXXBoolLiteral();
index 0893ae7e495832c5e0dc926f2f99728535fa4c1b..0957e77557eb182ad77f140f6689fe8d952e18b0 100644 (file)
@@ -925,6 +925,11 @@ struct DeclaratorChunk {
     /// specification and their locations.
     TypeAndRange *Exceptions;
 
+    /// TrailingReturnType - If this isn't null, it's the trailing return type
+    /// specified. This is actually a ParsedType, but stored as void* to
+    /// allow union storage.
+    void *TrailingReturnType;
+
     /// freeArgs - reset the argument list to having zero arguments.  This is
     /// used in various places for error recovery.
     void freeArgs() {
@@ -1077,7 +1082,8 @@ struct DeclaratorChunk {
                                      SourceRange *ExceptionRanges,
                                      unsigned NumExceptions,
                                      SourceLocation LPLoc, SourceLocation RPLoc,
-                                     Declarator &TheDeclarator);
+                                     Declarator &TheDeclarator,
+                                     ParsedType TrailingReturnType = ParsedType());
 
   /// getBlockPointer - Return a DeclaratorChunk for a block.
   ///
index ce76fcd9ba462fd1b31867214766645727188f48..5c9530beb244a75a669640e86d4c9614ba3fdfea 100644 (file)
@@ -1733,6 +1733,7 @@ QualType ASTContext::getFunctionType(QualType ResultTy,const QualType *ArgArray,
                                      bool hasAnyExceptionSpec, unsigned NumExs,
                                      const QualType *ExArray,
                                      const FunctionType::ExtInfo &Info) {
+
   const CallingConv CallConv= Info.getCC();
   // Unique functions, to guarantee there is only one function of a particular
   // structure.
index 46b671da19916fbd3d3bde5f31e526fc3385ffec..ac69595227fde6ecf304f7b7234f7a86ce167f59 100644 (file)
@@ -518,6 +518,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
            .Case("cxx_exceptions", LangOpts.Exceptions)
            .Case("cxx_rtti", LangOpts.RTTI)
            .Case("cxx_static_assert", LangOpts.CPlusPlus0x)
+           .Case("cxx_trailing_return", LangOpts.CPlusPlus0x)
            .Case("objc_nonfragile_abi", LangOpts.ObjCNonFragileABI)
            .Case("objc_weak_class", LangOpts.ObjCNonFragileABI)
            .Case("ownership_holds", true)
index 0a48d4d550777501630d4dcf8d569117607399dd..89b41828b062301ea2e3397ad00ebd814ccf5b3b 100644 (file)
@@ -3024,6 +3024,8 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
   // lparen is already consumed!
   assert(D.isPastIdentifier() && "Should not call before identifier!");
 
+  ParsedType TrailingReturnType;
+
   // This parameter list may be empty.
   if (Tok.is(tok::r_paren)) {
     if (RequiresArg) {
@@ -3055,6 +3057,11 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
         assert(Exceptions.size() == ExceptionRanges.size() &&
                "Produced different number of exception types and ranges.");
       }
+
+      // Parse trailing-return-type.
+      if (getLang().CPlusPlus0x && Tok.is(tok::arrow)) {
+        TrailingReturnType = ParseTrailingReturnType().get();
+      }
     }
 
     // Remember that we parsed a function type, and remember the attributes.
@@ -3069,7 +3076,8 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
                                                Exceptions.data(),
                                                ExceptionRanges.data(),
                                                Exceptions.size(),
-                                               LParenLoc, RParenLoc, D),
+                                               LParenLoc, RParenLoc, D,
+                                               TrailingReturnType),
                   EndLoc);
     return;
   }
@@ -3260,9 +3268,6 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
     ConsumeToken();
   }
 
-  // Leave prototype scope.
-  PrototypeScope.Exit();
-
   // If we have the closing ')', eat it.
   SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
   SourceLocation EndLoc = RParenLoc;
@@ -3289,8 +3294,19 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
       assert(Exceptions.size() == ExceptionRanges.size() &&
              "Produced different number of exception types and ranges.");
     }
+
+    // Parse trailing-return-type.
+    if (getLang().CPlusPlus0x && Tok.is(tok::arrow)) {
+      TrailingReturnType = ParseTrailingReturnType().get();
+    }
   }
 
+  // FIXME: We should leave the prototype scope before parsing the exception
+  // specification, and then reenter it when parsing the trailing return type.
+
+  // Leave prototype scope.
+  PrototypeScope.Exit();
+
   // Remember that we parsed a function type, and remember the attributes.
   D.AddTypeInfo(DeclaratorChunk::getFunction(/*proto*/true, IsVariadic,
                                              EllipsisLoc,
@@ -3301,7 +3317,8 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D,
                                              Exceptions.data(),
                                              ExceptionRanges.data(),
                                              Exceptions.size(),
-                                             LParenLoc, RParenLoc, D),
+                                             LParenLoc, RParenLoc, D,
+                                             TrailingReturnType),
                 EndLoc);
 }
 
index 092268c82156760966b27298cb48413567d01bbd..59d4f9fad39d19f4cf3522689cec67f9649781aa 100644 (file)
@@ -1871,6 +1871,25 @@ bool Parser::ParseExceptionSpecification(SourceLocation &EndLoc,
   return false;
 }
 
+/// ParseTrailingReturnType - Parse a trailing return type on a new-style
+/// function declaration.
+TypeResult Parser::ParseTrailingReturnType() {
+  assert(Tok.is(tok::arrow) && "expected arrow");
+
+  ConsumeToken();
+
+  // FIXME: Need to suppress declarations when parsing this typename.
+  // Otherwise in this function definition:
+  //
+  //   auto f() -> struct X {}
+  //
+  // struct X is parsed as class definition because of the trailing
+  // brace.
+
+  SourceRange Range;
+  return ParseTypeName(&Range);
+}
+
 /// \brief We have just started parsing the definition of a new class,
 /// so push that class onto our stack of classes that is currently
 /// being parsed.
index b46e8af9db868095111773cbce877241809a70fa..979b76ae984e4d4b746ad1f5440a8f90a7f805d5 100644 (file)
@@ -59,7 +59,8 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic,
                                              unsigned NumExceptions,
                                              SourceLocation LPLoc,
                                              SourceLocation RPLoc,
-                                             Declarator &TheDeclarator) {
+                                             Declarator &TheDeclarator,
+                                             ParsedType TrailingReturnType) {
   DeclaratorChunk I;
   I.Kind                 = Function;
   I.Loc                  = LPLoc;
@@ -76,6 +77,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic,
   I.Fun.hasAnyExceptionSpec = hasAnyExceptionSpec;
   I.Fun.NumExceptions    = NumExceptions;
   I.Fun.Exceptions       = 0;
+  I.Fun.TrailingReturnType   = TrailingReturnType.getAsOpaquePtr();
 
   // new[] an argument array if needed.
   if (NumArgs) {
index 254eb7984dc4b8f67a3c33e8d587f03c6eeb6067..542c31abfc48e6c860abcd85c050a490594a8599 100644 (file)
@@ -994,7 +994,30 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S,
                           &ReturnTypeInfo);
     break;
   }
-  
+
+  // Check for auto functions and trailing return type and adjust the
+  // return type accordingly.
+  if (getLangOptions().CPlusPlus0x && D.isFunctionDeclarator()) {
+    const DeclaratorChunk::FunctionTypeInfo &FTI = D.getTypeObject(0).Fun;
+    if (T == Context.UndeducedAutoTy) {
+      if (FTI.TrailingReturnType) {
+          T = GetTypeFromParser(ParsedType::getFromOpaquePtr(FTI.TrailingReturnType),
+                                &ReturnTypeInfo);
+      }
+      else {
+          Diag(D.getDeclSpec().getTypeSpecTypeLoc(),
+               diag::err_auto_missing_trailing_return);
+          T = Context.IntTy;
+          D.setInvalidType(true);
+      }
+    }
+    else if (FTI.TrailingReturnType) {
+      Diag(D.getDeclSpec().getTypeSpecTypeLoc(),
+           diag::err_trailing_return_without_auto);
+      D.setInvalidType(true);
+    }
+  }
+
   if (T.isNull())
     return Context.getNullTypeSourceInfo();
 
@@ -1635,6 +1658,7 @@ namespace {
       assert(Chunk.Kind == DeclaratorChunk::Function);
       TL.setLParenLoc(Chunk.Loc);
       TL.setRParenLoc(Chunk.EndLoc);
+      TL.setTrailingReturn(!!Chunk.Fun.TrailingReturnType);
 
       const DeclaratorChunk::FunctionTypeInfo &FTI = Chunk.Fun;
       for (unsigned i = 0, e = TL.getNumArgs(), tpi = 0; i != e; ++i) {
index 71492254c6ef69b60b92de743572d63b431d90bf..135ca46958d82f53535a88fbd4152866833ecd90 100644 (file)
@@ -2928,20 +2928,33 @@ TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB,
   // the parameters, because users tend to expect this (even if they shouldn't
   // rely on it!).
   //
-  // FIXME: When we implement late-specified return types, we'll need to
-  // instantiate the return tpe *after* the parameter types in that case,
-  // since the return type can then refer to the parameters themselves (via
-  // decltype, sizeof, etc.).
+  // When the function has a trailing return type, we instantiate the
+  // parameters before the return type,  since the return type can then refer
+  // to the parameters themselves (via decltype, sizeof, etc.).
+  //
   llvm::SmallVector<QualType, 4> ParamTypes;
   llvm::SmallVector<ParmVarDecl*, 4> ParamDecls;
   FunctionProtoType *T = TL.getTypePtr();
-  QualType ResultType = getDerived().TransformType(TLB, TL.getResultLoc());
-  if (ResultType.isNull())
-    return QualType();
 
-  if (getDerived().TransformFunctionTypeParams(TL, ParamTypes, ParamDecls))
-    return QualType();  
-  
+  QualType ResultType;
+
+  if (TL.getTrailingReturn()) {
+    if (getDerived().TransformFunctionTypeParams(TL, ParamTypes, ParamDecls))
+      return QualType();
+
+    ResultType = getDerived().TransformType(TLB, TL.getResultLoc());
+    if (ResultType.isNull())
+      return QualType();
+  }
+  else {
+    ResultType = getDerived().TransformType(TLB, TL.getResultLoc());
+    if (ResultType.isNull())
+      return QualType();
+
+    if (getDerived().TransformFunctionTypeParams(TL, ParamTypes, ParamDecls))
+      return QualType();
+  }
+
   QualType Result = TL.getType();
   if (getDerived().AlwaysRebuild() ||
       ResultType != T->getResultType() ||
@@ -2959,6 +2972,7 @@ TreeTransform<Derived>::TransformFunctionProtoType(TypeLocBuilder &TLB,
   FunctionProtoTypeLoc NewTL = TLB.push<FunctionProtoTypeLoc>(Result);
   NewTL.setLParenLoc(TL.getLParenLoc());
   NewTL.setRParenLoc(TL.getRParenLoc());
+  NewTL.setTrailingReturn(TL.getTrailingReturn());
   for (unsigned i = 0, e = NewTL.getNumArgs(); i != e; ++i)
     NewTL.setArg(i, ParamDecls[i]);
 
@@ -2983,6 +2997,7 @@ QualType TreeTransform<Derived>::TransformFunctionNoProtoType(
   FunctionNoProtoTypeLoc NewTL = TLB.push<FunctionNoProtoTypeLoc>(Result);
   NewTL.setLParenLoc(TL.getLParenLoc());
   NewTL.setRParenLoc(TL.getRParenLoc());
+  NewTL.setTrailingReturn(false);
 
   return Result;
 }
index 7b4264d51c916435a65a64f21371fd9ede75dd05..ac7385976c09974e3e69e6c7208345a50b1a5333 100644 (file)
@@ -2942,6 +2942,7 @@ void TypeLocReader::VisitExtVectorTypeLoc(ExtVectorTypeLoc TL) {
 void TypeLocReader::VisitFunctionTypeLoc(FunctionTypeLoc TL) {
   TL.setLParenLoc(SourceLocation::getFromRawEncoding(Record[Idx++]));
   TL.setRParenLoc(SourceLocation::getFromRawEncoding(Record[Idx++]));
+  TL.setTrailingReturn(Record[Idx++]);
   for (unsigned i = 0, e = TL.getNumArgs(); i != e; ++i) {
     TL.setArg(i, cast_or_null<ParmVarDecl>(Reader.GetDecl(Record[Idx++])));
   }
index f5f96ecd24e8081fb098af855e888f5134e5c617..ef8c52f4be1cade8d221b3fce0ee52c463b9efa5 100644 (file)
@@ -412,6 +412,7 @@ void TypeLocWriter::VisitExtVectorTypeLoc(ExtVectorTypeLoc TL) {
 void TypeLocWriter::VisitFunctionTypeLoc(FunctionTypeLoc TL) {
   Writer.AddSourceLocation(TL.getLParenLoc(), Record);
   Writer.AddSourceLocation(TL.getRParenLoc(), Record);
+  Record.push_back(TL.getTrailingReturn());
   for (unsigned i = 0, e = TL.getNumArgs(); i != e; ++i)
     Writer.AddDeclRef(TL.getArg(i), Record);
 }
diff --git a/test/SemaCXX/trailing-return-0x.cpp b/test/SemaCXX/trailing-return-0x.cpp
new file mode 100644 (file)
index 0000000..0912750
--- /dev/null
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s
+
+template <class T>
+struct only
+{
+    only(T) {}
+
+    template <class U>
+    only(U)
+    {
+        static_assert(sizeof(U) == 0, "expected type failure");
+    }
+};
+
+auto f() -> int
+{
+    return 0;
+}
+
+auto g(); // expected-error{{return without trailing return type}}
+
+int h() -> int; // expected-error{{trailing return type without 'auto'}}
+
+int x;
+
+template <class T>
+auto i(T x) -> decltype(x)
+{
+    return x;
+}
+
+only<double> p1 = i(1.0);
+
+template <class T>
+struct X
+{
+    auto f(T x) -> T { return x; }
+
+    template <class U>
+    auto g(T x, U y) -> decltype(x + y)
+    {
+        return x + y;
+    }
+
+  template<typename U>
+  struct nested {
+    template <class V>
+    auto h(T x, U y, V z) -> decltype(x + y + z)
+    {
+        return x + y + z;
+    }
+  };
+
+  template<typename U>
+  nested<U> get_nested();
+};
+
+X<int> xx;
+only<int> p2 = xx.f(0L);
+only<double> p3 = xx.g(0L, 1.0);
+only<double> p4 = xx.get_nested<double>().h(0L, 1.0, 3.14f);