summaryrefslogtreecommitdiffstats
path: root/clang/lib/Sema
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r--clang/lib/Sema/SemaDecl.cpp86
-rw-r--r--clang/lib/Sema/SemaDeclAttr.cpp3
-rw-r--r--clang/lib/Sema/SemaStmt.cpp147
3 files changed, 159 insertions, 77 deletions
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index d299ec1e57f..f75a2cee63c 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -13501,6 +13501,49 @@ static void CheckForDuplicateEnumValues(Sema &S, ArrayRef<Decl *> Elements,
}
}
+bool
+Sema::IsValueInFlagEnum(const EnumDecl *ED, const llvm::APInt &Val,
+ bool AllowMask) const {
+ FlagEnumAttr *FEAttr = ED->getAttr<FlagEnumAttr>();
+ assert(FEAttr && "looking for value in non-flag enum");
+
+ llvm::APInt FlagMask = ~FEAttr->getFlagBits();
+ unsigned Width = FlagMask.getBitWidth();
+
+ // We will try a zero-extended value for the regular check first.
+ llvm::APInt ExtVal = Val.zextOrSelf(Width);
+
+ // A value is in a flag enum if either its bits are a subset of the enum's
+ // flag bits (the first condition) or we are allowing masks and the same is
+ // true of its complement (the second condition). When masks are allowed, we
+ // allow the common idiom of ~(enum1 | enum2) to be a valid enum value.
+ //
+ // While it's true that any value could be used as a mask, the assumption is
+ // that a mask will have all of the insignificant bits set. Anything else is
+ // likely a logic error.
+ if (!(FlagMask & ExtVal))
+ return true;
+
+ if (AllowMask) {
+ // Try a one-extended value instead. This can happen if the enum is wider
+ // than the constant used, in C with extensions to allow for wider enums.
+ // The mask will still have the correct behaviour, so we give the user the
+ // benefit of the doubt.
+ //
+ // FIXME: This heuristic can cause weird results if the enum was extended
+ // to a larger type and is signed, because then bit-masks of smaller types
+ // that get extended will fall out of range (e.g. ~0x1u). We currently don't
+ // detect that case and will get a false positive for it. In most cases,
+ // though, it can be fixed by making it a signed type (e.g. ~0x1), so it may
+ // be fine just to accept this as a warning.
+ ExtVal |= llvm::APInt::getHighBitsSet(Width, Width - Val.getBitWidth());
+ if (!(FlagMask & ~ExtVal))
+ return true;
+ }
+
+ return false;
+}
+
void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc,
SourceLocation RBraceLoc, Decl *EnumDeclX,
ArrayRef<Decl *> Elements,
@@ -13586,10 +13629,8 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc,
BestPromotionType = Context.getPromotedIntegerType(BestType);
else
BestPromotionType = BestType;
- // We don't need to set BestWidth, because BestType is going to be the type
- // of the enumerators, but we do anyway because otherwise some compilers
- // warn that it might be used uninitialized.
- BestWidth = CharWidth;
+
+ BestWidth = Context.getIntWidth(BestType);
}
else if (NumNegativeBits) {
// If there is a negative value, figure out the smallest integer type (of
@@ -13654,10 +13695,15 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc,
}
}
+ FlagEnumAttr *FEAttr = Enum->getAttr<FlagEnumAttr>();
+ if (FEAttr)
+ FEAttr->getFlagBits() = llvm::APInt(BestWidth, 0);
+
// Loop over all of the enumerator constants, changing their types to match
- // the type of the enum if needed.
- for (unsigned i = 0, e = Elements.size(); i != e; ++i) {
- EnumConstantDecl *ECD = cast_or_null<EnumConstantDecl>(Elements[i]);
+ // the type of the enum if needed. If we have a flag type, we also prepare the
+ // FlagBits cache.
+ for (auto *D : Elements) {
+ auto *ECD = cast_or_null<EnumConstantDecl>(D);
if (!ECD) continue; // Already issued a diagnostic.
// Standard C says the enumerators have int type, but we allow, as an
@@ -13687,7 +13733,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc,
// enum-specifier, each enumerator has the type of its
// enumeration.
ECD->setType(EnumType);
- continue;
+ goto flagbits;
} else {
NewTy = BestType;
NewWidth = BestWidth;
@@ -13714,8 +13760,32 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc,
ECD->setType(EnumType);
else
ECD->setType(NewTy);
+
+flagbits:
+ // Check to see if we have a constant with exactly one bit set. Note that x
+ // & (x - 1) will be nonzero if and only if x has more than one bit set.
+ if (FEAttr) {
+ llvm::APInt ExtVal = InitVal.zextOrSelf(BestWidth);
+ if (ExtVal != 0 && !(ExtVal & (ExtVal - 1))) {
+ FEAttr->getFlagBits() |= ExtVal;
+ }
+ }
}
+ if (FEAttr) {
+ for (Decl *D : Elements) {
+ EnumConstantDecl *ECD = cast_or_null<EnumConstantDecl>(D);
+ if (!ECD) continue; // Already issued a diagnostic.
+
+ llvm::APSInt InitVal = ECD->getInitVal();
+ if (InitVal != 0 && !IsValueInFlagEnum(Enum, InitVal, true))
+ Diag(ECD->getLocation(), diag::warn_flag_enum_constant_out_of_range)
+ << ECD << Enum;
+ }
+ }
+
+
+
Enum->completeDefinition(BestType, BestPromotionType,
NumPositiveBits, NumNegativeBits);
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 00f9f803286..d3f28afb2c1 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -4313,6 +4313,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case AttributeList::AT_OptimizeNone:
handleOptimizeNoneAttr(S, D, Attr);
break;
+ case AttributeList::AT_FlagEnum:
+ handleSimpleAttribute<FlagEnumAttr>(S, D, Attr);
+ break;
case AttributeList::AT_Flatten:
handleSimpleAttribute<FlattenAttr>(S, D, Attr);
break;
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 14f7092e155..78e33c3bf8c 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -687,26 +687,39 @@ static void checkCaseValue(Sema &S, SourceLocation Loc, const llvm::APSInt &Val,
}
}
+typedef SmallVector<std::pair<llvm::APSInt, EnumConstantDecl*>, 64> EnumValsTy;
+
/// Returns true if we should emit a diagnostic about this case expression not
/// being a part of the enum used in the switch controlling expression.
-static bool ShouldDiagnoseSwitchCaseNotInEnum(const ASTContext &Ctx,
+static bool ShouldDiagnoseSwitchCaseNotInEnum(const Sema &S,
const EnumDecl *ED,
- const Expr *CaseExpr) {
- // Don't warn if the 'case' expression refers to a static const variable of
- // the enum type.
- CaseExpr = CaseExpr->IgnoreParenImpCasts();
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(CaseExpr)) {
+ const Expr *CaseExpr,
+ EnumValsTy::iterator &EI,
+ EnumValsTy::iterator &EIEnd,
+ const llvm::APSInt &Val) {
+ bool FlagType = ED->hasAttr<FlagEnumAttr>();
+
+ if (const DeclRefExpr *DRE =
+ dyn_cast<DeclRefExpr>(CaseExpr->IgnoreParenImpCasts())) {
if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
- if (!VD->hasGlobalStorage())
- return true;
QualType VarType = VD->getType();
- if (!VarType.isConstQualified())
- return true;
- QualType EnumType = Ctx.getTypeDeclType(ED);
- if (Ctx.hasSameUnqualifiedType(EnumType, VarType))
+ QualType EnumType = S.Context.getTypeDeclType(ED);
+ if (VD->hasGlobalStorage() && VarType.isConstQualified() &&
+ S.Context.hasSameUnqualifiedType(EnumType, VarType))
return false;
}
}
+
+ if (FlagType) {
+ return !S.IsValueInFlagEnum(ED, Val, false);
+ } else {
+ while (EI != EIEnd && EI->first < Val)
+ EI++;
+
+ if (EI != EIEnd && EI->first == Val)
+ return false;
+ }
+
return true;
}
@@ -1045,8 +1058,6 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
// If switch has default case, then ignore it.
if (!CaseListIsErroneous && !HasConstantCond && ET) {
const EnumDecl *ED = ET->getDecl();
- typedef SmallVector<std::pair<llvm::APSInt, EnumConstantDecl*>, 64>
- EnumValsTy;
EnumValsTy EnumVals;
// Gather all enum values, set their type and sort them,
@@ -1057,57 +1068,48 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
EnumVals.push_back(std::make_pair(Val, EDI));
}
std::stable_sort(EnumVals.begin(), EnumVals.end(), CmpEnumVals);
- EnumValsTy::iterator EIend =
+ auto EI = EnumVals.begin(), EIEnd =
std::unique(EnumVals.begin(), EnumVals.end(), EqEnumVals);
// See which case values aren't in enum.
- EnumValsTy::const_iterator EI = EnumVals.begin();
for (CaseValsTy::const_iterator CI = CaseVals.begin();
- CI != CaseVals.end(); CI++) {
- while (EI != EIend && EI->first < CI->first)
- EI++;
- if (EI == EIend || EI->first > CI->first) {
- Expr *CaseExpr = CI->second->getLHS();
- if (ShouldDiagnoseSwitchCaseNotInEnum(Context, ED, CaseExpr))
- Diag(CaseExpr->getExprLoc(), diag::warn_not_in_enum)
- << CondTypeBeforePromotion;
- }
+ CI != CaseVals.end(); CI++) {
+ Expr *CaseExpr = CI->second->getLHS();
+ if (ShouldDiagnoseSwitchCaseNotInEnum(*this, ED, CaseExpr, EI, EIEnd,
+ CI->first))
+ Diag(CaseExpr->getExprLoc(), diag::warn_not_in_enum)
+ << CondTypeBeforePromotion;
}
+
// See which of case ranges aren't in enum
EI = EnumVals.begin();
for (CaseRangesTy::const_iterator RI = CaseRanges.begin();
- RI != CaseRanges.end() && EI != EIend; RI++) {
- while (EI != EIend && EI->first < RI->first)
- EI++;
-
- if (EI == EIend || EI->first != RI->first) {
- Expr *CaseExpr = RI->second->getLHS();
- if (ShouldDiagnoseSwitchCaseNotInEnum(Context, ED, CaseExpr))
- Diag(CaseExpr->getExprLoc(), diag::warn_not_in_enum)
- << CondTypeBeforePromotion;
- }
+ RI != CaseRanges.end(); RI++) {
+ Expr *CaseExpr = RI->second->getLHS();
+ if (ShouldDiagnoseSwitchCaseNotInEnum(*this, ED, CaseExpr, EI, EIEnd,
+ RI->first))
+ Diag(CaseExpr->getExprLoc(), diag::warn_not_in_enum)
+ << CondTypeBeforePromotion;
llvm::APSInt Hi =
RI->second->getRHS()->EvaluateKnownConstInt(Context);
AdjustAPSInt(Hi, CondWidth, CondIsSigned);
- while (EI != EIend && EI->first < Hi)
- EI++;
- if (EI == EIend || EI->first != Hi) {
- Expr *CaseExpr = RI->second->getRHS();
- if (ShouldDiagnoseSwitchCaseNotInEnum(Context, ED, CaseExpr))
- Diag(CaseExpr->getExprLoc(), diag::warn_not_in_enum)
- << CondTypeBeforePromotion;
- }
+
+ CaseExpr = RI->second->getRHS();
+ if (ShouldDiagnoseSwitchCaseNotInEnum(*this, ED, CaseExpr, EI, EIEnd,
+ Hi))
+ Diag(CaseExpr->getExprLoc(), diag::warn_not_in_enum)
+ << CondTypeBeforePromotion;
}
// Check which enum vals aren't in switch
- CaseValsTy::const_iterator CI = CaseVals.begin();
- CaseRangesTy::const_iterator RI = CaseRanges.begin();
+ auto CI = CaseVals.begin();
+ auto RI = CaseRanges.begin();
bool hasCasesNotInSwitch = false;
SmallVector<DeclarationName,8> UnhandledNames;
- for (EI = EnumVals.begin(); EI != EIend; EI++){
+ for (EI = EnumVals.begin(); EI != EIEnd; EI++){
// Drop unneeded case values
while (CI != CaseVals.end() && CI->first < EI->first)
CI++;
@@ -1194,30 +1196,37 @@ Sema::DiagnoseAssignmentEnum(QualType DstType, QualType SrcType,
llvm::APSInt RhsVal = SrcExpr->EvaluateKnownConstInt(Context);
AdjustAPSInt(RhsVal, DstWidth, DstIsSigned);
const EnumDecl *ED = ET->getDecl();
- typedef SmallVector<std::pair<llvm::APSInt, EnumConstantDecl *>, 64>
- EnumValsTy;
- EnumValsTy EnumVals;
-
- // Gather all enum values, set their type and sort them,
- // allowing easier comparison with rhs constant.
- for (auto *EDI : ED->enumerators()) {
- llvm::APSInt Val = EDI->getInitVal();
- AdjustAPSInt(Val, DstWidth, DstIsSigned);
- EnumVals.push_back(std::make_pair(Val, EDI));
- }
- if (EnumVals.empty())
- return;
- std::stable_sort(EnumVals.begin(), EnumVals.end(), CmpEnumVals);
- EnumValsTy::iterator EIend =
- std::unique(EnumVals.begin(), EnumVals.end(), EqEnumVals);
-
- // See which values aren't in the enum.
- EnumValsTy::const_iterator EI = EnumVals.begin();
- while (EI != EIend && EI->first < RhsVal)
- EI++;
- if (EI == EIend || EI->first != RhsVal) {
- Diag(SrcExpr->getExprLoc(), diag::warn_not_in_enum_assignment)
+
+ if (ED->hasAttr<FlagEnumAttr>()) {
+ if (!IsValueInFlagEnum(ED, RhsVal, true))
+ Diag(SrcExpr->getExprLoc(), diag::warn_not_in_enum_assignment)
<< DstType.getUnqualifiedType();
+ } else {
+ typedef SmallVector<std::pair<llvm::APSInt, EnumConstantDecl *>, 64>
+ EnumValsTy;
+ EnumValsTy EnumVals;
+
+ // Gather all enum values, set their type and sort them,
+ // allowing easier comparison with rhs constant.
+ for (auto *EDI : ED->enumerators()) {
+ llvm::APSInt Val = EDI->getInitVal();
+ AdjustAPSInt(Val, DstWidth, DstIsSigned);
+ EnumVals.push_back(std::make_pair(Val, EDI));
+ }
+ if (EnumVals.empty())
+ return;
+ std::stable_sort(EnumVals.begin(), EnumVals.end(), CmpEnumVals);
+ EnumValsTy::iterator EIend =
+ std::unique(EnumVals.begin(), EnumVals.end(), EqEnumVals);
+
+ // See which values aren't in the enum.
+ EnumValsTy::const_iterator EI = EnumVals.begin();
+ while (EI != EIend && EI->first < RhsVal)
+ EI++;
+ if (EI == EIend || EI->first != RhsVal) {
+ Diag(SrcExpr->getExprLoc(), diag::warn_not_in_enum_assignment)
+ << DstType.getUnqualifiedType();
+ }
}
}
}
OpenPOWER on IntegriCloud