From: Douglas Gregor Date: Tue, 29 Jun 2010 23:17:37 +0000 (+0000) Subject: Factor the conversion from a switch condition to an integral or X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c30614b7e2bad089f2509499379de509f33162d6;p=clang Factor the conversion from a switch condition to an integral or enumeration type out into a separate, reusable routine. The only functionality change here is that we recover a little more aggressively from ill-formed switch conditions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@107222 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index f93a9492b2..c771898d96 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1188,6 +1188,15 @@ public: ImplicitConversionSequence TryContextuallyConvertToObjCId(Expr *From); bool PerformContextuallyConvertToObjCId(Expr *&From); + OwningExprResult + ConvertToIntegralOrEnumerationType(SourceLocation Loc, ExprArg FromE, + const PartialDiagnostic &NotIntDiag, + const PartialDiagnostic &IncompleteDiag, + const PartialDiagnostic &ExplicitConvDiag, + const PartialDiagnostic &ExplicitConvNote, + const PartialDiagnostic &AmbigDiag, + const PartialDiagnostic &AmbigNote); + bool PerformObjectMemberConversion(Expr *&From, NestedNameSpecifier *Qualifier, NamedDecl *FoundDecl, diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 1801565ff0..86d4d42ee0 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -722,6 +722,7 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal, Expr *ArraySize = (Expr *)ArraySizeE.get(); if (ArraySize && !ArraySize->isTypeDependent()) { QualType SizeType = ArraySize->getType(); + if (!SizeType->isIntegralOrEnumerationType()) return ExprError(Diag(ArraySize->getSourceRange().getBegin(), diag::err_array_size_not_integral) diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 317e8cd6d3..af28d46b5f 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -3068,6 +3068,140 @@ bool Sema::PerformContextuallyConvertToObjCId(Expr *&From) { return true; } +/// \brief Attempt to convert the given expression to an integral or +/// enumeration type. +/// +/// This routine will attempt to convert an expression of class type to an +/// integral or enumeration type, if that class type only has a single +/// conversion to an integral or enumeration type. +/// +/// \param From The expression we're converting from. +/// +/// \returns The expression converted to an integral or enumeration type, +/// if successful, or an invalid expression. +Sema::OwningExprResult +Sema::ConvertToIntegralOrEnumerationType(SourceLocation Loc, ExprArg FromE, + const PartialDiagnostic &NotIntDiag, + const PartialDiagnostic &IncompleteDiag, + const PartialDiagnostic &ExplicitConvDiag, + const PartialDiagnostic &ExplicitConvNote, + const PartialDiagnostic &AmbigDiag, + const PartialDiagnostic &AmbigNote) { + Expr *From = static_cast(FromE.get()); + + // We can't perform any more checking for type-dependent expressions. + if (From->isTypeDependent()) + return move(FromE); + + // If the expression already has integral or enumeration type, we're golden. + QualType T = From->getType(); + if (T->isIntegralOrEnumerationType()) + return move(FromE); + + // FIXME: Check for missing '()' if T is a function type? + + // If we don't have a class type in C++, there's no way we can get an + // expression of integral or enumeration type. + const RecordType *RecordTy = T->getAs(); + if (!RecordTy || !getLangOptions().CPlusPlus) { + Diag(Loc, NotIntDiag) + << T << From->getSourceRange(); + return ExprError(); + } + + // We must have a complete class type. + if (RequireCompleteType(Loc, T, IncompleteDiag)) + return ExprError(); + + // Look for a conversion to an integral or enumeration type. + UnresolvedSet<4> ViableConversions; + UnresolvedSet<4> ExplicitConversions; + const UnresolvedSetImpl *Conversions + = cast(RecordTy->getDecl())->getVisibleConversionFunctions(); + + for (UnresolvedSetImpl::iterator I = Conversions->begin(), + E = Conversions->end(); + I != E; + ++I) { + if (CXXConversionDecl *Conversion + = dyn_cast((*I)->getUnderlyingDecl())) + if (Conversion->getConversionType().getNonReferenceType() + ->isIntegralOrEnumerationType()) { + if (Conversion->isExplicit()) + ExplicitConversions.addDecl(I.getDecl(), I.getAccess()); + else + ViableConversions.addDecl(I.getDecl(), I.getAccess()); + } + } + + switch (ViableConversions.size()) { + case 0: + if (ExplicitConversions.size() == 1) { + DeclAccessPair Found = ExplicitConversions[0]; + CXXConversionDecl *Conversion + = cast(Found->getUnderlyingDecl()); + + // The user probably meant to invoke the given explicit + // conversion; use it. + QualType ConvTy + = Conversion->getConversionType().getNonReferenceType(); + std::string TypeStr; + ConvTy.getAsStringInternal(TypeStr, Context.PrintingPolicy); + + Diag(Loc, ExplicitConvDiag) + << T << ConvTy + << FixItHint::CreateInsertion(From->getLocStart(), + "static_cast<" + TypeStr + ">(") + << FixItHint::CreateInsertion(PP.getLocForEndOfToken(From->getLocEnd()), + ")"); + Diag(Conversion->getLocation(), ExplicitConvNote) + << ConvTy->isEnumeralType() << ConvTy; + + // If we aren't in a SFINAE context, build a call to the + // explicit conversion function. + if (isSFINAEContext()) + return ExprError(); + + CheckMemberOperatorAccess(From->getExprLoc(), From, 0, Found); + From = BuildCXXMemberCallExpr(FromE.takeAs(), Found, Conversion); + FromE = Owned(From); + } + + // We'll complain below about a non-integral condition type. + break; + + case 1: { + // Apply this conversion. + DeclAccessPair Found = ViableConversions[0]; + CheckMemberOperatorAccess(From->getExprLoc(), From, 0, Found); + From = BuildCXXMemberCallExpr(FromE.takeAs(), Found, + cast(Found->getUnderlyingDecl())); + FromE = Owned(From); + break; + } + + default: + Diag(Loc, AmbigDiag) + << T << From->getSourceRange(); + for (unsigned I = 0, N = ViableConversions.size(); I != N; ++I) { + CXXConversionDecl *Conv + = cast(ViableConversions[I]->getUnderlyingDecl()); + QualType ConvTy = Conv->getConversionType().getNonReferenceType(); + Diag(Conv->getLocation(), AmbigNote) + << ConvTy->isEnumeralType() << ConvTy; + } + return ExprError(); + } + + if (!From->getType()->isIntegralOrEnumerationType()) { + Diag(Loc, NotIntDiag) + << From->getType() << From->getSourceRange(); + return ExprError(); + } + + return move(FromE); +} + /// AddOverloadCandidate - Adds the given function to the set of /// candidate functions, using the given function call arguments. If /// @p SuppressUserConversions, then don't allow user-defined diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 8e163ef245..fed30e4e56 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -410,112 +410,6 @@ static QualType GetTypeBeforeIntegralPromotion(const Expr* expr) { return expr->getType(); } -/// \brief Check (and possibly convert) the condition in a switch -/// statement in C++. -static bool CheckCXXSwitchCondition(Sema &S, SourceLocation SwitchLoc, - Expr *&CondExpr) { - if (CondExpr->isTypeDependent()) - return false; - - QualType CondType = CondExpr->getType(); - - // C++ 6.4.2.p2: - // The condition shall be of integral type, enumeration type, or of a class - // type for which a single conversion function to integral or enumeration - // type exists (12.3). If the condition is of class type, the condition is - // converted by calling that conversion function, and the result of the - // conversion is used in place of the original condition for the remainder - // of this section. Integral promotions are performed. - - // Make sure that the condition expression has a complete type, - // otherwise we'll never find any conversions. - if (S.RequireCompleteType(SwitchLoc, CondType, - S.PDiag(diag::err_switch_incomplete_class_type) - << CondExpr->getSourceRange())) - return true; - - UnresolvedSet<4> ViableConversions; - UnresolvedSet<4> ExplicitConversions; - if (const RecordType *RecordTy = CondType->getAs()) { - const UnresolvedSetImpl *Conversions - = cast(RecordTy->getDecl()) - ->getVisibleConversionFunctions(); - for (UnresolvedSetImpl::iterator I = Conversions->begin(), - E = Conversions->end(); I != E; ++I) { - if (CXXConversionDecl *Conversion - = dyn_cast((*I)->getUnderlyingDecl())) - if (Conversion->getConversionType().getNonReferenceType() - ->isIntegralOrEnumerationType()) { - if (Conversion->isExplicit()) - ExplicitConversions.addDecl(I.getDecl(), I.getAccess()); - else - ViableConversions.addDecl(I.getDecl(), I.getAccess()); - } - } - - switch (ViableConversions.size()) { - case 0: - if (ExplicitConversions.size() == 1) { - DeclAccessPair Found = ExplicitConversions[0]; - CXXConversionDecl *Conversion = - cast(Found->getUnderlyingDecl()); - // The user probably meant to invoke the given explicit - // conversion; use it. - QualType ConvTy - = Conversion->getConversionType().getNonReferenceType(); - std::string TypeStr; - ConvTy.getAsStringInternal(TypeStr, S.Context.PrintingPolicy); - - S.Diag(SwitchLoc, diag::err_switch_explicit_conversion) - << CondType << ConvTy << CondExpr->getSourceRange() - << FixItHint::CreateInsertion(CondExpr->getLocStart(), - "static_cast<" + TypeStr + ">(") - << FixItHint::CreateInsertion( - S.PP.getLocForEndOfToken(CondExpr->getLocEnd()), - ")"); - S.Diag(Conversion->getLocation(), diag::note_switch_conversion) - << ConvTy->isEnumeralType() << ConvTy; - - // If we aren't in a SFINAE context, build a call to the - // explicit conversion function. - if (S.isSFINAEContext()) - return true; - - S.CheckMemberOperatorAccess(CondExpr->getExprLoc(), - CondExpr, 0, Found); - CondExpr = S.BuildCXXMemberCallExpr(CondExpr, Found, Conversion); - } - - // We'll complain below about a non-integral condition type. - break; - - case 1: { - // Apply this conversion. - DeclAccessPair Found = ViableConversions[0]; - S.CheckMemberOperatorAccess(CondExpr->getExprLoc(), - CondExpr, 0, Found); - CondExpr = S.BuildCXXMemberCallExpr(CondExpr, Found, - cast(Found->getUnderlyingDecl())); - break; - } - - default: - S.Diag(SwitchLoc, diag::err_switch_multiple_conversions) - << CondType << CondExpr->getSourceRange(); - for (unsigned I = 0, N = ViableConversions.size(); I != N; ++I) { - CXXConversionDecl *Conv - = cast(ViableConversions[I]->getUnderlyingDecl()); - QualType ConvTy = Conv->getConversionType().getNonReferenceType(); - S.Diag(Conv->getLocation(), diag::note_switch_conversion) - << ConvTy->isEnumeralType() << ConvTy; - } - return true; - } - } - - return false; -} - Action::OwningStmtResult Sema::ActOnStartOfSwitchStmt(SourceLocation SwitchLoc, ExprArg Cond, DeclPtrTy CondVar) { @@ -529,14 +423,24 @@ Sema::ActOnStartOfSwitchStmt(SourceLocation SwitchLoc, ExprArg Cond, Cond = move(CondE); } - Expr *CondExpr = Cond.takeAs(); - if (!CondExpr) + if (!Cond.get()) return StmtError(); - if (getLangOptions().CPlusPlus && - CheckCXXSwitchCondition(*this, SwitchLoc, CondExpr)) - return StmtError(); - + Expr *CondExpr = static_cast(Cond.get()); + OwningExprResult ConvertedCond + = ConvertToIntegralOrEnumerationType(SwitchLoc, move(Cond), + PDiag(diag::err_typecheck_statement_requires_integer), + PDiag(diag::err_switch_incomplete_class_type) + << CondExpr->getSourceRange(), + PDiag(diag::err_switch_explicit_conversion), + PDiag(diag::note_switch_conversion), + PDiag(diag::err_switch_multiple_conversions), + PDiag(diag::note_switch_conversion)); + if (ConvertedCond.isInvalid()) + return StmtError(); + + CondExpr = ConvertedCond.takeAs(); + if (!CondVar.get()) { CondExpr = MaybeCreateCXXExprWithTemporaries(CondExpr); if (!CondExpr) diff --git a/test/Sema/complex-int.c b/test/Sema/complex-int.c index cb76a342c2..bcd9939b29 100644 --- a/test/Sema/complex-int.c +++ b/test/Sema/complex-int.c @@ -16,9 +16,13 @@ result = arr*brr; result = xx*yy; switch (arr) { // expected-error{{statement requires expression of integer type ('_Complex int' invalid)}} + default: ; +} + + switch (ii) { case brr: ; // expected-error{{expression is not an integer constant expression}} case xx: ; // expected-error{{expression is not an integer constant expression}} -} + } } void Tester() {