Skip to content

Commit

Permalink
Add support for GCC's '__auto_type' extension, per the GCC manual:
Browse files Browse the repository at this point in the history
https://gcc.gnu.org/onlinedocs/gcc/Typeof.html

Differences from the GCC extension:
 * __auto_type is also permitted in C++ (but only in places where
   it could appear in C), allowing its use in headers that might
   be shared across C and C++, or used from C++98
 * __auto_type can be combined with a declarator, as with C++ auto
   (for instance, "__auto_type *p")
 * multiple variables can be declared in a single __auto_type
   declaration, with the C++ semantics (the deduced type must be
   the same in each case)

This patch also adds a missing restriction on applying typeof to
a bit-field, which GCC has historically rejected in C (due to
lack of clarity as to whether the operand should be promoted).
The same restriction also applies to __auto_type in C (in both
GCC and Clang).

This also fixes PR25449.

Patch by Nicholas Allegra!

llvm-svn: 252690
  • Loading branch information
zygoloid committed Nov 11, 2015
1 parent a543f77 commit e301ba2
Show file tree
Hide file tree
Showing 33 changed files with 192 additions and 93 deletions.
2 changes: 1 addition & 1 deletion clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
UnaryTransformType::UTTKind UKind) const;

/// \brief C++11 deduced auto type.
QualType getAutoType(QualType DeducedType, bool IsDecltypeAuto,
QualType getAutoType(QualType DeducedType, AutoTypeKeyword Keyword,
bool IsDependent) const;

/// \brief C++11 deduction pattern for 'auto' type.
Expand Down
34 changes: 24 additions & 10 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,16 @@ enum RefQualifierKind {
RQ_RValue
};

/// Which keyword(s) were used to create an AutoType.
enum class AutoTypeKeyword {
/// \brief auto
Auto,
/// \brief decltype(auto)
DecltypeAuto,
/// \brief __auto_type (GNU extension)
GNUAutoType
};

/// The base class of the type hierarchy.
///
/// A central concept with types is that each type always has a canonical
Expand Down Expand Up @@ -1428,8 +1438,9 @@ class Type : public ExtQualsTypeCommonBase {

unsigned : NumTypeBits;

/// Was this placeholder type spelled as 'decltype(auto)'?
unsigned IsDecltypeAuto : 1;
/// Was this placeholder type spelled as 'auto', 'decltype(auto)',
/// or '__auto_type'? AutoTypeKeyword value.
unsigned Keyword : 2;
};

union {
Expand Down Expand Up @@ -3902,22 +3913,26 @@ class SubstTemplateTypeParmPackType : public Type, public llvm::FoldingSetNode {
/// is no deduced type and an auto type is canonical. In the latter case, it is
/// also a dependent type.
class AutoType : public Type, public llvm::FoldingSetNode {
AutoType(QualType DeducedType, bool IsDecltypeAuto,
bool IsDependent)
AutoType(QualType DeducedType, AutoTypeKeyword Keyword, bool IsDependent)
: Type(Auto, DeducedType.isNull() ? QualType(this, 0) : DeducedType,
/*Dependent=*/IsDependent, /*InstantiationDependent=*/IsDependent,
/*VariablyModified=*/false,
/*ContainsParameterPack=*/DeducedType.isNull()
? false : DeducedType->containsUnexpandedParameterPack()) {
assert((DeducedType.isNull() || !IsDependent) &&
"auto deduced to dependent type");
AutoTypeBits.IsDecltypeAuto = IsDecltypeAuto;
AutoTypeBits.Keyword = (unsigned)Keyword;
}

friend class ASTContext; // ASTContext creates these

public:
bool isDecltypeAuto() const { return AutoTypeBits.IsDecltypeAuto; }
bool isDecltypeAuto() const {
return getKeyword() == AutoTypeKeyword::DecltypeAuto;
}
AutoTypeKeyword getKeyword() const {
return (AutoTypeKeyword)AutoTypeBits.Keyword;
}

bool isSugared() const { return !isCanonicalUnqualified(); }
QualType desugar() const { return getCanonicalTypeInternal(); }
Expand All @@ -3932,14 +3947,13 @@ class AutoType : public Type, public llvm::FoldingSetNode {
}

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getDeducedType(), isDecltypeAuto(),
isDependentType());
Profile(ID, getDeducedType(), getKeyword(), isDependentType());
}

static void Profile(llvm::FoldingSetNodeID &ID, QualType Deduced,
bool IsDecltypeAuto, bool IsDependent) {
AutoTypeKeyword Keyword, bool IsDependent) {
ID.AddPointer(Deduced.getAsOpaquePtr());
ID.AddBoolean(IsDecltypeAuto);
ID.AddInteger((unsigned)Keyword);
ID.AddBoolean(IsDependent);
}

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def : DiagGroup<"aggregate-return">;
def GNUAlignofExpression : DiagGroup<"gnu-alignof-expression">;
def AmbigMemberTemplate : DiagGroup<"ambiguous-member-template">;
def GNUAnonymousStruct : DiagGroup<"gnu-anonymous-struct">;
def GNUAutoType : DiagGroup<"gnu-auto-type">;
def ArrayBounds : DiagGroup<"array-bounds">;
def ArrayBoundsPointerArithmetic : DiagGroup<"array-bounds-pointer-arithmetic">;
def Availability : DiagGroup<"availability">;
Expand Down Expand Up @@ -707,6 +708,7 @@ def C99 : DiagGroup<"c99-extensions">;

// A warning group for warnings about GCC extensions.
def GNU : DiagGroup<"gnu", [GNUAlignofExpression, GNUAnonymousStruct,
GNUAutoType,
GNUBinaryLiteral, GNUCaseRange,
GNUComplexInteger, GNUCompoundLiteralInitializer,
GNUConditionalOmittedOperand, GNUDesignator,
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,9 @@ def ext_decltype_auto_type_specifier : ExtWarn<
def warn_cxx11_compat_decltype_auto_type_specifier : Warning<
"'decltype(auto)' type specifier is incompatible with C++ standards before "
"C++14">, InGroup<CXXPre14Compat>, DefaultIgnore;
def ext_auto_type : Extension<
"'__auto_type' is a GNU extension">,
InGroup<GNUAutoType>;
def ext_for_range : ExtWarn<
"range-based for loop is a C++11 extension">, InGroup<CXX11>;
def warn_cxx98_compat_for_range : Warning<
Expand Down
26 changes: 17 additions & 9 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1664,18 +1664,22 @@ def warn_cxx98_compat_auto_type_specifier : Warning<
"'auto' type specifier is incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;
def err_auto_variable_cannot_appear_in_own_initializer : Error<
"variable %0 declared with 'auto' type cannot appear in its own initializer">;
"variable %0 declared with %select{'auto'|'decltype(auto)'|'__auto_type'}1 "
"type cannot appear in its own initializer">;
def err_illegal_decl_array_of_auto : Error<
"'%0' declared as array of %1">;
def err_new_array_of_auto : Error<
"cannot allocate array of 'auto'">;
def err_auto_not_allowed : Error<
"%select{'auto'|'decltype(auto)'}0 not allowed %select{in function prototype"
"|in non-static struct member"
"|in non-static union member|in non-static class member|in interface member"
"%select{'auto'|'decltype(auto)'|'__auto_type'}0 not allowed "
"%select{in function prototype"
"|in non-static struct member|in struct member"
"|in non-static union member|in union member"
"|in non-static class member|in interface member"
"|in exception declaration|in template parameter|in block literal"
"|in template argument|in typedef|in type alias|in function return type"
"|in conversion function type|here|in lambda parameter}1">;
"|in conversion function type|here|in lambda parameter"
"|in type allocated by 'new'|in K&R-style function parameter}1">;
def err_auto_not_allowed_var_inst : Error<
"'auto' variable template instantiation is not allowed">;
def err_auto_var_requires_init : Error<
Expand Down Expand Up @@ -1709,8 +1713,8 @@ def err_auto_var_deduction_failure_from_init_list : Error<
def err_auto_new_deduction_failure : Error<
"new expression for type %0 has incompatible constructor argument of type %1">;
def err_auto_different_deductions : Error<
"'%select{auto|decltype(auto)}0' deduced as %1 in declaration of %2 and "
"deduced as %3 in declaration of %4">;
"'%select{auto|decltype(auto)|__auto_type}0' deduced as %1 in declaration "
"of %2 and deduced as %3 in declaration of %4">;
def err_implied_std_initializer_list_not_found : Error<
"cannot deduce type of initializer list because std::initializer_list was "
"not found; include <initializer_list>">;
Expand All @@ -1720,6 +1724,10 @@ def warn_dangling_std_initializer_list : Warning<
"array backing the initializer list will be destroyed at the end of "
"%select{the full-expression|the constructor}0">,
InGroup<DiagGroup<"dangling-initializer-list">>;
def err_auto_init_list_from_c : Error<
"cannot use __auto_type with initializer list in C">;
def err_auto_bitfield : Error<
"cannot pass bit-field as __auto_type initializer in C">;

// C++1y decltype(auto) type
def err_decltype_auto_cannot_be_combined : Error<
Expand Down Expand Up @@ -4812,8 +4820,8 @@ def err_sizeof_alignof_function_type : Error<
"function type">;
def err_openmp_default_simd_align_expr : Error<
"invalid application of '__builtin_omp_required_simd_align' to an expression, only type is allowed">;
def err_sizeof_alignof_bitfield : Error<
"invalid application of '%select{sizeof|alignof}0' to bit-field">;
def err_sizeof_alignof_typeof_bitfield : Error<
"invalid application of '%select{sizeof|alignof|typeof}0' to bit-field">;
def err_alignof_member_of_incomplete_type : Error<
"invalid application of 'alignof' to a field of a class still being defined">;
def err_vecstep_non_scalar_vector_type : Error<
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/Specifiers.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ namespace clang {
TST_underlyingType, // __underlying_type for C++11
TST_auto, // C++11 auto
TST_decltype_auto, // C++1y decltype(auto)
TST_auto_type, // __auto_type extension
TST_unknown_anytype, // __unknown_anytype extension
TST_atomic, // C11 _Atomic
TST_error // erroneous type
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ KEYWORD(__real , KEYALL)
KEYWORD(__thread , KEYALL)
KEYWORD(__FUNCTION__ , KEYALL)
KEYWORD(__PRETTY_FUNCTION__ , KEYALL)
KEYWORD(__auto_type , KEYALL)

// GNU Extensions (outside impl-reserved namespace)
KEYWORD(typeof , KEYGNU)
Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/Sema/DeclSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ class DeclSpec {
static const TST TST_decltype_auto = clang::TST_decltype_auto;
static const TST TST_underlyingType = clang::TST_underlyingType;
static const TST TST_auto = clang::TST_auto;
static const TST TST_auto_type = clang::TST_auto_type;
static const TST TST_unknown_anytype = clang::TST_unknown_anytype;
static const TST TST_atomic = clang::TST_atomic;
static const TST TST_error = clang::TST_error;
Expand Down Expand Up @@ -512,7 +513,8 @@ class DeclSpec {
void setTypeofParensRange(SourceRange range) { TypeofParensRange = range; }

bool containsPlaceholderType() const {
return TypeSpecType == TST_auto || TypeSpecType == TST_decltype_auto;
return (TypeSpecType == TST_auto || TypeSpecType == TST_auto_type ||
TypeSpecType == TST_decltype_auto);
}

bool hasTagDefinition() const;
Expand Down
10 changes: 5 additions & 5 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3983,20 +3983,20 @@ QualType ASTContext::getUnaryTransformType(QualType BaseType,
/// getAutoType - Return the uniqued reference to the 'auto' type which has been
/// deduced to the given type, or to the canonical undeduced 'auto' type, or the
/// canonical deduced-but-dependent 'auto' type.
QualType ASTContext::getAutoType(QualType DeducedType, bool IsDecltypeAuto,
QualType ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword,
bool IsDependent) const {
if (DeducedType.isNull() && !IsDecltypeAuto && !IsDependent)
if (DeducedType.isNull() && Keyword == AutoTypeKeyword::Auto && !IsDependent)
return getAutoDeductType();

// Look in the folding set for an existing type.
void *InsertPos = nullptr;
llvm::FoldingSetNodeID ID;
AutoType::Profile(ID, DeducedType, IsDecltypeAuto, IsDependent);
AutoType::Profile(ID, DeducedType, Keyword, IsDependent);
if (AutoType *AT = AutoTypes.FindNodeOrInsertPos(ID, InsertPos))
return QualType(AT, 0);

AutoType *AT = new (*this, TypeAlignment) AutoType(DeducedType,
IsDecltypeAuto,
Keyword,
IsDependent);
Types.push_back(AT);
if (InsertPos)
Expand Down Expand Up @@ -4036,7 +4036,7 @@ QualType ASTContext::getAtomicType(QualType T) const {
QualType ASTContext::getAutoDeductType() const {
if (AutoDeductTy.isNull())
AutoDeductTy = QualType(
new (*this, TypeAlignment) AutoType(QualType(), /*decltype(auto)*/false,
new (*this, TypeAlignment) AutoType(QualType(), AutoTypeKeyword::Auto,
/*dependent*/false),
0);
return AutoDeductTy;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1746,7 +1746,7 @@ QualType ASTNodeImporter::VisitAutoType(const AutoType *T) {
return QualType();
}

return Importer.getToContext().getAutoType(ToDeduced, T->isDecltypeAuto(),
return Importer.getToContext().getAutoType(ToDeduced, T->getKeyword(),
/*IsDependent*/false);
}

Expand Down
6 changes: 4 additions & 2 deletions clang/lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2653,9 +2653,11 @@ void CXXNameMangler::mangleType(const UnaryTransformType *T) {
void CXXNameMangler::mangleType(const AutoType *T) {
QualType D = T->getDeducedType();
// <builtin-type> ::= Da # dependent auto
if (D.isNull())
if (D.isNull()) {
assert(T->getKeyword() != AutoTypeKeyword::GNUAutoType &&
"shouldn't need to mangle __auto_type!");
Out << (T->isDecltypeAuto() ? "Dc" : "Da");
else
} else
mangleType(D);
}

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/MicrosoftMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1818,6 +1818,8 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
Out << '?';
mangleQualifiers(ResultType.getLocalQualifiers(), /*IsMember=*/false);
Out << '?';
assert(AT->getKeyword() != AutoTypeKeyword::GNUAutoType &&
"shouldn't need to mangle __auto_type!");
mangleSourceName(AT->isDecltypeAuto() ? "<decltype-auto>" : "<auto>");
Out << '@';
} else {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ struct SimpleTransformVisitor
== T->getDeducedType().getAsOpaquePtr())
return QualType(T, 0);

return Ctx.getAutoType(deducedType, T->isDecltypeAuto(),
return Ctx.getAutoType(deducedType, T->getKeyword(),
T->isDependentType());
}

Expand Down
6 changes: 5 additions & 1 deletion clang/lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,11 @@ void TypePrinter::printAutoBefore(const AutoType *T, raw_ostream &OS) {
if (!T->getDeducedType().isNull()) {
printBefore(T->getDeducedType(), OS);
} else {
OS << (T->isDecltypeAuto() ? "decltype(auto)" : "auto");
switch (T->getKeyword()) {
case AutoTypeKeyword::Auto: OS << "auto"; break;
case AutoTypeKeyword::DecltypeAuto: OS << "decltype(auto)"; break;
case AutoTypeKeyword::GNUAutoType: OS << "__auto_type"; break;
}
spaceBeforePlaceHolder(OS);
}
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ class AnnotatingParser {
(!Line.MightBeFunctionDecl || Current.NestingLevel != 0)) {
Contexts.back().FirstStartOfName = &Current;
Current.Type = TT_StartOfName;
} else if (Current.is(tok::kw_auto)) {
} else if (Current.isOneOf(tok::kw_auto, tok::kw___auto_type)) {
AutoFound = true;
} else if (Current.is(tok::arrow) &&
Style.Language == FormatStyle::LK_Java) {
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3138,6 +3138,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
PrevSpec, DiagID, Policy);
isStorageClass = true;
break;
case tok::kw___auto_type:
Diag(Tok, diag::ext_auto_type);
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_auto_type, Loc, PrevSpec,
DiagID, Policy);
break;
case tok::kw_register:
isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_register, Loc,
PrevSpec, DiagID, Policy);
Expand Down Expand Up @@ -4440,6 +4445,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
case tok::kw___private_extern__:
case tok::kw_static:
case tok::kw_auto:
case tok::kw___auto_type:
case tok::kw_register:
case tok::kw___thread:
case tok::kw_thread_local:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Parse/ParseObjc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,7 @@ IdentifierInfo *Parser::ParseObjCSelectorPiece(SourceLocation &SelectorLoc) {
case tok::kw__Bool:
case tok::kw__Complex:
case tok::kw___alignof:
case tok::kw___auto_type:
IdentifierInfo *II = Tok.getIdentifierInfo();
SelectorLoc = ConsumeToken();
return II;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Parse/ParseTentative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,7 @@ class TentativeParseCCC : public CorrectionCandidateCallback {
/// [GNU] typeof-specifier
/// [GNU] '_Complex'
/// [C++11] 'auto'
/// [GNU] '__auto_type'
/// [C++11] 'decltype' ( expression )
/// [C++1y] 'decltype' ( 'auto' )
///
Expand Down Expand Up @@ -1262,6 +1263,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
case tok::kw_restrict:
case tok::kw__Complex:
case tok::kw___attribute:
case tok::kw___auto_type:
return TPResult::True;

// Microsoft
Expand Down Expand Up @@ -1515,6 +1517,7 @@ bool Parser::isCXXDeclarationSpecifierAType() {
case tok::kw_double:
case tok::kw_void:
case tok::kw___unknown_anytype:
case tok::kw___auto_type:
return true;

case tok::kw_auto:
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/DeclSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ bool Declarator::isDeclarationOfFunction() const {
switch (DS.getTypeSpecType()) {
case TST_atomic:
case TST_auto:
case TST_auto_type:
case TST_bool:
case TST_char:
case TST_char16:
Expand Down Expand Up @@ -476,6 +477,7 @@ const char *DeclSpec::getSpecifierName(DeclSpec::TST T,
case DeclSpec::TST_typeofType:
case DeclSpec::TST_typeofExpr: return "typeof";
case DeclSpec::TST_auto: return "auto";
case DeclSpec::TST_auto_type: return "__auto_type";
case DeclSpec::TST_decltype: return "(decltype)";
case DeclSpec::TST_decltype_auto: return "decltype(auto)";
case DeclSpec::TST_underlyingType: return "__underlying_type";
Expand Down
Loading

0 comments on commit e301ba2

Please sign in to comment.