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