From 4cf4a5e96ab0babd13774b17112e7c1d83042ea7 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 28 Mar 2013 01:55:44 +0000 Subject: [PATCH] Support C11 _Atomic type qualifier. This is more-or-less just syntactic sugar for the _Atomic type specifier. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@178210 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 3 +- include/clang/Parse/Parser.h | 3 +- include/clang/Sema/DeclSpec.h | 30 +++-- include/clang/Sema/Sema.h | 6 +- lib/Parse/ParseDecl.cpp | 43 +++++-- lib/Parse/ParseStmt.cpp | 3 + lib/Sema/DeclSpec.cpp | 16 ++- lib/Sema/SemaCodeComplete.cpp | 3 + lib/Sema/SemaDecl.cpp | 17 ++- lib/Sema/SemaType.cpp | 141 +++++++++++++++++---- test/Parser/asm.c | 6 + test/Parser/atomic.c | 35 +++++ test/SemaCXX/atomic-type.cxx | 25 +++- 13 files changed, 265 insertions(+), 66 deletions(-) create mode 100644 test/Parser/atomic.c diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 7e26387cc8..2a71b17293 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1166,8 +1166,7 @@ def warn_cxx98_compat_static_data_member_in_union : Warning< def err_union_member_of_reference_type : Error< "union member %0 has reference type %1">; def ext_anonymous_struct_union_qualified : Extension< - "anonymous %select{struct|union}0 cannot be '%select{const|volatile|" - "restrict}1'">; + "anonymous %select{struct|union}0 cannot be '%1'">; def err_different_return_type_for_overriding_virtual_function : Error< "virtual function %0 has a different return type " "%diff{($) than the function it overrides (which has return type $)|" diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 1ac0f3be5e..11a3bdbd50 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1999,7 +1999,8 @@ private: DirectDeclParseFunction DirectDeclParser); void ParseTypeQualifierListOpt(DeclSpec &DS, bool GNUAttributesAllowed = true, - bool CXX11AttributesAllowed = true); + bool CXX11AttributesAllowed = true, + bool AtomicAllowed = true); void ParseDirectDeclarator(Declarator &D); void ParseParenDeclarator(Declarator &D); void ParseFunctionDeclarator(Declarator &D, diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index 0c734216b1..48cd4d14f8 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -291,7 +291,10 @@ public: TQ_unspecified = 0, TQ_const = 1, TQ_restrict = 2, - TQ_volatile = 4 + TQ_volatile = 4, + // This has no corresponding Qualifiers::TQ value, because it's not treated + // as a qualifier in our type system. + TQ_atomic = 8 }; /// ParsedSpecifiers - Flags to query which specifiers were applied. This is @@ -321,7 +324,7 @@ private: unsigned TypeSpecOwned : 1; // type-qualifiers - unsigned TypeQualifiers : 3; // Bitwise OR of TQ. + unsigned TypeQualifiers : 4; // Bitwise OR of TQ. // function-specifier unsigned FS_inline_specified : 1; @@ -369,7 +372,7 @@ private: /// TSTNameLoc provides source range info for tag types. SourceLocation TSTNameLoc; SourceRange TypeofParensRange; - SourceLocation TQ_constLoc, TQ_restrictLoc, TQ_volatileLoc; + SourceLocation TQ_constLoc, TQ_restrictLoc, TQ_volatileLoc, TQ_atomicLoc; SourceLocation FS_inlineLoc, FS_virtualLoc, FS_explicitLoc, FS_noreturnLoc; SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc; @@ -503,6 +506,7 @@ public: SourceLocation getConstSpecLoc() const { return TQ_constLoc; } SourceLocation getRestrictSpecLoc() const { return TQ_restrictLoc; } SourceLocation getVolatileSpecLoc() const { return TQ_volatileLoc; } + SourceLocation getAtomicSpecLoc() const { return TQ_atomicLoc; } /// \brief Clear out all of the type qualifiers. void ClearTypeQualifiers() { @@ -510,6 +514,7 @@ public: TQ_constLoc = SourceLocation(); TQ_restrictLoc = SourceLocation(); TQ_volatileLoc = SourceLocation(); + TQ_atomicLoc = SourceLocation(); } // function-specifier @@ -1025,8 +1030,8 @@ struct DeclaratorChunk { }; struct PointerTypeInfo : TypeInfoCommon { - /// The type qualifiers: const/volatile/restrict. - unsigned TypeQuals : 3; + /// The type qualifiers: const/volatile/restrict/atomic. + unsigned TypeQuals : 4; /// The location of the const-qualifier, if any. unsigned ConstQualLoc; @@ -1037,6 +1042,9 @@ struct DeclaratorChunk { /// The location of the restrict-qualifier, if any. unsigned RestrictQualLoc; + /// The location of the _Atomic-qualifier, if any. + unsigned AtomicQualLoc; + void destroy() { } }; @@ -1051,8 +1059,8 @@ struct DeclaratorChunk { }; struct ArrayTypeInfo : TypeInfoCommon { - /// The type qualifiers for the array: const/volatile/restrict. - unsigned TypeQuals : 3; + /// The type qualifiers for the array: const/volatile/restrict/_Atomic. + unsigned TypeQuals : 4; /// True if this dimension included the 'static' keyword. bool hasStatic : 1; @@ -1274,16 +1282,16 @@ struct DeclaratorChunk { struct BlockPointerTypeInfo : TypeInfoCommon { /// For now, sema will catch these as invalid. - /// The type qualifiers: const/volatile/restrict. - unsigned TypeQuals : 3; + /// The type qualifiers: const/volatile/restrict/_Atomic. + unsigned TypeQuals : 4; void destroy() { } }; struct MemberPointerTypeInfo : TypeInfoCommon { - /// The type qualifiers: const/volatile/restrict. - unsigned TypeQuals : 3; + /// The type qualifiers: const/volatile/restrict/_Atomic. + unsigned TypeQuals : 4; // CXXScopeSpec has a constructor, so it can't be a direct member. // So we need some pointer-aligned storage and a bit of trickery. union { diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 4259bb88ed..049290b5bb 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -936,10 +936,8 @@ public: QualType BuildQualifiedType(QualType T, SourceLocation Loc, Qualifiers Qs, const DeclSpec *DS = 0); - QualType BuildQualifiedType(QualType T, SourceLocation Loc, unsigned CVR, - const DeclSpec *DS = 0) { - return BuildQualifiedType(T, Loc, Qualifiers::fromCVRMask(CVR), DS); - } + QualType BuildQualifiedType(QualType T, SourceLocation Loc, unsigned CVRA, + const DeclSpec *DS = 0); QualType BuildPointerType(QualType T, SourceLocation Loc, DeclarationName Entity); QualType BuildReferenceType(QualType T, bool LValueRef, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7dfbf5ff86..03bffde06e 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2905,8 +2905,17 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, continue; case tok::kw__Atomic: - ParseAtomicSpecifier(DS); - continue; + // C11 6.7.2.4/4: + // If the _Atomic keyword is immediately followed by a left parenthesis, + // it is interpreted as a type specifier (with a type name), not as a + // type qualifier. + if (NextToken().is(tok::l_paren)) { + ParseAtomicSpecifier(DS); + continue; + } + isInvalid = DS.SetTypeQual(DeclSpec::TQ_atomic, Loc, PrevSpec, DiagID, + getLangOpts()); + break; // OpenCL qualifiers: case tok::kw_private: @@ -3814,7 +3823,7 @@ bool Parser::isTypeSpecifierQualifier() { case tok::kw_private: return getLangOpts().OpenCL; - // C11 _Atomic() + // C11 _Atomic case tok::kw__Atomic: return true; } @@ -3959,7 +3968,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { case tok::annot_decltype: case tok::kw_constexpr: - // C11 _Atomic() + // C11 _Atomic case tok::kw__Atomic: return true; @@ -4099,7 +4108,8 @@ bool Parser::isConstructorDeclarator() { /// void Parser::ParseTypeQualifierListOpt(DeclSpec &DS, bool VendorAttributesAllowed, - bool CXX11AttributesAllowed) { + bool CXX11AttributesAllowed, + bool AtomicAllowed) { if (getLangOpts().CPlusPlus11 && CXX11AttributesAllowed && isCXX11AttributeSpecifier()) { ParsedAttributesWithRange attrs(AttrFactory); @@ -4132,6 +4142,12 @@ void Parser::ParseTypeQualifierListOpt(DeclSpec &DS, isInvalid = DS.SetTypeQual(DeclSpec::TQ_restrict, Loc, PrevSpec, DiagID, getLangOpts()); break; + case tok::kw__Atomic: + if (!AtomicAllowed) + goto DoneWithTypeQuals; + isInvalid = DS.SetTypeQual(DeclSpec::TQ_atomic, Loc, PrevSpec, DiagID, + getLangOpts()); + break; // OpenCL qualifiers: case tok::kw_private: @@ -4346,6 +4362,10 @@ void Parser::ParseDeclaratorInternal(Declarator &D, if (DS.getTypeQualifiers() & DeclSpec::TQ_volatile) Diag(DS.getVolatileSpecLoc(), diag::err_invalid_reference_qualifier_application) << "volatile"; + // 'restrict' is permitted as an extension. + if (DS.getTypeQualifiers() & DeclSpec::TQ_atomic) + Diag(DS.getAtomicSpecLoc(), + diag::err_invalid_reference_qualifier_application) << "_Atomic"; } // Recursively parse the declarator. @@ -4368,7 +4388,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D, } } - // Remember that we parsed a reference type. It doesn't have type-quals. + // Remember that we parsed a reference type. D.AddTypeInfo(DeclaratorChunk::getReference(DS.getTypeQualifiers(), Loc, Kind == tok::amp), DS.getAttributes(), @@ -4809,7 +4829,9 @@ void Parser::ParseFunctionDeclarator(Declarator &D, // with the virt-specifier-seq and pure-specifier in the same way. // Parse cv-qualifier-seq[opt]. - ParseTypeQualifierListOpt(DS, false /*no attributes*/, false); + ParseTypeQualifierListOpt(DS, /*VendorAttributesAllowed*/ false, + /*CXX11AttributesAllowed*/ false, + /*AtomicAllowed*/ false); if (!DS.getSourceRange().getEnd().isInvalid()) { EndLoc = DS.getSourceRange().getEnd(); ConstQualifierLoc = DS.getConstSpecLoc(); @@ -5350,14 +5372,13 @@ void Parser::ParseTypeofSpecifier(DeclSpec &DS) { /// _Atomic ( type-name ) /// void Parser::ParseAtomicSpecifier(DeclSpec &DS) { - assert(Tok.is(tok::kw__Atomic) && "Not an atomic specifier"); + assert(Tok.is(tok::kw__Atomic) && NextToken().is(tok::l_paren) && + "Not an atomic specifier"); SourceLocation StartLoc = ConsumeToken(); BalancedDelimiterTracker T(*this, tok::l_paren); - if (T.expectAndConsume(diag::err_expected_lparen_after, "_Atomic")) { - SkipUntil(tok::r_paren); + if (T.consumeOpen()) return; - } TypeResult Result = ParseTypeName(); if (Result.isInvalid()) { diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 6db47a0157..355f3694bb 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1810,6 +1810,9 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) { Diag(Loc, diag::w_asm_qualifier_ignored) << "const"; if (DS.getTypeQualifiers() & DeclSpec::TQ_restrict) Diag(Loc, diag::w_asm_qualifier_ignored) << "restrict"; + // FIXME: Once GCC supports _Atomic, check whether it permits it here. + if (DS.getTypeQualifiers() & DeclSpec::TQ_atomic) + Diag(Loc, diag::w_asm_qualifier_ignored) << "_Atomic"; // Remember if this was a volatile asm. bool isVolatile = DS.getTypeQualifiers() & DeclSpec::TQ_volatile; diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index 569352ecb9..45f524b97b 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -169,6 +169,9 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, SourceLocation LocalRangeEnd, Declarator &TheDeclarator, TypeResult TrailingReturnType) { + assert(!(TypeQuals & DeclSpec::TQ_atomic) && + "function cannot have _Atomic qualifier"); + DeclaratorChunk I; I.Kind = Function; I.Loc = LocalRangeBegin; @@ -442,6 +445,7 @@ const char *DeclSpec::getSpecifierName(TQ T) { case DeclSpec::TQ_const: return "const"; case DeclSpec::TQ_restrict: return "restrict"; case DeclSpec::TQ_volatile: return "volatile"; + case DeclSpec::TQ_atomic: return "_Atomic"; } llvm_unreachable("Unknown typespec!"); } @@ -710,12 +714,14 @@ bool DeclSpec::SetTypeQual(TQ T, SourceLocation Loc, const char *&PrevSpec, TypeQualifiers |= T; switch (T) { - default: llvm_unreachable("Unknown type qualifier!"); - case TQ_const: TQ_constLoc = Loc; break; - case TQ_restrict: TQ_restrictLoc = Loc; break; - case TQ_volatile: TQ_volatileLoc = Loc; break; + case TQ_unspecified: break; + case TQ_const: TQ_constLoc = Loc; return false; + case TQ_restrict: TQ_restrictLoc = Loc; return false; + case TQ_volatile: TQ_volatileLoc = Loc; return false; + case TQ_atomic: TQ_atomicLoc = Loc; return false; } - return false; + + llvm_unreachable("Unknown type qualifier!"); } bool DeclSpec::setFunctionSpecInline(SourceLocation Loc) { diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 2fa08fd5e1..9c77e3f154 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -3729,6 +3729,9 @@ void Sema::CodeCompleteTypeQualifiers(DeclSpec &DS) { if (getLangOpts().C99 && !(DS.getTypeQualifiers() & DeclSpec::TQ_restrict)) Results.AddResult("restrict"); + if (getLangOpts().C11 && + !(DS.getTypeQualifiers() & DeclSpec::TQ_atomic)) + Results.AddResult("_Atomic"); Results.ExitScope(); HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(), diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 9261df559e..dfa87d314d 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -3175,6 +3175,8 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, if (DS.getTypeQualifiers() & DeclSpec::TQ_volatile) Diag(DS.getConstSpecLoc(), DiagID) << "volatile"; // Restrict is covered above. + if (DS.getTypeQualifiers() & DeclSpec::TQ_atomic) + Diag(DS.getAtomicSpecLoc(), DiagID) << "_Atomic"; } // Warn about ignored type attributes, for example: @@ -3411,18 +3413,23 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, if (DS.getTypeQualifiers()) { if (DS.getTypeQualifiers() & DeclSpec::TQ_const) Diag(DS.getConstSpecLoc(), diag::ext_anonymous_struct_union_qualified) - << Record->isUnion() << 0 + << Record->isUnion() << "const" << FixItHint::CreateRemoval(DS.getConstSpecLoc()); if (DS.getTypeQualifiers() & DeclSpec::TQ_volatile) - Diag(DS.getVolatileSpecLoc(), + Diag(DS.getVolatileSpecLoc(), diag::ext_anonymous_struct_union_qualified) - << Record->isUnion() << 1 + << Record->isUnion() << "volatile" << FixItHint::CreateRemoval(DS.getVolatileSpecLoc()); if (DS.getTypeQualifiers() & DeclSpec::TQ_restrict) - Diag(DS.getRestrictSpecLoc(), + Diag(DS.getRestrictSpecLoc(), diag::ext_anonymous_struct_union_qualified) - << Record->isUnion() << 2 + << Record->isUnion() << "restrict" << FixItHint::CreateRemoval(DS.getRestrictSpecLoc()); + if (DS.getTypeQualifiers() & DeclSpec::TQ_atomic) + Diag(DS.getAtomicSpecLoc(), + diag::ext_anonymous_struct_union_qualified) + << Record->isUnion() << "_Atomic" + << FixItHint::CreateRemoval(DS.getAtomicSpecLoc()); DS.ClearTypeQualifiers(); } diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 57f2aae80e..25112fefe9 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -1089,21 +1089,18 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { // of a function type includes any type qualifiers, the behavior is // undefined." if (Result->isFunctionType() && TypeQuals) { - // Get some location to point at, either the C or V location. - SourceLocation Loc; if (TypeQuals & DeclSpec::TQ_const) - Loc = DS.getConstSpecLoc(); + S.Diag(DS.getConstSpecLoc(), diag::warn_typecheck_function_qualifiers) + << Result << DS.getSourceRange(); else if (TypeQuals & DeclSpec::TQ_volatile) - Loc = DS.getVolatileSpecLoc(); + S.Diag(DS.getVolatileSpecLoc(), diag::warn_typecheck_function_qualifiers) + << Result << DS.getSourceRange(); else { - assert((TypeQuals & DeclSpec::TQ_restrict) && - "Has CVR quals but not C, V, or R?"); - // No diagnostic; we'll diagnose 'restrict' applied to a function type - // later, in BuildQualifiedType. + assert((TypeQuals & (DeclSpec::TQ_restrict | DeclSpec::TQ_atomic)) && + "Has CVRA quals but not C, V, R, or A?"); + // No diagnostic; we'll diagnose 'restrict' or '_Atomic' applied to a + // function type later, in BuildQualifiedType. } - if (!Loc.isInvalid()) - S.Diag(Loc, diag::warn_typecheck_function_qualifiers) - << Result << DS.getSourceRange(); } // C++ [dcl.ref]p1: @@ -1116,6 +1113,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { TypeQuals && Result->isReferenceType()) { TypeQuals &= ~DeclSpec::TQ_const; TypeQuals &= ~DeclSpec::TQ_volatile; + TypeQuals &= ~DeclSpec::TQ_atomic; } // C90 6.5.3 constraints: "The same type qualifier shall not appear more @@ -1133,11 +1131,17 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { << "volatile"; } - // C90 doesn't have restrict, so it doesn't force us to produce a warning - // in this case. + // C90 doesn't have restrict nor _Atomic, so it doesn't force us to + // produce a warning in this case. } - return S.BuildQualifiedType(Result, DeclLoc, TypeQuals, &DS); + QualType Qualified = S.BuildQualifiedType(Result, DeclLoc, TypeQuals, &DS); + + // If adding qualifiers fails, just use the unqualified type. + if (Qualified.isNull()) + declarator.setInvalidType(true); + else + Result = Qualified; } return Result; @@ -1188,6 +1192,39 @@ QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc, return Context.getQualifiedType(T, Qs); } +QualType Sema::BuildQualifiedType(QualType T, SourceLocation Loc, + unsigned CVRA, const DeclSpec *DS) { + // Convert from DeclSpec::TQ to Qualifiers::TQ by just dropping TQ_atomic. + unsigned CVR = CVRA & ~DeclSpec::TQ_atomic; + + // C11 6.7.3/5: + // If the same qualifier appears more than once in the same + // specifier-qualifier-list, either directly or via one or more typedefs, + // the behavior is the same as if it appeared only once. + // + // It's not specified what happens when the _Atomic qualifier is applied to + // a type specified with the _Atomic specifier, but we assume that this + // should be treated as if the _Atomic qualifier appeared multiple times. + if (CVRA & DeclSpec::TQ_atomic && !T->isAtomicType()) { + // C11 6.7.3/5: + // If other qualifiers appear along with the _Atomic qualifier in a + // specifier-qualifier-list, the resulting type is the so-qualified + // atomic type. + // + // Don't need to worry about array types here, since _Atomic can't be + // applied to such types. + SplitQualType Split = T.getSplitUnqualifiedType(); + T = BuildAtomicType(QualType(Split.Ty, 0), + DS ? DS->getAtomicSpecLoc() : Loc); + if (T.isNull()) + return T; + Split.Quals.addCVRQualifiers(CVR); + return BuildQualifiedType(T, Loc, Split.Quals); + } + + return BuildQualifiedType(T, Loc, Qualifiers::fromCVRMask(CVR), DS); +} + /// \brief Build a paren type including \p T. QualType Sema::BuildParenType(QualType T) { return Context.getParenType(T); @@ -1847,6 +1884,7 @@ static void DiagnoseIgnoredQualifiers(unsigned Quals, SourceLocation ConstQualLoc, SourceLocation VolatileQualLoc, SourceLocation RestrictQualLoc, + SourceLocation AtomicQualLoc, Sema& S) { std::string QualStr; unsigned NumQuals = 0; @@ -1855,38 +1893,47 @@ static void DiagnoseIgnoredQualifiers(unsigned Quals, FixItHint ConstFixIt; FixItHint VolatileFixIt; FixItHint RestrictFixIt; + FixItHint AtomicFixIt; const SourceManager &SM = S.getSourceManager(); // FIXME: The locations here are set kind of arbitrarily. It'd be nicer to // find a range and grow it to encompass all the qualifiers, regardless of // the order in which they textually appear. - if (Quals & Qualifiers::Const) { + if (Quals & DeclSpec::TQ_const) { ConstFixIt = FixItHint::CreateRemoval(ConstQualLoc); QualStr = "const"; ++NumQuals; if (!Loc.isValid() || SM.isBeforeInTranslationUnit(ConstQualLoc, Loc)) Loc = ConstQualLoc; } - if (Quals & Qualifiers::Volatile) { + if (Quals & DeclSpec::TQ_volatile) { VolatileFixIt = FixItHint::CreateRemoval(VolatileQualLoc); QualStr += (NumQuals == 0 ? "volatile" : " volatile"); ++NumQuals; if (!Loc.isValid() || SM.isBeforeInTranslationUnit(VolatileQualLoc, Loc)) Loc = VolatileQualLoc; } - if (Quals & Qualifiers::Restrict) { + if (Quals & DeclSpec::TQ_restrict) { RestrictFixIt = FixItHint::CreateRemoval(RestrictQualLoc); QualStr += (NumQuals == 0 ? "restrict" : " restrict"); ++NumQuals; if (!Loc.isValid() || SM.isBeforeInTranslationUnit(RestrictQualLoc, Loc)) Loc = RestrictQualLoc; } + if (Quals & DeclSpec::TQ_atomic) { + AtomicFixIt = FixItHint::CreateRemoval(AtomicQualLoc); + QualStr += (NumQuals == 0 ? "_Atomic" : " _Atomic"); + ++NumQuals; + if (!Loc.isValid() || SM.isBeforeInTranslationUnit(AtomicQualLoc, Loc)) + Loc = AtomicQualLoc; + } assert(NumQuals > 0 && "No known qualifiers?"); S.Diag(Loc, diag::warn_qual_return_type) - << QualStr << NumQuals << ConstFixIt << VolatileFixIt << RestrictFixIt; + << QualStr << NumQuals + << ConstFixIt << VolatileFixIt << RestrictFixIt << AtomicFixIt; } static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, @@ -2496,16 +2543,18 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, SourceLocation::getFromRawEncoding(PTI.ConstQualLoc), SourceLocation::getFromRawEncoding(PTI.VolatileQualLoc), SourceLocation::getFromRawEncoding(PTI.RestrictQualLoc), + SourceLocation::getFromRawEncoding(PTI.AtomicQualLoc), S); } else if (T.getCVRQualifiers() && D.getDeclSpec().getTypeQualifiers() && - (!LangOpts.CPlusPlus || - (!T->isDependentType() && !T->isRecordType()))) { + (!LangOpts.CPlusPlus || + (!T->isDependentType() && !T->isRecordType()))) { DiagnoseIgnoredQualifiers(D.getDeclSpec().getTypeQualifiers(), D.getDeclSpec().getConstSpecLoc(), D.getDeclSpec().getVolatileSpecLoc(), D.getDeclSpec().getRestrictSpecLoc(), + D.getDeclSpec().getAtomicSpecLoc(), S); } @@ -3310,13 +3359,22 @@ namespace { TL.setNameLoc(DS.getTypeSpecTypeNameLoc()); } void VisitAtomicTypeLoc(AtomicTypeLoc TL) { - TL.setKWLoc(DS.getTypeSpecTypeLoc()); - TL.setParensRange(DS.getTypeofParensRange()); + // An AtomicTypeLoc can come from either an _Atomic(...) type specifier + // or an _Atomic qualifier. + if (DS.getTypeSpecType() == DeclSpec::TST_atomic) { + TL.setKWLoc(DS.getTypeSpecTypeLoc()); + TL.setParensRange(DS.getTypeofParensRange()); - TypeSourceInfo *TInfo = 0; - Sema::GetTypeFromParser(DS.getRepAsType(), &TInfo); - assert(TInfo); - TL.getValueLoc().initializeFullCopy(TInfo->getTypeLoc()); + TypeSourceInfo *TInfo = 0; + Sema::GetTypeFromParser(DS.getRepAsType(), &TInfo); + assert(TInfo); + TL.getValueLoc().initializeFullCopy(TInfo->getTypeLoc()); + } else { + TL.setKWLoc(DS.getAtomicSpecLoc()); + // No parens, to indicate this was spelled as an _Atomic qualifier. + TL.setParensRange(SourceRange()); + Visit(TL.getValueLoc()); + } } void VisitTypeLoc(TypeLoc TL) { @@ -3439,6 +3497,29 @@ namespace { }; } +static void fillAtomicQualLoc(AtomicTypeLoc ATL, const DeclaratorChunk &Chunk) { + SourceLocation Loc; + switch (Chunk.Kind) { + case DeclaratorChunk::Function: + case DeclaratorChunk::Array: + case DeclaratorChunk::Paren: + llvm_unreachable("cannot be _Atomic qualified"); + + case DeclaratorChunk::Pointer: + Loc = SourceLocation::getFromRawEncoding(Chunk.Ptr.AtomicQualLoc); + break; + + case DeclaratorChunk::BlockPointer: + case DeclaratorChunk::Reference: + case DeclaratorChunk::MemberPointer: + // FIXME: Provide a source location for the _Atomic keyword. + break; + } + + ATL.setKWLoc(Loc); + ATL.setParensRange(SourceRange()); +} + /// \brief Create and instantiate a TypeSourceInfo with type source information. /// /// \param T QualType referring to the type as written in source code. @@ -3460,6 +3541,14 @@ Sema::GetTypeSourceInfoForDeclarator(Declarator &D, QualType T, } for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) { + // An AtomicTypeLoc might be produced by an atomic qualifier in this + // declarator chunk. + // FIXME: Relative order of this and attributed type loc? + if (AtomicTypeLoc ATL = CurrTL.getAs()) { + fillAtomicQualLoc(ATL, D.getTypeObject(i)); + CurrTL = ATL.getValueLoc().getUnqualifiedLoc(); + } + while (AttributedTypeLoc TL = CurrTL.getAs()) { fillAttributedTypeLoc(TL, D.getTypeObject(i).getAttrs()); CurrTL = TL.getNextTypeLoc().getUnqualifiedLoc(); diff --git a/test/Parser/asm.c b/test/Parser/asm.c index 23052c389e..b95e08bcca 100644 --- a/test/Parser/asm.c +++ b/test/Parser/asm.c @@ -8,6 +8,12 @@ void f1() { void f2() { asm("foo" : "=r" (a)); // expected-error {{use of undeclared identifier 'a'}} asm("foo" : : "r" (b)); // expected-error {{use of undeclared identifier 'b'}} + + asm const (""); // expected-warning {{ignored const qualifier on asm}} + asm volatile (""); + asm restrict (""); // expected-warning {{ignored restrict qualifier on asm}} + // FIXME: Once GCC supports _Atomic, check whether it allows this. + asm _Atomic (""); // expected-warning {{ignored _Atomic qualifier on asm}} } diff --git a/test/Parser/atomic.c b/test/Parser/atomic.c new file mode 100644 index 0000000000..432deeb59c --- /dev/null +++ b/test/Parser/atomic.c @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -std=c11 %s -fsyntax-only -verify -pedantic + +typedef _Atomic(int) atomic_int; +typedef _Atomic int atomic_int; +typedef _Atomic _Atomic _Atomic(int) atomic_int; // expected-warning {{duplicate '_Atomic' declaration specifier}} + +typedef const int const_int; + +typedef const atomic_int const_atomic_int; +typedef _Atomic const int const_atomic_int; +typedef const _Atomic int const_atomic_int; +typedef const _Atomic(int) const_atomic_int; +typedef const _Atomic(_Atomic int) const_atomic_int; // expected-error {{_Atomic cannot be applied to atomic type '_Atomic(int)'}} +typedef _Atomic const_int const_atomic_int; +typedef _Atomic(const_int) const_atomic_int; // expected-error {{_Atomic cannot be applied to qualified type 'const_int' (aka 'const int')}} + +typedef int *_Atomic atomic_int_ptr; +typedef _Atomic(int *) atomic_int_ptr; +typedef int (*_Atomic atomic_int_ptr); + +typedef int _Atomic *int_atomic_ptr; +typedef _Atomic(int) *int_atomic_ptr; + +typedef int int_fn(); +typedef _Atomic int_fn atomic_int_fn; // expected-error {{_Atomic cannot be applied to function type 'int_fn' (aka 'int ()')}} +typedef _Atomic int atomic_int_array[3]; +typedef _Atomic atomic_int_array atomic_int_atomic_array; // expected-error {{_Atomic cannot be applied to array type 'atomic_int_array' (aka '_Atomic(int) [3]')}} + +_Atomic struct S { int n; }; // expected-warning {{'_Atomic' ignored on this declaration}} + +typedef _Atomic int __attribute__((address_space(1))) atomic_addr_space_int; +typedef _Atomic(int) __attribute__((address_space(1))) atomic_addr_space_int; + +typedef _Atomic int __attribute__((vector_size(16))) atomic_vector_int; +typedef _Atomic(int __attribute__((vector_size(16)))) atomic_vector_int; diff --git a/test/SemaCXX/atomic-type.cxx b/test/SemaCXX/atomic-type.cxx index 18707eb8c5..947bb3c5f4 100644 --- a/test/SemaCXX/atomic-type.cxx +++ b/test/SemaCXX/atomic-type.cxx @@ -1,7 +1,9 @@ -// RUN: %clang_cc1 -verify %s +// RUN: %clang_cc1 -verify -pedantic %s template struct atomic { _Atomic(T) value; + + void f() _Atomic; // expected-error {{expected ';' at end of declaration list}} }; template struct user { @@ -15,9 +17,11 @@ user u; struct A { }; int &ovl1(_Atomic(int)); +int &ovl1(_Atomic int); // ok, redeclaration long &ovl1(_Atomic(long)); float &ovl1(_Atomic(float)); double &ovl1(_Atomic(A const *const *)); +double &ovl1(A const *const *_Atomic); short &ovl1(_Atomic(A **)); void test_overloading(int i, float f, _Atomic(int) ai, _Atomic(float) af, @@ -33,3 +37,22 @@ void test_overloading(int i, float f, _Atomic(int) ai, _Atomic(float) af, double &dr2 = ovl1(ac); short &sr1 = ovl1(a); } + +typedef int (A::*fp)() _Atomic; // expected-error {{expected ';' after top level declarator}} expected-warning {{does not declare anything}} + +typedef _Atomic(int(A::*)) atomic_mem_ptr_to_int; +typedef int(A::*_Atomic atomic_mem_ptr_to_int); + +typedef _Atomic(int)(A::*mem_ptr_to_atomic_int); +typedef _Atomic int(A::*mem_ptr_to_atomic_int); + +typedef _Atomic(int)&atomic_int_ref; +typedef _Atomic int &atomic_int_ref; +typedef _Atomic atomic_int_ref atomic_int_ref; // ok, qualifiers on references ignored in this case. + +typedef int &_Atomic atomic_reference_to_int; // expected-error {{'_Atomic' qualifier may not be applied to a reference}} +typedef _Atomic(int &) atomic_reference_to_int; // expected-error {{_Atomic cannot be applied to reference type 'int &'}} + +struct S { + _Atomic union { int n; }; // expected-warning {{anonymous union cannot be '_Atomic'}} +}; -- 2.40.0