2006-11-08 14:54:53 +08:00
|
|
|
//===--- SemaDeclSpec.cpp - Declaration Specifier Semantic Analysis -------===//
|
2006-08-04 12:39:53 +08:00
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
2007-12-30 03:59:25 +08:00
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
2006-08-04 12:39:53 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2006-11-08 14:54:53 +08:00
|
|
|
// This file implements semantic analysis for declaration specifiers.
|
2006-08-04 12:39:53 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2010-08-21 02:27:03 +08:00
|
|
|
#include "clang/Parse/ParseDiagnostic.h" // FIXME: remove this back-dependency!
|
|
|
|
#include "clang/Sema/DeclSpec.h"
|
2011-06-25 08:56:27 +08:00
|
|
|
#include "clang/Sema/LocInfoType.h"
|
2010-08-21 02:27:03 +08:00
|
|
|
#include "clang/Sema/ParsedTemplate.h"
|
2011-10-18 07:06:20 +08:00
|
|
|
#include "clang/Sema/SemaDiagnostic.h"
|
2011-10-06 11:01:00 +08:00
|
|
|
#include "clang/Sema/Sema.h"
|
2011-02-25 01:54:50 +08:00
|
|
|
#include "clang/AST/ASTContext.h"
|
2011-06-25 08:56:27 +08:00
|
|
|
#include "clang/AST/Expr.h"
|
2011-02-24 08:17:56 +08:00
|
|
|
#include "clang/AST/NestedNameSpecifier.h"
|
|
|
|
#include "clang/AST/TypeLoc.h"
|
2009-04-02 06:41:11 +08:00
|
|
|
#include "clang/Lex/Preprocessor.h"
|
2006-08-04 13:25:55 +08:00
|
|
|
#include "clang/Basic/LangOptions.h"
|
2009-01-21 03:11:22 +08:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2009-08-04 02:47:27 +08:00
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
Introduce a representation for types that we referred to via a
qualified name, e.g.,
foo::x
so that we retain the nested-name-specifier as written in the source
code and can reproduce that qualified name when printing the types
back (e.g., in diagnostics). This is PR3493, which won't be complete
until finished the other tasks mentioned near the end of this commit.
The parser's representation of nested-name-specifiers, CXXScopeSpec,
is now a bit fatter, because it needs to contain the scopes that
precede each '::' and keep track of whether the global scoping
operator '::' was at the beginning. For example, we need to keep track
of the leading '::', 'foo', and 'bar' in
::foo::bar::x
The Action's CXXScopeTy * is no longer a DeclContext *. It's now the
opaque version of the new NestedNameSpecifier, which contains a single
component of a nested-name-specifier (either a DeclContext * or a Type
*, bitmangled).
The new sugar type QualifiedNameType composes a sequence of
NestedNameSpecifiers with a representation of the type we're actually
referring to. At present, we only build QualifiedNameType nodes within
Sema::getTypeName. This will be extended to other type-constructing
actions (e.g., ActOnClassTemplateId).
Also on the way: QualifiedDeclRefExprs will also store a sequence of
NestedNameSpecifiers, so that we can print out the property
nested-name-specifier. I expect to also use this for handling
dependent names like Fibonacci<I - 1>::value.
llvm-svn: 67265
2009-03-19 08:18:19 +08:00
|
|
|
#include <cstring>
|
2006-08-04 12:39:53 +08:00
|
|
|
using namespace clang;
|
|
|
|
|
2008-11-22 16:32:36 +08:00
|
|
|
|
2011-09-26 07:23:43 +08:00
|
|
|
static DiagnosticBuilder Diag(DiagnosticsEngine &D, SourceLocation Loc,
|
2010-11-19 04:06:41 +08:00
|
|
|
unsigned DiagID) {
|
|
|
|
return D.Report(Loc, DiagID);
|
2008-11-22 16:32:36 +08:00
|
|
|
}
|
|
|
|
|
2009-11-11 03:49:08 +08:00
|
|
|
|
|
|
|
void UnqualifiedId::setTemplateId(TemplateIdAnnotation *TemplateId) {
|
|
|
|
assert(TemplateId && "NULL template-id annotation?");
|
|
|
|
Kind = IK_TemplateId;
|
|
|
|
this->TemplateId = TemplateId;
|
|
|
|
StartLocation = TemplateId->TemplateNameLoc;
|
|
|
|
EndLocation = TemplateId->RAngleLoc;
|
|
|
|
}
|
|
|
|
|
2010-01-14 01:31:36 +08:00
|
|
|
void UnqualifiedId::setConstructorTemplateId(TemplateIdAnnotation *TemplateId) {
|
|
|
|
assert(TemplateId && "NULL template-id annotation?");
|
|
|
|
Kind = IK_ConstructorTemplateId;
|
|
|
|
this->TemplateId = TemplateId;
|
|
|
|
StartLocation = TemplateId->TemplateNameLoc;
|
|
|
|
EndLocation = TemplateId->RAngleLoc;
|
|
|
|
}
|
|
|
|
|
2011-02-24 08:17:56 +08:00
|
|
|
void CXXScopeSpec::Extend(ASTContext &Context, SourceLocation TemplateKWLoc,
|
|
|
|
TypeLoc TL, SourceLocation ColonColonLoc) {
|
2011-03-01 07:58:31 +08:00
|
|
|
Builder.Extend(Context, TemplateKWLoc, TL, ColonColonLoc);
|
2011-02-24 08:17:56 +08:00
|
|
|
if (Range.getBegin().isInvalid())
|
|
|
|
Range.setBegin(TL.getBeginLoc());
|
|
|
|
Range.setEnd(ColonColonLoc);
|
2011-02-25 01:54:50 +08:00
|
|
|
|
2011-03-01 07:58:31 +08:00
|
|
|
assert(Range == Builder.getSourceRange() &&
|
2011-02-25 01:54:50 +08:00
|
|
|
"NestedNameSpecifierLoc range computation incorrect");
|
2011-02-24 08:17:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void CXXScopeSpec::Extend(ASTContext &Context, IdentifierInfo *Identifier,
|
|
|
|
SourceLocation IdentifierLoc,
|
|
|
|
SourceLocation ColonColonLoc) {
|
2011-03-01 07:58:31 +08:00
|
|
|
Builder.Extend(Context, Identifier, IdentifierLoc, ColonColonLoc);
|
|
|
|
|
2011-02-24 08:17:56 +08:00
|
|
|
if (Range.getBegin().isInvalid())
|
|
|
|
Range.setBegin(IdentifierLoc);
|
|
|
|
Range.setEnd(ColonColonLoc);
|
2011-02-25 01:54:50 +08:00
|
|
|
|
2011-03-01 07:58:31 +08:00
|
|
|
assert(Range == Builder.getSourceRange() &&
|
2011-02-25 01:54:50 +08:00
|
|
|
"NestedNameSpecifierLoc range computation incorrect");
|
2011-02-24 08:17:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void CXXScopeSpec::Extend(ASTContext &Context, NamespaceDecl *Namespace,
|
|
|
|
SourceLocation NamespaceLoc,
|
|
|
|
SourceLocation ColonColonLoc) {
|
2011-03-01 07:58:31 +08:00
|
|
|
Builder.Extend(Context, Namespace, NamespaceLoc, ColonColonLoc);
|
|
|
|
|
2011-02-24 08:17:56 +08:00
|
|
|
if (Range.getBegin().isInvalid())
|
|
|
|
Range.setBegin(NamespaceLoc);
|
|
|
|
Range.setEnd(ColonColonLoc);
|
2011-02-25 01:54:50 +08:00
|
|
|
|
2011-03-01 07:58:31 +08:00
|
|
|
assert(Range == Builder.getSourceRange() &&
|
2011-02-25 01:54:50 +08:00
|
|
|
"NestedNameSpecifierLoc range computation incorrect");
|
2011-02-24 08:17:56 +08:00
|
|
|
}
|
|
|
|
|
2011-02-24 10:36:08 +08:00
|
|
|
void CXXScopeSpec::Extend(ASTContext &Context, NamespaceAliasDecl *Alias,
|
|
|
|
SourceLocation AliasLoc,
|
|
|
|
SourceLocation ColonColonLoc) {
|
2011-03-01 07:58:31 +08:00
|
|
|
Builder.Extend(Context, Alias, AliasLoc, ColonColonLoc);
|
|
|
|
|
2011-02-24 10:36:08 +08:00
|
|
|
if (Range.getBegin().isInvalid())
|
|
|
|
Range.setBegin(AliasLoc);
|
|
|
|
Range.setEnd(ColonColonLoc);
|
2011-02-25 01:54:50 +08:00
|
|
|
|
2011-03-01 07:58:31 +08:00
|
|
|
assert(Range == Builder.getSourceRange() &&
|
2011-02-25 01:54:50 +08:00
|
|
|
"NestedNameSpecifierLoc range computation incorrect");
|
2011-02-24 10:36:08 +08:00
|
|
|
}
|
|
|
|
|
2011-02-24 08:17:56 +08:00
|
|
|
void CXXScopeSpec::MakeGlobal(ASTContext &Context,
|
|
|
|
SourceLocation ColonColonLoc) {
|
2011-03-01 07:58:31 +08:00
|
|
|
Builder.MakeGlobal(Context, ColonColonLoc);
|
2011-02-25 01:54:50 +08:00
|
|
|
|
2011-03-01 07:58:31 +08:00
|
|
|
Range = SourceRange(ColonColonLoc);
|
2011-02-25 01:54:50 +08:00
|
|
|
|
2011-03-01 07:58:31 +08:00
|
|
|
assert(Range == Builder.getSourceRange() &&
|
2011-02-25 01:54:50 +08:00
|
|
|
"NestedNameSpecifierLoc range computation incorrect");
|
|
|
|
}
|
|
|
|
|
|
|
|
void CXXScopeSpec::MakeTrivial(ASTContext &Context,
|
|
|
|
NestedNameSpecifier *Qualifier, SourceRange R) {
|
2011-03-01 07:58:31 +08:00
|
|
|
Builder.MakeTrivial(Context, Qualifier, R);
|
2011-02-25 01:54:50 +08:00
|
|
|
Range = R;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CXXScopeSpec::Adopt(NestedNameSpecifierLoc Other) {
|
|
|
|
if (!Other) {
|
|
|
|
Range = SourceRange();
|
2011-03-01 07:58:31 +08:00
|
|
|
Builder.Clear();
|
2011-02-25 01:54:50 +08:00
|
|
|
return;
|
|
|
|
}
|
2011-03-01 07:58:31 +08:00
|
|
|
|
2011-02-25 01:54:50 +08:00
|
|
|
Range = Other.getSourceRange();
|
2011-03-01 07:58:31 +08:00
|
|
|
Builder.Adopt(Other);
|
2011-02-25 01:54:50 +08:00
|
|
|
}
|
|
|
|
|
2011-07-06 14:57:57 +08:00
|
|
|
SourceLocation CXXScopeSpec::getLastQualifierNameLoc() const {
|
|
|
|
if (!Builder.getRepresentation())
|
|
|
|
return SourceLocation();
|
|
|
|
return Builder.getTemporary().getLocalBeginLoc();
|
|
|
|
}
|
|
|
|
|
2011-02-25 10:25:35 +08:00
|
|
|
NestedNameSpecifierLoc
|
|
|
|
CXXScopeSpec::getWithLocInContext(ASTContext &Context) const {
|
2011-03-04 05:48:55 +08:00
|
|
|
if (!Builder.getRepresentation())
|
2011-02-25 01:54:50 +08:00
|
|
|
return NestedNameSpecifierLoc();
|
|
|
|
|
2011-03-01 07:58:31 +08:00
|
|
|
return Builder.getWithLocInContext(Context);
|
2011-02-24 08:17:56 +08:00
|
|
|
}
|
|
|
|
|
2009-01-21 03:11:22 +08:00
|
|
|
/// DeclaratorChunk::getFunction - Return a DeclaratorChunk for a function.
|
|
|
|
/// "TheDeclarator" is the declarator that this will be added to.
|
2011-03-24 19:26:52 +08:00
|
|
|
DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic,
|
2009-02-18 15:07:28 +08:00
|
|
|
SourceLocation EllipsisLoc,
|
2009-01-21 03:11:22 +08:00
|
|
|
ParamInfo *ArgInfo,
|
|
|
|
unsigned NumArgs,
|
|
|
|
unsigned TypeQuals,
|
2011-01-26 11:43:54 +08:00
|
|
|
bool RefQualifierIsLvalueRef,
|
|
|
|
SourceLocation RefQualifierLoc,
|
2011-10-19 14:04:55 +08:00
|
|
|
SourceLocation ConstQualifierLoc,
|
|
|
|
SourceLocation
|
|
|
|
VolatileQualifierLoc,
|
2011-07-14 05:47:47 +08:00
|
|
|
SourceLocation MutableLoc,
|
2011-03-06 06:42:13 +08:00
|
|
|
ExceptionSpecificationType
|
|
|
|
ESpecType,
|
|
|
|
SourceLocation ESpecLoc,
|
2010-08-24 13:47:05 +08:00
|
|
|
ParsedType *Exceptions,
|
2009-05-30 02:02:33 +08:00
|
|
|
SourceRange *ExceptionRanges,
|
2009-04-30 01:30:04 +08:00
|
|
|
unsigned NumExceptions,
|
2011-03-06 06:42:13 +08:00
|
|
|
Expr *NoexceptExpr,
|
2011-03-12 19:17:06 +08:00
|
|
|
SourceLocation LocalRangeBegin,
|
|
|
|
SourceLocation LocalRangeEnd,
|
2010-10-02 02:44:50 +08:00
|
|
|
Declarator &TheDeclarator,
|
2012-06-12 09:51:59 +08:00
|
|
|
TypeResult TrailingReturnType) {
|
2009-01-21 03:11:22 +08:00
|
|
|
DeclaratorChunk I;
|
2011-03-06 06:42:13 +08:00
|
|
|
I.Kind = Function;
|
2011-03-12 19:17:06 +08:00
|
|
|
I.Loc = LocalRangeBegin;
|
|
|
|
I.EndLoc = LocalRangeEnd;
|
2011-03-24 19:26:52 +08:00
|
|
|
I.Fun.AttrList = 0;
|
2011-03-06 06:42:13 +08:00
|
|
|
I.Fun.hasPrototype = hasProto;
|
|
|
|
I.Fun.isVariadic = isVariadic;
|
|
|
|
I.Fun.EllipsisLoc = EllipsisLoc.getRawEncoding();
|
|
|
|
I.Fun.DeleteArgInfo = false;
|
|
|
|
I.Fun.TypeQuals = TypeQuals;
|
|
|
|
I.Fun.NumArgs = NumArgs;
|
|
|
|
I.Fun.ArgInfo = 0;
|
2011-01-26 11:43:54 +08:00
|
|
|
I.Fun.RefQualifierIsLValueRef = RefQualifierIsLvalueRef;
|
2011-03-06 06:42:13 +08:00
|
|
|
I.Fun.RefQualifierLoc = RefQualifierLoc.getRawEncoding();
|
2011-10-19 14:04:55 +08:00
|
|
|
I.Fun.ConstQualifierLoc = ConstQualifierLoc.getRawEncoding();
|
|
|
|
I.Fun.VolatileQualifierLoc = VolatileQualifierLoc.getRawEncoding();
|
2011-07-14 05:47:47 +08:00
|
|
|
I.Fun.MutableLoc = MutableLoc.getRawEncoding();
|
2011-03-06 06:42:13 +08:00
|
|
|
I.Fun.ExceptionSpecType = ESpecType;
|
|
|
|
I.Fun.ExceptionSpecLoc = ESpecLoc.getRawEncoding();
|
|
|
|
I.Fun.NumExceptions = 0;
|
|
|
|
I.Fun.Exceptions = 0;
|
|
|
|
I.Fun.NoexceptExpr = 0;
|
2012-06-12 09:51:59 +08:00
|
|
|
I.Fun.HasTrailingReturnType = TrailingReturnType.isUsable() ||
|
|
|
|
TrailingReturnType.isInvalid();
|
|
|
|
I.Fun.TrailingReturnType = TrailingReturnType.get();
|
2009-04-30 01:30:04 +08:00
|
|
|
|
2009-01-21 03:11:22 +08:00
|
|
|
// new[] an argument array if needed.
|
|
|
|
if (NumArgs) {
|
|
|
|
// If the 'InlineParams' in Declarator is unused and big enough, put our
|
|
|
|
// parameter list there (in an effort to avoid new/delete traffic). If it
|
|
|
|
// is already used (consider a function returning a function pointer) or too
|
|
|
|
// small (function taking too many arguments), go to the heap.
|
2009-09-09 23:08:12 +08:00
|
|
|
if (!TheDeclarator.InlineParamsUsed &&
|
2009-01-21 03:11:22 +08:00
|
|
|
NumArgs <= llvm::array_lengthof(TheDeclarator.InlineParams)) {
|
|
|
|
I.Fun.ArgInfo = TheDeclarator.InlineParams;
|
|
|
|
I.Fun.DeleteArgInfo = false;
|
|
|
|
TheDeclarator.InlineParamsUsed = true;
|
|
|
|
} else {
|
|
|
|
I.Fun.ArgInfo = new DeclaratorChunk::ParamInfo[NumArgs];
|
|
|
|
I.Fun.DeleteArgInfo = true;
|
|
|
|
}
|
|
|
|
memcpy(I.Fun.ArgInfo, ArgInfo, sizeof(ArgInfo[0])*NumArgs);
|
|
|
|
}
|
2011-03-06 06:42:13 +08:00
|
|
|
|
|
|
|
// Check what exception specification information we should actually store.
|
|
|
|
switch (ESpecType) {
|
|
|
|
default: break; // By default, save nothing.
|
|
|
|
case EST_Dynamic:
|
|
|
|
// new[] an exception array if needed
|
|
|
|
if (NumExceptions) {
|
|
|
|
I.Fun.NumExceptions = NumExceptions;
|
|
|
|
I.Fun.Exceptions = new DeclaratorChunk::TypeAndRange[NumExceptions];
|
|
|
|
for (unsigned i = 0; i != NumExceptions; ++i) {
|
|
|
|
I.Fun.Exceptions[i].Ty = Exceptions[i];
|
|
|
|
I.Fun.Exceptions[i].Range = ExceptionRanges[i];
|
|
|
|
}
|
2009-05-30 02:02:33 +08:00
|
|
|
}
|
2011-03-06 06:42:13 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case EST_ComputedNoexcept:
|
|
|
|
I.Fun.NoexceptExpr = NoexceptExpr;
|
|
|
|
break;
|
2009-04-30 01:30:04 +08:00
|
|
|
}
|
2009-01-21 03:11:22 +08:00
|
|
|
return I;
|
|
|
|
}
|
2008-11-22 16:32:36 +08:00
|
|
|
|
2011-06-25 08:56:27 +08:00
|
|
|
bool Declarator::isDeclarationOfFunction() const {
|
2011-06-25 10:28:38 +08:00
|
|
|
for (unsigned i = 0, i_end = DeclTypeInfo.size(); i < i_end; ++i) {
|
|
|
|
switch (DeclTypeInfo[i].Kind) {
|
|
|
|
case DeclaratorChunk::Function:
|
|
|
|
return true;
|
|
|
|
case DeclaratorChunk::Paren:
|
|
|
|
continue;
|
|
|
|
case DeclaratorChunk::Pointer:
|
|
|
|
case DeclaratorChunk::Reference:
|
|
|
|
case DeclaratorChunk::Array:
|
|
|
|
case DeclaratorChunk::BlockPointer:
|
|
|
|
case DeclaratorChunk::MemberPointer:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
llvm_unreachable("Invalid type chunk");
|
|
|
|
}
|
2011-06-25 08:56:27 +08:00
|
|
|
|
|
|
|
switch (DS.getTypeSpecType()) {
|
2011-10-07 07:00:33 +08:00
|
|
|
case TST_atomic:
|
2011-06-25 08:56:27 +08:00
|
|
|
case TST_auto:
|
|
|
|
case TST_bool:
|
|
|
|
case TST_char:
|
|
|
|
case TST_char16:
|
|
|
|
case TST_char32:
|
|
|
|
case TST_class:
|
|
|
|
case TST_decimal128:
|
|
|
|
case TST_decimal32:
|
|
|
|
case TST_decimal64:
|
|
|
|
case TST_double:
|
|
|
|
case TST_enum:
|
|
|
|
case TST_error:
|
|
|
|
case TST_float:
|
2011-10-15 07:23:15 +08:00
|
|
|
case TST_half:
|
2011-06-25 08:56:27 +08:00
|
|
|
case TST_int:
|
2012-04-04 14:24:32 +08:00
|
|
|
case TST_int128:
|
2011-06-25 08:56:27 +08:00
|
|
|
case TST_struct:
|
|
|
|
case TST_union:
|
|
|
|
case TST_unknown_anytype:
|
|
|
|
case TST_unspecified:
|
|
|
|
case TST_void:
|
|
|
|
case TST_wchar:
|
|
|
|
return false;
|
|
|
|
|
|
|
|
case TST_decltype:
|
|
|
|
case TST_typeofExpr:
|
|
|
|
if (Expr *E = DS.getRepAsExpr())
|
|
|
|
return E->getType()->isFunctionType();
|
|
|
|
return false;
|
|
|
|
|
|
|
|
case TST_underlyingType:
|
|
|
|
case TST_typename:
|
|
|
|
case TST_typeofType: {
|
|
|
|
QualType QT = DS.getRepAsType().get();
|
|
|
|
if (QT.isNull())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (const LocInfoType *LIT = dyn_cast<LocInfoType>(QT))
|
|
|
|
QT = LIT->getType();
|
|
|
|
|
|
|
|
if (QT.isNull())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return QT->isFunctionType();
|
|
|
|
}
|
|
|
|
}
|
2012-01-17 14:56:22 +08:00
|
|
|
|
|
|
|
llvm_unreachable("Invalid TypeSpecType!");
|
2011-06-25 08:56:27 +08:00
|
|
|
}
|
|
|
|
|
2006-08-04 12:39:53 +08:00
|
|
|
/// getParsedSpecifiers - Return a bitmask of which flavors of specifiers this
|
2009-02-28 02:35:46 +08:00
|
|
|
/// declaration specifier includes.
|
2006-08-04 12:39:53 +08:00
|
|
|
///
|
|
|
|
unsigned DeclSpec::getParsedSpecifiers() const {
|
|
|
|
unsigned Res = 0;
|
2006-08-05 11:28:50 +08:00
|
|
|
if (StorageClassSpec != SCS_unspecified ||
|
|
|
|
SCS_thread_specified)
|
2006-08-04 12:39:53 +08:00
|
|
|
Res |= PQ_StorageClassSpecifier;
|
2008-06-20 03:52:46 +08:00
|
|
|
|
2006-11-28 13:05:08 +08:00
|
|
|
if (TypeQualifiers != TQ_unspecified)
|
2006-08-04 12:39:53 +08:00
|
|
|
Res |= PQ_TypeQualifier;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2006-11-28 12:28:12 +08:00
|
|
|
if (hasTypeSpecifier())
|
2006-08-04 12:39:53 +08:00
|
|
|
Res |= PQ_TypeSpecifier;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-10-31 17:07:45 +08:00
|
|
|
if (FS_inline_specified || FS_virtual_specified || FS_explicit_specified)
|
2006-08-04 12:39:53 +08:00
|
|
|
Res |= PQ_FunctionSpecifier;
|
|
|
|
return Res;
|
|
|
|
}
|
|
|
|
|
2009-08-04 04:12:06 +08:00
|
|
|
template <class T> static bool BadSpecifier(T TNew, T TPrev,
|
|
|
|
const char *&PrevSpec,
|
|
|
|
unsigned &DiagID) {
|
2009-08-04 02:47:27 +08:00
|
|
|
PrevSpec = DeclSpec::getSpecifierName(TPrev);
|
2009-08-04 04:12:06 +08:00
|
|
|
DiagID = (TNew == TPrev ? diag::ext_duplicate_declspec
|
|
|
|
: diag::err_invalid_decl_spec_combination);
|
2009-08-04 02:47:27 +08:00
|
|
|
return true;
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-08-04 02:47:27 +08:00
|
|
|
|
2007-01-23 12:35:33 +08:00
|
|
|
const char *DeclSpec::getSpecifierName(DeclSpec::SCS S) {
|
2006-08-05 11:28:50 +08:00
|
|
|
switch (S) {
|
|
|
|
case DeclSpec::SCS_unspecified: return "unspecified";
|
|
|
|
case DeclSpec::SCS_typedef: return "typedef";
|
|
|
|
case DeclSpec::SCS_extern: return "extern";
|
|
|
|
case DeclSpec::SCS_static: return "static";
|
|
|
|
case DeclSpec::SCS_auto: return "auto";
|
|
|
|
case DeclSpec::SCS_register: return "register";
|
2009-04-20 04:27:55 +08:00
|
|
|
case DeclSpec::SCS_private_extern: return "__private_extern__";
|
2008-11-15 07:42:31 +08:00
|
|
|
case DeclSpec::SCS_mutable: return "mutable";
|
2006-08-05 11:28:50 +08:00
|
|
|
}
|
2009-12-12 13:05:38 +08:00
|
|
|
llvm_unreachable("Unknown typespec!");
|
2006-08-05 11:28:50 +08:00
|
|
|
}
|
|
|
|
|
2009-08-04 02:47:27 +08:00
|
|
|
const char *DeclSpec::getSpecifierName(TSW W) {
|
2006-08-04 12:39:53 +08:00
|
|
|
switch (W) {
|
2009-08-04 02:47:27 +08:00
|
|
|
case TSW_unspecified: return "unspecified";
|
|
|
|
case TSW_short: return "short";
|
|
|
|
case TSW_long: return "long";
|
|
|
|
case TSW_longlong: return "long long";
|
2006-08-04 12:39:53 +08:00
|
|
|
}
|
2009-12-12 13:05:38 +08:00
|
|
|
llvm_unreachable("Unknown typespec!");
|
2006-08-04 12:39:53 +08:00
|
|
|
}
|
|
|
|
|
2009-08-04 02:47:27 +08:00
|
|
|
const char *DeclSpec::getSpecifierName(TSC C) {
|
2006-08-04 12:39:53 +08:00
|
|
|
switch (C) {
|
2009-08-04 02:47:27 +08:00
|
|
|
case TSC_unspecified: return "unspecified";
|
|
|
|
case TSC_imaginary: return "imaginary";
|
|
|
|
case TSC_complex: return "complex";
|
2006-08-04 12:39:53 +08:00
|
|
|
}
|
2009-12-12 13:05:38 +08:00
|
|
|
llvm_unreachable("Unknown typespec!");
|
2006-08-04 12:39:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-04 02:47:27 +08:00
|
|
|
const char *DeclSpec::getSpecifierName(TSS S) {
|
2006-08-04 12:39:53 +08:00
|
|
|
switch (S) {
|
2009-08-04 02:47:27 +08:00
|
|
|
case TSS_unspecified: return "unspecified";
|
|
|
|
case TSS_signed: return "signed";
|
|
|
|
case TSS_unsigned: return "unsigned";
|
2006-08-04 12:39:53 +08:00
|
|
|
}
|
2009-12-12 13:05:38 +08:00
|
|
|
llvm_unreachable("Unknown typespec!");
|
2006-08-04 12:39:53 +08:00
|
|
|
}
|
|
|
|
|
2007-01-23 12:34:43 +08:00
|
|
|
const char *DeclSpec::getSpecifierName(DeclSpec::TST T) {
|
2006-08-04 12:39:53 +08:00
|
|
|
switch (T) {
|
2006-08-04 14:15:52 +08:00
|
|
|
case DeclSpec::TST_unspecified: return "unspecified";
|
|
|
|
case DeclSpec::TST_void: return "void";
|
|
|
|
case DeclSpec::TST_char: return "char";
|
2008-08-10 00:51:54 +08:00
|
|
|
case DeclSpec::TST_wchar: return "wchar_t";
|
2009-07-14 14:30:34 +08:00
|
|
|
case DeclSpec::TST_char16: return "char16_t";
|
|
|
|
case DeclSpec::TST_char32: return "char32_t";
|
2006-08-04 14:15:52 +08:00
|
|
|
case DeclSpec::TST_int: return "int";
|
2012-04-04 14:24:32 +08:00
|
|
|
case DeclSpec::TST_int128: return "__int128";
|
2011-10-15 07:23:15 +08:00
|
|
|
case DeclSpec::TST_half: return "half";
|
2006-08-04 14:15:52 +08:00
|
|
|
case DeclSpec::TST_float: return "float";
|
|
|
|
case DeclSpec::TST_double: return "double";
|
|
|
|
case DeclSpec::TST_bool: return "_Bool";
|
|
|
|
case DeclSpec::TST_decimal32: return "_Decimal32";
|
|
|
|
case DeclSpec::TST_decimal64: return "_Decimal64";
|
|
|
|
case DeclSpec::TST_decimal128: return "_Decimal128";
|
2006-08-14 06:16:42 +08:00
|
|
|
case DeclSpec::TST_enum: return "enum";
|
2008-04-14 02:59:07 +08:00
|
|
|
case DeclSpec::TST_class: return "class";
|
2006-08-14 06:16:42 +08:00
|
|
|
case DeclSpec::TST_union: return "union";
|
|
|
|
case DeclSpec::TST_struct: return "struct";
|
2009-02-09 23:09:02 +08:00
|
|
|
case DeclSpec::TST_typename: return "type-name";
|
2007-07-31 20:34:36 +08:00
|
|
|
case DeclSpec::TST_typeofType:
|
|
|
|
case DeclSpec::TST_typeofExpr: return "typeof";
|
2009-08-04 02:47:27 +08:00
|
|
|
case DeclSpec::TST_auto: return "auto";
|
|
|
|
case DeclSpec::TST_decltype: return "(decltype)";
|
2011-05-25 06:41:36 +08:00
|
|
|
case DeclSpec::TST_underlyingType: return "__underlying_type";
|
2011-04-10 06:50:59 +08:00
|
|
|
case DeclSpec::TST_unknown_anytype: return "__unknown_anytype";
|
2011-10-07 07:00:33 +08:00
|
|
|
case DeclSpec::TST_atomic: return "_Atomic";
|
2009-08-04 02:47:27 +08:00
|
|
|
case DeclSpec::TST_error: return "(error)";
|
2006-08-04 12:39:53 +08:00
|
|
|
}
|
2009-12-12 13:05:38 +08:00
|
|
|
llvm_unreachable("Unknown typespec!");
|
2006-08-04 14:15:52 +08:00
|
|
|
}
|
|
|
|
|
2009-08-04 02:47:27 +08:00
|
|
|
const char *DeclSpec::getSpecifierName(TQ T) {
|
2006-08-04 13:25:55 +08:00
|
|
|
switch (T) {
|
2009-08-04 02:47:27 +08:00
|
|
|
case DeclSpec::TQ_unspecified: return "unspecified";
|
|
|
|
case DeclSpec::TQ_const: return "const";
|
|
|
|
case DeclSpec::TQ_restrict: return "restrict";
|
|
|
|
case DeclSpec::TQ_volatile: return "volatile";
|
2006-08-04 13:25:55 +08:00
|
|
|
}
|
2009-12-12 13:05:38 +08:00
|
|
|
llvm_unreachable("Unknown typespec!");
|
2006-08-04 13:25:55 +08:00
|
|
|
}
|
2006-08-04 12:39:53 +08:00
|
|
|
|
2011-10-06 11:01:00 +08:00
|
|
|
bool DeclSpec::SetStorageClassSpec(Sema &S, SCS SC, SourceLocation Loc,
|
2009-08-04 04:12:06 +08:00
|
|
|
const char *&PrevSpec,
|
2011-10-06 11:01:00 +08:00
|
|
|
unsigned &DiagID) {
|
|
|
|
// OpenCL 1.1 6.8g: "The extern, static, auto and register storage-class
|
|
|
|
// specifiers are not supported."
|
2011-02-12 03:59:54 +08:00
|
|
|
// It seems sensible to prohibit private_extern too
|
2011-10-06 11:01:00 +08:00
|
|
|
// The cl_clang_storage_class_specifiers extension enables support for
|
|
|
|
// these storage-class specifiers.
|
2012-03-11 15:00:24 +08:00
|
|
|
if (S.getLangOpts().OpenCL &&
|
2011-10-06 11:01:00 +08:00
|
|
|
!S.getOpenCLOptions().cl_clang_storage_class_specifiers) {
|
|
|
|
switch (SC) {
|
2011-02-12 03:59:54 +08:00
|
|
|
case SCS_extern:
|
|
|
|
case SCS_private_extern:
|
|
|
|
case SCS_auto:
|
|
|
|
case SCS_register:
|
|
|
|
case SCS_static:
|
|
|
|
DiagID = diag::err_not_opencl_storage_class_specifier;
|
2011-10-06 11:01:00 +08:00
|
|
|
PrevSpec = getSpecifierName(SC);
|
2011-02-12 03:59:54 +08:00
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-31 00:47:02 +08:00
|
|
|
if (StorageClassSpec != SCS_unspecified) {
|
2011-09-05 03:54:14 +08:00
|
|
|
// Maybe this is an attempt to use C++0x 'auto' outside of C++0x mode.
|
|
|
|
bool isInvalid = true;
|
2012-03-11 15:00:24 +08:00
|
|
|
if (TypeSpecType == TST_unspecified && S.getLangOpts().CPlusPlus) {
|
2011-10-06 11:01:00 +08:00
|
|
|
if (SC == SCS_auto)
|
2011-09-05 03:54:14 +08:00
|
|
|
return SetTypeSpecType(TST_auto, Loc, PrevSpec, DiagID);
|
|
|
|
if (StorageClassSpec == SCS_auto) {
|
|
|
|
isInvalid = SetTypeSpecType(TST_auto, StorageClassSpecLoc,
|
|
|
|
PrevSpec, DiagID);
|
|
|
|
assert(!isInvalid && "auto SCS -> TST recovery failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-31 00:47:02 +08:00
|
|
|
// Changing storage class is allowed only if the previous one
|
|
|
|
// was the 'extern' that is part of a linkage specification and
|
|
|
|
// the new storage class is 'typedef'.
|
2011-09-05 03:54:14 +08:00
|
|
|
if (isInvalid &&
|
|
|
|
!(SCS_extern_in_linkage_spec &&
|
2010-07-31 00:47:02 +08:00
|
|
|
StorageClassSpec == SCS_extern &&
|
2011-10-06 11:01:00 +08:00
|
|
|
SC == SCS_typedef))
|
|
|
|
return BadSpecifier(SC, (SCS)StorageClassSpec, PrevSpec, DiagID);
|
2010-07-31 00:47:02 +08:00
|
|
|
}
|
2011-10-06 11:01:00 +08:00
|
|
|
StorageClassSpec = SC;
|
2006-11-28 13:05:08 +08:00
|
|
|
StorageClassSpecLoc = Loc;
|
2011-10-06 11:01:00 +08:00
|
|
|
assert((unsigned)SC == StorageClassSpec && "SCS constants overflow bitfield");
|
2006-08-05 11:28:50 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
bool DeclSpec::SetStorageClassSpecThread(SourceLocation Loc,
|
2009-08-04 04:12:06 +08:00
|
|
|
const char *&PrevSpec,
|
|
|
|
unsigned &DiagID) {
|
2006-11-28 12:50:12 +08:00
|
|
|
if (SCS_thread_specified) {
|
|
|
|
PrevSpec = "__thread";
|
2009-08-04 04:12:06 +08:00
|
|
|
DiagID = diag::ext_duplicate_declspec;
|
2006-11-28 12:50:12 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
SCS_thread_specified = true;
|
2006-11-28 13:05:08 +08:00
|
|
|
SCS_threadLoc = Loc;
|
2006-11-28 12:50:12 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2006-08-04 12:39:53 +08:00
|
|
|
/// These methods set the specified attribute of the DeclSpec, but return true
|
|
|
|
/// and ignore the request if invalid (e.g. "extern" then "auto" is
|
|
|
|
/// specified).
|
2006-11-28 13:30:29 +08:00
|
|
|
bool DeclSpec::SetTypeSpecWidth(TSW W, SourceLocation Loc,
|
2009-08-04 04:12:06 +08:00
|
|
|
const char *&PrevSpec,
|
|
|
|
unsigned &DiagID) {
|
2011-03-07 06:21:56 +08:00
|
|
|
// Overwrite TSWLoc only if TypeSpecWidth was unspecified, so that
|
|
|
|
// for 'long long' we will keep the source location of the first 'long'.
|
|
|
|
if (TypeSpecWidth == TSW_unspecified)
|
|
|
|
TSWLoc = Loc;
|
|
|
|
// Allow turning long -> long long.
|
|
|
|
else if (W != TSW_longlong || TypeSpecWidth != TSW_long)
|
2009-08-04 04:12:06 +08:00
|
|
|
return BadSpecifier(W, (TSW)TypeSpecWidth, PrevSpec, DiagID);
|
2006-08-04 12:39:53 +08:00
|
|
|
TypeSpecWidth = W;
|
2010-06-23 14:00:24 +08:00
|
|
|
if (TypeAltiVecVector && !TypeAltiVecBool &&
|
|
|
|
((TypeSpecWidth == TSW_long) || (TypeSpecWidth == TSW_longlong))) {
|
2010-02-05 08:12:22 +08:00
|
|
|
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType);
|
|
|
|
DiagID = diag::warn_vector_long_decl_spec_combination;
|
|
|
|
return true;
|
|
|
|
}
|
2006-08-04 12:39:53 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
bool DeclSpec::SetTypeSpecComplex(TSC C, SourceLocation Loc,
|
2009-08-04 04:12:06 +08:00
|
|
|
const char *&PrevSpec,
|
|
|
|
unsigned &DiagID) {
|
2006-08-04 12:39:53 +08:00
|
|
|
if (TypeSpecComplex != TSC_unspecified)
|
2009-08-04 04:12:06 +08:00
|
|
|
return BadSpecifier(C, (TSC)TypeSpecComplex, PrevSpec, DiagID);
|
2006-08-04 12:39:53 +08:00
|
|
|
TypeSpecComplex = C;
|
2006-11-28 13:30:29 +08:00
|
|
|
TSCLoc = Loc;
|
2006-08-04 12:39:53 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-09-09 23:08:12 +08:00
|
|
|
bool DeclSpec::SetTypeSpecSign(TSS S, SourceLocation Loc,
|
2009-08-04 04:12:06 +08:00
|
|
|
const char *&PrevSpec,
|
|
|
|
unsigned &DiagID) {
|
2006-08-04 12:39:53 +08:00
|
|
|
if (TypeSpecSign != TSS_unspecified)
|
2009-08-04 04:12:06 +08:00
|
|
|
return BadSpecifier(S, (TSS)TypeSpecSign, PrevSpec, DiagID);
|
2006-08-04 13:26:52 +08:00
|
|
|
TypeSpecSign = S;
|
2006-11-28 13:30:29 +08:00
|
|
|
TSSLoc = Loc;
|
2006-08-04 12:39:53 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2006-11-28 13:30:29 +08:00
|
|
|
bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc,
|
2009-08-04 04:12:06 +08:00
|
|
|
const char *&PrevSpec,
|
|
|
|
unsigned &DiagID,
|
2010-08-24 13:47:05 +08:00
|
|
|
ParsedType Rep) {
|
2011-03-17 04:16:18 +08:00
|
|
|
return SetTypeSpecType(T, Loc, Loc, PrevSpec, DiagID, Rep);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DeclSpec::SetTypeSpecType(TST T, SourceLocation TagKwLoc,
|
|
|
|
SourceLocation TagNameLoc,
|
|
|
|
const char *&PrevSpec,
|
|
|
|
unsigned &DiagID,
|
|
|
|
ParsedType Rep) {
|
2010-08-24 13:47:05 +08:00
|
|
|
assert(isTypeRep(T) && "T does not store a type");
|
|
|
|
assert(Rep && "no type provided!");
|
|
|
|
if (TypeSpecType != TST_unspecified) {
|
|
|
|
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType);
|
|
|
|
DiagID = diag::err_invalid_decl_spec_combination;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
TypeSpecType = T;
|
|
|
|
TypeRep = Rep;
|
2011-03-17 04:16:18 +08:00
|
|
|
TSTLoc = TagKwLoc;
|
|
|
|
TSTNameLoc = TagNameLoc;
|
2010-08-24 13:47:05 +08:00
|
|
|
TypeSpecOwned = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc,
|
|
|
|
const char *&PrevSpec,
|
|
|
|
unsigned &DiagID,
|
|
|
|
Expr *Rep) {
|
|
|
|
assert(isExprRep(T) && "T does not store an expr");
|
|
|
|
assert(Rep && "no expression provided!");
|
|
|
|
if (TypeSpecType != TST_unspecified) {
|
|
|
|
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType);
|
|
|
|
DiagID = diag::err_invalid_decl_spec_combination;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
TypeSpecType = T;
|
|
|
|
ExprRep = Rep;
|
|
|
|
TSTLoc = Loc;
|
2011-03-17 04:16:18 +08:00
|
|
|
TSTNameLoc = Loc;
|
2010-08-24 13:47:05 +08:00
|
|
|
TypeSpecOwned = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc,
|
|
|
|
const char *&PrevSpec,
|
|
|
|
unsigned &DiagID,
|
|
|
|
Decl *Rep, bool Owned) {
|
2011-03-17 04:16:18 +08:00
|
|
|
return SetTypeSpecType(T, Loc, Loc, PrevSpec, DiagID, Rep, Owned);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DeclSpec::SetTypeSpecType(TST T, SourceLocation TagKwLoc,
|
|
|
|
SourceLocation TagNameLoc,
|
|
|
|
const char *&PrevSpec,
|
|
|
|
unsigned &DiagID,
|
|
|
|
Decl *Rep, bool Owned) {
|
2010-08-24 13:47:05 +08:00
|
|
|
assert(isDeclRep(T) && "T does not store a decl");
|
|
|
|
// Unlike the other cases, we don't assert that we actually get a decl.
|
|
|
|
|
|
|
|
if (TypeSpecType != TST_unspecified) {
|
|
|
|
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType);
|
|
|
|
DiagID = diag::err_invalid_decl_spec_combination;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
TypeSpecType = T;
|
|
|
|
DeclRep = Rep;
|
2011-03-17 04:16:18 +08:00
|
|
|
TSTLoc = TagKwLoc;
|
|
|
|
TSTNameLoc = TagNameLoc;
|
2010-08-24 13:47:05 +08:00
|
|
|
TypeSpecOwned = Owned;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc,
|
|
|
|
const char *&PrevSpec,
|
|
|
|
unsigned &DiagID) {
|
|
|
|
assert(!isDeclRep(T) && !isTypeRep(T) && !isExprRep(T) &&
|
|
|
|
"rep required for these type-spec kinds!");
|
2009-08-04 04:12:06 +08:00
|
|
|
if (TypeSpecType != TST_unspecified) {
|
|
|
|
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType);
|
|
|
|
DiagID = diag::err_invalid_decl_spec_combination;
|
|
|
|
return true;
|
|
|
|
}
|
2011-03-17 04:16:18 +08:00
|
|
|
TSTLoc = Loc;
|
|
|
|
TSTNameLoc = Loc;
|
2010-06-23 14:00:24 +08:00
|
|
|
if (TypeAltiVecVector && (T == TST_bool) && !TypeAltiVecBool) {
|
|
|
|
TypeAltiVecBool = true;
|
|
|
|
return false;
|
|
|
|
}
|
2006-08-04 13:26:52 +08:00
|
|
|
TypeSpecType = T;
|
2010-08-24 13:47:05 +08:00
|
|
|
TypeSpecOwned = false;
|
2010-06-23 14:00:24 +08:00
|
|
|
if (TypeAltiVecVector && !TypeAltiVecBool && (TypeSpecType == TST_double)) {
|
2010-02-05 08:12:22 +08:00
|
|
|
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType);
|
2010-06-23 14:00:24 +08:00
|
|
|
DiagID = diag::err_invalid_vector_decl_spec;
|
2010-02-05 08:12:22 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DeclSpec::SetTypeAltiVecVector(bool isAltiVecVector, SourceLocation Loc,
|
|
|
|
const char *&PrevSpec, unsigned &DiagID) {
|
|
|
|
if (TypeSpecType != TST_unspecified) {
|
|
|
|
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType);
|
|
|
|
DiagID = diag::err_invalid_vector_decl_spec_combination;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
TypeAltiVecVector = isAltiVecVector;
|
|
|
|
AltiVecLoc = Loc;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DeclSpec::SetTypeAltiVecPixel(bool isAltiVecPixel, SourceLocation Loc,
|
|
|
|
const char *&PrevSpec, unsigned &DiagID) {
|
2010-06-23 14:00:24 +08:00
|
|
|
if (!TypeAltiVecVector || TypeAltiVecPixel ||
|
|
|
|
(TypeSpecType != TST_unspecified)) {
|
2010-02-05 08:12:22 +08:00
|
|
|
PrevSpec = DeclSpec::getSpecifierName((TST) TypeSpecType);
|
|
|
|
DiagID = diag::err_invalid_pixel_decl_spec_combination;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
TypeAltiVecPixel = isAltiVecPixel;
|
|
|
|
TSTLoc = Loc;
|
2011-03-17 04:16:18 +08:00
|
|
|
TSTNameLoc = Loc;
|
2006-08-04 12:39:53 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-02-07 06:42:48 +08:00
|
|
|
bool DeclSpec::SetTypeSpecError() {
|
|
|
|
TypeSpecType = TST_error;
|
2010-08-27 01:22:34 +08:00
|
|
|
TypeSpecOwned = false;
|
2009-02-07 06:42:48 +08:00
|
|
|
TSTLoc = SourceLocation();
|
2011-03-17 04:16:18 +08:00
|
|
|
TSTNameLoc = SourceLocation();
|
2009-02-07 06:42:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2006-11-28 13:18:46 +08:00
|
|
|
bool DeclSpec::SetTypeQual(TQ T, SourceLocation Loc, const char *&PrevSpec,
|
2009-08-04 04:12:06 +08:00
|
|
|
unsigned &DiagID, const LangOptions &Lang) {
|
2006-08-04 13:25:55 +08:00
|
|
|
// Duplicates turn into warnings pre-C99.
|
|
|
|
if ((TypeQualifiers & T) && !Lang.C99)
|
2009-08-04 04:12:06 +08:00
|
|
|
return BadSpecifier(T, T, PrevSpec, DiagID);
|
2006-08-04 13:25:55 +08:00
|
|
|
TypeQualifiers |= T;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2006-11-28 13:18:46 +08:00
|
|
|
switch (T) {
|
2011-09-23 13:06:16 +08:00
|
|
|
default: llvm_unreachable("Unknown type qualifier!");
|
2006-11-28 13:18:46 +08:00
|
|
|
case TQ_const: TQ_constLoc = Loc; break;
|
|
|
|
case TQ_restrict: TQ_restrictLoc = Loc; break;
|
|
|
|
case TQ_volatile: TQ_volatileLoc = Loc; break;
|
|
|
|
}
|
2006-08-04 13:25:55 +08:00
|
|
|
return false;
|
|
|
|
}
|
2006-08-04 12:39:53 +08:00
|
|
|
|
2009-08-04 04:12:06 +08:00
|
|
|
bool DeclSpec::SetFunctionSpecInline(SourceLocation Loc, const char *&PrevSpec,
|
|
|
|
unsigned &DiagID) {
|
2006-11-28 12:33:46 +08:00
|
|
|
// 'inline inline' is ok.
|
|
|
|
FS_inline_specified = true;
|
2006-11-28 13:12:07 +08:00
|
|
|
FS_inlineLoc = Loc;
|
2006-11-28 12:33:46 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-08-04 04:12:06 +08:00
|
|
|
bool DeclSpec::SetFunctionSpecVirtual(SourceLocation Loc, const char *&PrevSpec,
|
|
|
|
unsigned &DiagID) {
|
2008-10-31 17:07:45 +08:00
|
|
|
// 'virtual virtual' is ok.
|
|
|
|
FS_virtual_specified = true;
|
|
|
|
FS_virtualLoc = Loc;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-08-04 04:12:06 +08:00
|
|
|
bool DeclSpec::SetFunctionSpecExplicit(SourceLocation Loc, const char *&PrevSpec,
|
|
|
|
unsigned &DiagID) {
|
2008-10-31 17:07:45 +08:00
|
|
|
// 'explicit explicit' is ok.
|
|
|
|
FS_explicit_specified = true;
|
|
|
|
FS_explicitLoc = Loc;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-08-04 04:12:06 +08:00
|
|
|
bool DeclSpec::SetFriendSpec(SourceLocation Loc, const char *&PrevSpec,
|
|
|
|
unsigned &DiagID) {
|
2009-05-06 12:46:28 +08:00
|
|
|
if (Friend_specified) {
|
|
|
|
PrevSpec = "friend";
|
2009-08-04 04:12:06 +08:00
|
|
|
DiagID = diag::ext_duplicate_declspec;
|
2009-05-06 12:46:28 +08:00
|
|
|
return true;
|
|
|
|
}
|
2009-08-04 04:12:06 +08:00
|
|
|
|
2009-05-06 12:46:28 +08:00
|
|
|
Friend_specified = true;
|
|
|
|
FriendLoc = Loc;
|
|
|
|
return false;
|
|
|
|
}
|
2006-11-28 12:33:46 +08:00
|
|
|
|
2011-09-09 10:06:17 +08:00
|
|
|
bool DeclSpec::setModulePrivateSpec(SourceLocation Loc, const char *&PrevSpec,
|
|
|
|
unsigned &DiagID) {
|
|
|
|
if (isModulePrivateSpecified()) {
|
|
|
|
PrevSpec = "__module_private__";
|
|
|
|
DiagID = diag::ext_duplicate_declspec;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ModulePrivateLoc = Loc;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-11-05 23:47:02 +08:00
|
|
|
bool DeclSpec::SetConstexprSpec(SourceLocation Loc, const char *&PrevSpec,
|
|
|
|
unsigned &DiagID) {
|
|
|
|
// 'constexpr constexpr' is ok.
|
|
|
|
Constexpr_specified = true;
|
|
|
|
ConstexprLoc = Loc;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-08-21 17:40:31 +08:00
|
|
|
void DeclSpec::setProtocolQualifiers(Decl * const *Protos,
|
2009-09-30 03:42:11 +08:00
|
|
|
unsigned NP,
|
|
|
|
SourceLocation *ProtoLocs,
|
|
|
|
SourceLocation LAngleLoc) {
|
|
|
|
if (NP == 0) return;
|
2010-08-21 17:40:31 +08:00
|
|
|
ProtocolQualifiers = new Decl*[NP];
|
2009-09-30 03:42:11 +08:00
|
|
|
ProtocolLocs = new SourceLocation[NP];
|
2010-08-21 17:40:31 +08:00
|
|
|
memcpy((void*)ProtocolQualifiers, Protos, sizeof(Decl*)*NP);
|
2009-09-30 03:42:11 +08:00
|
|
|
memcpy(ProtocolLocs, ProtoLocs, sizeof(SourceLocation)*NP);
|
|
|
|
NumProtocolQualifiers = NP;
|
|
|
|
ProtocolLAngleLoc = LAngleLoc;
|
|
|
|
}
|
|
|
|
|
2010-01-19 02:04:31 +08:00
|
|
|
void DeclSpec::SaveWrittenBuiltinSpecs() {
|
|
|
|
writtenBS.Sign = getTypeSpecSign();
|
|
|
|
writtenBS.Width = getTypeSpecWidth();
|
|
|
|
writtenBS.Type = getTypeSpecType();
|
|
|
|
// Search the list of attributes for the presence of a mode attribute.
|
|
|
|
writtenBS.ModeAttr = false;
|
2010-12-24 10:08:15 +08:00
|
|
|
AttributeList* attrs = getAttributes().getList();
|
2010-01-19 02:04:31 +08:00
|
|
|
while (attrs) {
|
|
|
|
if (attrs->getKind() == AttributeList::AT_mode) {
|
|
|
|
writtenBS.ModeAttr = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
attrs = attrs->getNext();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-31 00:47:02 +08:00
|
|
|
void DeclSpec::SaveStorageSpecifierAsWritten() {
|
|
|
|
if (SCS_extern_in_linkage_spec && StorageClassSpec == SCS_extern)
|
|
|
|
// If 'extern' is part of a linkage specification,
|
|
|
|
// then it is not a storage class "as written".
|
|
|
|
StorageClassSpecAsWritten = SCS_unspecified;
|
|
|
|
else
|
|
|
|
StorageClassSpecAsWritten = StorageClassSpec;
|
|
|
|
}
|
|
|
|
|
2006-08-04 12:39:53 +08:00
|
|
|
/// Finish - This does final analysis of the declspec, rejecting things like
|
|
|
|
/// "_Imaginary" (lacking an FP type). This returns a diagnostic to issue or
|
|
|
|
/// diag::NUM_DIAGNOSTICS if there is no error. After calling this method,
|
|
|
|
/// DeclSpec is guaranteed self-consistent, even if an error occurred.
|
2011-09-26 07:23:43 +08:00
|
|
|
void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP) {
|
2010-01-19 02:04:31 +08:00
|
|
|
// Before possibly changing their values, save specs as written.
|
|
|
|
SaveWrittenBuiltinSpecs();
|
2010-04-20 06:54:31 +08:00
|
|
|
SaveStorageSpecifierAsWritten();
|
2010-01-19 02:04:31 +08:00
|
|
|
|
2006-08-04 14:36:52 +08:00
|
|
|
// Check the type specifier components first.
|
|
|
|
|
2010-06-23 14:00:24 +08:00
|
|
|
// Validate and finalize AltiVec vector declspec.
|
|
|
|
if (TypeAltiVecVector) {
|
|
|
|
if (TypeAltiVecBool) {
|
|
|
|
// Sign specifiers are not allowed with vector bool. (PIM 2.1)
|
|
|
|
if (TypeSpecSign != TSS_unspecified) {
|
2010-11-19 04:06:41 +08:00
|
|
|
Diag(D, TSSLoc, diag::err_invalid_vector_bool_decl_spec)
|
2010-06-23 14:00:24 +08:00
|
|
|
<< getSpecifierName((TSS)TypeSpecSign);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only char/int are valid with vector bool. (PIM 2.1)
|
2010-06-24 03:34:52 +08:00
|
|
|
if (((TypeSpecType != TST_unspecified) && (TypeSpecType != TST_char) &&
|
|
|
|
(TypeSpecType != TST_int)) || TypeAltiVecPixel) {
|
2010-11-19 04:06:41 +08:00
|
|
|
Diag(D, TSTLoc, diag::err_invalid_vector_bool_decl_spec)
|
2010-06-23 14:00:24 +08:00
|
|
|
<< (TypeAltiVecPixel ? "__pixel" :
|
|
|
|
getSpecifierName((TST)TypeSpecType));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only 'short' is valid with vector bool. (PIM 2.1)
|
|
|
|
if ((TypeSpecWidth != TSW_unspecified) && (TypeSpecWidth != TSW_short))
|
2010-11-19 04:06:41 +08:00
|
|
|
Diag(D, TSWLoc, diag::err_invalid_vector_bool_decl_spec)
|
2010-06-23 14:00:24 +08:00
|
|
|
<< getSpecifierName((TSW)TypeSpecWidth);
|
|
|
|
|
|
|
|
// Elements of vector bool are interpreted as unsigned. (PIM 2.1)
|
|
|
|
if ((TypeSpecType == TST_char) || (TypeSpecType == TST_int) ||
|
|
|
|
(TypeSpecWidth != TSW_unspecified))
|
|
|
|
TypeSpecSign = TSS_unsigned;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TypeAltiVecPixel) {
|
|
|
|
//TODO: perform validation
|
|
|
|
TypeSpecType = TST_int;
|
|
|
|
TypeSpecSign = TSS_unsigned;
|
|
|
|
TypeSpecWidth = TSW_short;
|
2010-08-27 01:22:34 +08:00
|
|
|
TypeSpecOwned = false;
|
2010-06-23 14:00:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-10 00:51:54 +08:00
|
|
|
// signed/unsigned are only valid with int/char/wchar_t.
|
2006-08-04 14:15:52 +08:00
|
|
|
if (TypeSpecSign != TSS_unspecified) {
|
2006-08-04 14:36:52 +08:00
|
|
|
if (TypeSpecType == TST_unspecified)
|
|
|
|
TypeSpecType = TST_int; // unsigned -> unsigned int, signed -> signed int.
|
2012-04-04 14:24:32 +08:00
|
|
|
else if (TypeSpecType != TST_int && TypeSpecType != TST_int128 &&
|
2008-08-10 00:51:54 +08:00
|
|
|
TypeSpecType != TST_char && TypeSpecType != TST_wchar) {
|
2010-11-19 04:06:41 +08:00
|
|
|
Diag(D, TSSLoc, diag::err_invalid_sign_spec)
|
2008-11-22 16:32:36 +08:00
|
|
|
<< getSpecifierName((TST)TypeSpecType);
|
2006-08-04 14:36:52 +08:00
|
|
|
// signed double -> double.
|
2006-08-04 14:15:52 +08:00
|
|
|
TypeSpecSign = TSS_unspecified;
|
|
|
|
}
|
|
|
|
}
|
2006-08-04 14:36:52 +08:00
|
|
|
|
2006-08-04 14:15:52 +08:00
|
|
|
// Validate the width of the type.
|
|
|
|
switch (TypeSpecWidth) {
|
|
|
|
case TSW_unspecified: break;
|
|
|
|
case TSW_short: // short int
|
|
|
|
case TSW_longlong: // long long int
|
2006-08-04 14:36:52 +08:00
|
|
|
if (TypeSpecType == TST_unspecified)
|
|
|
|
TypeSpecType = TST_int; // short -> short int, long long -> long long int.
|
|
|
|
else if (TypeSpecType != TST_int) {
|
2010-11-19 04:06:41 +08:00
|
|
|
Diag(D, TSWLoc,
|
2007-05-17 01:49:37 +08:00
|
|
|
TypeSpecWidth == TSW_short ? diag::err_invalid_short_spec
|
2008-11-22 16:32:36 +08:00
|
|
|
: diag::err_invalid_longlong_spec)
|
|
|
|
<< getSpecifierName((TST)TypeSpecType);
|
2006-08-04 14:15:52 +08:00
|
|
|
TypeSpecType = TST_int;
|
2010-08-27 01:22:34 +08:00
|
|
|
TypeSpecOwned = false;
|
2006-08-04 14:15:52 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TSW_long: // long double, long int
|
2006-08-04 14:36:52 +08:00
|
|
|
if (TypeSpecType == TST_unspecified)
|
|
|
|
TypeSpecType = TST_int; // long -> long int.
|
|
|
|
else if (TypeSpecType != TST_int && TypeSpecType != TST_double) {
|
2010-11-19 04:06:41 +08:00
|
|
|
Diag(D, TSWLoc, diag::err_invalid_long_spec)
|
2008-11-22 16:32:36 +08:00
|
|
|
<< getSpecifierName((TST)TypeSpecType);
|
2006-08-04 14:15:52 +08:00
|
|
|
TypeSpecType = TST_int;
|
2010-08-27 01:22:34 +08:00
|
|
|
TypeSpecOwned = false;
|
2006-08-04 14:15:52 +08:00
|
|
|
}
|
2006-08-04 14:36:52 +08:00
|
|
|
break;
|
2006-08-04 14:15:52 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2006-08-14 03:58:17 +08:00
|
|
|
// TODO: if the implementation does not implement _Complex or _Imaginary,
|
2006-08-04 14:36:52 +08:00
|
|
|
// disallow their use. Need information about the backend.
|
|
|
|
if (TypeSpecComplex != TSC_unspecified) {
|
|
|
|
if (TypeSpecType == TST_unspecified) {
|
2010-11-19 04:06:41 +08:00
|
|
|
Diag(D, TSCLoc, diag::ext_plain_complex)
|
2010-04-01 01:46:05 +08:00
|
|
|
<< FixItHint::CreateInsertion(
|
2009-04-02 06:41:11 +08:00
|
|
|
PP.getLocForEndOfToken(getTypeSpecComplexLoc()),
|
|
|
|
" double");
|
2006-08-04 14:36:52 +08:00
|
|
|
TypeSpecType = TST_double; // _Complex -> _Complex double.
|
|
|
|
} else if (TypeSpecType == TST_int || TypeSpecType == TST_char) {
|
2006-08-05 11:28:50 +08:00
|
|
|
// Note that this intentionally doesn't include _Complex _Bool.
|
2012-03-11 15:00:24 +08:00
|
|
|
if (!PP.getLangOpts().CPlusPlus)
|
2012-01-10 12:58:17 +08:00
|
|
|
Diag(D, TSTLoc, diag::ext_integer_complex);
|
2006-08-04 14:36:52 +08:00
|
|
|
} else if (TypeSpecType != TST_float && TypeSpecType != TST_double) {
|
2010-11-19 04:06:41 +08:00
|
|
|
Diag(D, TSCLoc, diag::err_invalid_complex_spec)
|
2008-11-22 16:32:36 +08:00
|
|
|
<< getSpecifierName((TST)TypeSpecType);
|
2006-08-04 14:36:52 +08:00
|
|
|
TypeSpecComplex = TSC_unspecified;
|
|
|
|
}
|
|
|
|
}
|
2006-08-05 11:30:45 +08:00
|
|
|
|
2011-09-05 03:54:14 +08:00
|
|
|
// If no type specifier was provided and we're parsing a language where
|
|
|
|
// the type specifier is not optional, but we got 'auto' as a storage
|
|
|
|
// class specifier, then assume this is an attempt to use C++0x's 'auto'
|
|
|
|
// type specifier.
|
|
|
|
// FIXME: Does Microsoft really support implicit int in C++?
|
2012-03-11 15:00:24 +08:00
|
|
|
if (PP.getLangOpts().CPlusPlus && !PP.getLangOpts().MicrosoftExt &&
|
2011-09-05 03:54:14 +08:00
|
|
|
TypeSpecType == TST_unspecified && StorageClassSpec == SCS_auto) {
|
|
|
|
TypeSpecType = TST_auto;
|
|
|
|
StorageClassSpec = StorageClassSpecAsWritten = SCS_unspecified;
|
|
|
|
TSTLoc = TSTNameLoc = StorageClassSpecLoc;
|
|
|
|
StorageClassSpecLoc = SourceLocation();
|
|
|
|
}
|
|
|
|
// Diagnose if we've recovered from an ill-formed 'auto' storage class
|
|
|
|
// specifier in a pre-C++0x dialect of C++.
|
2012-03-11 15:00:24 +08:00
|
|
|
if (!PP.getLangOpts().CPlusPlus0x && TypeSpecType == TST_auto)
|
2011-09-05 03:54:14 +08:00
|
|
|
Diag(D, TSTLoc, diag::ext_auto_type_specifier);
|
2012-03-11 15:00:24 +08:00
|
|
|
if (PP.getLangOpts().CPlusPlus && !PP.getLangOpts().CPlusPlus0x &&
|
2011-09-05 03:54:14 +08:00
|
|
|
StorageClassSpec == SCS_auto)
|
|
|
|
Diag(D, StorageClassSpecLoc, diag::warn_auto_storage_class)
|
|
|
|
<< FixItHint::CreateRemoval(StorageClassSpecLoc);
|
2011-10-18 07:06:20 +08:00
|
|
|
if (TypeSpecType == TST_char16 || TypeSpecType == TST_char32)
|
|
|
|
Diag(D, TSTLoc, diag::warn_cxx98_compat_unicode_type)
|
|
|
|
<< (TypeSpecType == TST_char16 ? "char16_t" : "char32_t");
|
|
|
|
if (Constexpr_specified)
|
|
|
|
Diag(D, ConstexprLoc, diag::warn_cxx98_compat_constexpr);
|
2011-09-05 03:54:14 +08:00
|
|
|
|
2009-08-06 10:15:43 +08:00
|
|
|
// C++ [class.friend]p6:
|
|
|
|
// No storage-class-specifier shall appear in the decl-specifier-seq
|
|
|
|
// of a friend declaration.
|
|
|
|
if (isFriendSpecified() && getStorageClassSpec()) {
|
|
|
|
DeclSpec::SCS SC = getStorageClassSpec();
|
|
|
|
const char *SpecName = getSpecifierName(SC);
|
|
|
|
|
|
|
|
SourceLocation SCLoc = getStorageClassSpecLoc();
|
2011-09-20 04:40:19 +08:00
|
|
|
SourceLocation SCEndLoc = SCLoc.getLocWithOffset(strlen(SpecName));
|
2009-08-06 10:15:43 +08:00
|
|
|
|
2010-11-19 04:06:41 +08:00
|
|
|
Diag(D, SCLoc, diag::err_friend_storage_spec)
|
2009-08-06 10:15:43 +08:00
|
|
|
<< SpecName
|
2010-04-01 01:46:05 +08:00
|
|
|
<< FixItHint::CreateRemoval(SourceRange(SCLoc, SCEndLoc));
|
2009-08-06 10:15:43 +08:00
|
|
|
|
|
|
|
ClearStorageClassSpecs();
|
|
|
|
}
|
|
|
|
|
2011-09-10 05:14:29 +08:00
|
|
|
assert(!TypeSpecOwned || isDeclRep((TST) TypeSpecType));
|
|
|
|
|
2006-08-05 11:30:45 +08:00
|
|
|
// Okay, now we can infer the real type.
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2006-08-14 03:58:17 +08:00
|
|
|
// TODO: return "auto function" and other bad things based on the real type.
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2006-08-04 14:42:31 +08:00
|
|
|
// 'data definition has no type or storage class'?
|
2006-08-04 12:39:53 +08:00
|
|
|
}
|
2008-08-11 11:45:03 +08:00
|
|
|
|
2008-12-28 23:28:59 +08:00
|
|
|
bool DeclSpec::isMissingDeclaratorOk() {
|
|
|
|
TST tst = getTypeSpecType();
|
2010-08-24 13:47:05 +08:00
|
|
|
return isDeclRep(tst) && getRepAsDecl() != 0 &&
|
|
|
|
StorageClassSpec != DeclSpec::SCS_typedef;
|
2008-12-28 23:28:59 +08:00
|
|
|
}
|
2009-11-03 09:35:08 +08:00
|
|
|
|
|
|
|
void UnqualifiedId::setOperatorFunctionId(SourceLocation OperatorLoc,
|
|
|
|
OverloadedOperatorKind Op,
|
|
|
|
SourceLocation SymbolLocations[3]) {
|
|
|
|
Kind = IK_OperatorFunctionId;
|
|
|
|
StartLocation = OperatorLoc;
|
|
|
|
EndLocation = OperatorLoc;
|
|
|
|
OperatorFunctionId.Operator = Op;
|
|
|
|
for (unsigned I = 0; I != 3; ++I) {
|
|
|
|
OperatorFunctionId.SymbolLocations[I] = SymbolLocations[I].getRawEncoding();
|
|
|
|
|
|
|
|
if (SymbolLocations[I].isValid())
|
|
|
|
EndLocation = SymbolLocations[I];
|
|
|
|
}
|
|
|
|
}
|
2011-01-17 11:05:47 +08:00
|
|
|
|
2011-01-23 00:56:46 +08:00
|
|
|
bool VirtSpecifiers::SetSpecifier(Specifier VS, SourceLocation Loc,
|
2011-01-22 23:58:16 +08:00
|
|
|
const char *&PrevSpec) {
|
2011-03-09 01:10:18 +08:00
|
|
|
LastLocation = Loc;
|
|
|
|
|
2011-01-17 11:05:47 +08:00
|
|
|
if (Specifiers & VS) {
|
|
|
|
PrevSpec = getSpecifierName(VS);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Specifiers |= VS;
|
|
|
|
|
|
|
|
switch (VS) {
|
2011-09-23 13:06:16 +08:00
|
|
|
default: llvm_unreachable("Unknown specifier!");
|
2011-01-17 11:05:47 +08:00
|
|
|
case VS_Override: VS_overrideLoc = Loc; break;
|
|
|
|
case VS_Final: VS_finalLoc = Loc; break;
|
|
|
|
}
|
2011-01-22 23:58:16 +08:00
|
|
|
|
2011-01-17 11:05:47 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-01-23 00:56:46 +08:00
|
|
|
const char *VirtSpecifiers::getSpecifierName(Specifier VS) {
|
2011-01-22 23:11:37 +08:00
|
|
|
switch (VS) {
|
2011-09-23 13:06:16 +08:00
|
|
|
default: llvm_unreachable("Unknown specifier");
|
2011-01-22 23:11:37 +08:00
|
|
|
case VS_Override: return "override";
|
|
|
|
case VS_Final: return "final";
|
|
|
|
}
|
|
|
|
}
|