From dab60ad68a3a98d687305941a3852e793705f945 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 1 Oct 2010 18:44:50 +0000 Subject: [PATCH] Implement the C++0x "trailing return type" feature, e.g., 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 --- docs/LanguageExtensions.html | 6 +++ include/clang/AST/TypeLoc.h | 9 ++++ include/clang/Basic/DiagnosticSemaKinds.td | 4 ++ include/clang/Parse/Parser.h | 4 ++ include/clang/Sema/DeclSpec.h | 8 ++- lib/AST/ASTContext.cpp | 1 + lib/Lex/PPMacroExpansion.cpp | 1 + lib/Parse/ParseDecl.cpp | 27 ++++++++-- lib/Parse/ParseDeclCXX.cpp | 19 +++++++ lib/Sema/DeclSpec.cpp | 4 +- lib/Sema/SemaType.cpp | 26 ++++++++- lib/Sema/TreeTransform.h | 35 +++++++++---- lib/Serialization/ASTReader.cpp | 1 + lib/Serialization/ASTWriter.cpp | 1 + test/SemaCXX/trailing-return-0x.cpp | 61 ++++++++++++++++++++++ 15 files changed, 189 insertions(+), 18 deletions(-) create mode 100644 test/SemaCXX/trailing-return-0x.cpp diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index 75a4608993..48d5aa64a1 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -41,6 +41,7 @@ td {
  • C++0x type inference
  • C++0x variadic templates
  • C++0x inline namespaces
  • +
  • C++0x trailing return type
  • Blocks
  • Function Overloading in C
  • @@ -353,6 +354,11 @@ enabled. clang does not yet fully implement this feature.

    Use __has_feature(cxx_inline_namespaces) to determine if support for inline namespaces is enabled.

    +

    C++0x trailing return type

    + +

    Use __has_feature(cxx_trailing_return) to determine if support for +the alternate function declaration syntax with trailing return type is enabled.

    +

    Blocks

    diff --git a/include/clang/AST/TypeLoc.h b/include/clang/AST/TypeLoc.h index 2323580174..35bba03d77 100644 --- a/include/clang/AST/TypeLoc.h +++ b/include/clang/AST/TypeLoc.h @@ -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(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); } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 9a2576208f..e6bcd86609 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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">; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index f8db30af5b..1cdeef727e 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1061,6 +1061,10 @@ private: llvm::SmallVectorImpl &Ranges, bool &hasAnyExceptionSpec); + //===--------------------------------------------------------------------===// + // C++0x 8: Function declaration trailing-return-type + TypeResult ParseTrailingReturnType(); + //===--------------------------------------------------------------------===// // C++ 2.13.5: C++ Boolean Literals ExprResult ParseCXXBoolLiteral(); diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index 0893ae7e49..0957e77557 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -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. /// diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index ce76fcd9ba..5c9530beb2 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -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. diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 46b671da19..ac69595227 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -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) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 0a48d4d550..89b41828b0 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -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); } diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 092268c821..59d4f9fad3 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -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. diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index b46e8af9db..979b76ae98 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -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) { diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 254eb7984d..542c31abfc 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -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) { diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 71492254c6..135ca46958 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -2928,20 +2928,33 @@ TreeTransform::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 ParamTypes; llvm::SmallVector 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::TransformFunctionProtoType(TypeLocBuilder &TLB, FunctionProtoTypeLoc NewTL = TLB.push(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::TransformFunctionNoProtoType( FunctionNoProtoTypeLoc NewTL = TLB.push(Result); NewTL.setLParenLoc(TL.getLParenLoc()); NewTL.setRParenLoc(TL.getRParenLoc()); + NewTL.setTrailingReturn(false); return Result; } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 7b4264d51c..ac7385976c 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -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(Reader.GetDecl(Record[Idx++]))); } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index f5f96ecd24..ef8c52f4be 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -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 index 0000000000..0912750ee0 --- /dev/null +++ b/test/SemaCXX/trailing-return-0x.cpp @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +template +struct only +{ + only(T) {} + + template + 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 +auto i(T x) -> decltype(x) +{ + return x; +} + +only p1 = i(1.0); + +template +struct X +{ + auto f(T x) -> T { return x; } + + template + auto g(T x, U y) -> decltype(x + y) + { + return x + y; + } + + template + struct nested { + template + auto h(T x, U y, V z) -> decltype(x + y + z) + { + return x + y + z; + } + }; + + template + nested get_nested(); +}; + +X xx; +only p2 = xx.f(0L); +only p3 = xx.g(0L, 1.0); +only p4 = xx.get_nested().h(0L, 1.0, 3.14f); -- 2.40.0