From: Eli Friedman Date: Fri, 20 Jan 2012 01:26:23 +0000 (+0000) Subject: Remove PotentiallyPotentiallyEvaluated, and replace it with a much simpler and less... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ef331b783bb96a0f0e34afdb7ef46677dc4764cb;p=clang Remove PotentiallyPotentiallyEvaluated, and replace it with a much simpler and less error-prone way of handling the relevant cases. Towards marking of whether a declaration is used more accurately. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@148522 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index c09d41af58..923209bbed 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -511,21 +511,6 @@ public: /// call was found yet. bool ObjCShouldCallSuperFinalize; - /// \brief The set of declarations that have been referenced within - /// a potentially evaluated expression. - typedef SmallVector, 10> - PotentiallyReferencedDecls; - - /// \brief A set of diagnostics that may be emitted. - typedef SmallVector, 10> - PotentiallyEmittedDiagnostics; - - typedef SmallVector - PotentiallyEmittedDelayedDiag; - - typedef SmallVector - PotentiallyEmittedPossiblyUnreachableDiag; - /// \brief Describes how the expressions currently being parsed are /// evaluated at run-time, if at all. enum ExpressionEvaluationContext { @@ -546,13 +531,6 @@ public: /// expression at run time. PotentiallyEvaluated, - /// \brief The current expression may be potentially evaluated or it may - /// be unevaluated, but it is impossible to tell from the lexical context. - /// This evaluation context is used primary for the operand of the C++ - /// \c typeid expression, whose argument is potentially evaluated only when - /// it is an lvalue of polymorphic class type (C++ [basic.def.odr]p2). - PotentiallyPotentiallyEvaluated, - /// \brief The current expression is potentially evaluated, but any /// declarations referenced inside that expression are only used if /// in fact the current expression is used. @@ -577,43 +555,11 @@ public: /// this expression evaluation context. unsigned NumCleanupObjects; - /// \brief The set of declarations referenced within a - /// potentially potentially-evaluated context. - /// - /// When leaving a potentially potentially-evaluated context, each - /// of these elements will be as referenced if the corresponding - /// potentially potentially evaluated expression is potentially - /// evaluated. - PotentiallyReferencedDecls *PotentiallyReferenced; - - // There are three kinds of diagnostics we care about in - // PotentiallyPotentiallyEvaluated contexts: regular Diag diagnostics, - // DelayedDiagnostics, and DiagRuntimeBehavior diagnostics. - PotentiallyEmittedDiagnostics *SavedDiag; - PotentiallyEmittedDelayedDiag *SavedDelayedDiag; - PotentiallyEmittedPossiblyUnreachableDiag *SavedRuntimeDiag; - ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context, unsigned NumCleanupObjects, bool ParentNeedsCleanups) : Context(Context), ParentNeedsCleanups(ParentNeedsCleanups), - NumCleanupObjects(NumCleanupObjects), - PotentiallyReferenced(0), SavedDiag(0), SavedDelayedDiag(0), - SavedRuntimeDiag(0) { } - - void addReferencedDecl(SourceLocation Loc, Decl *Decl) { - if (!PotentiallyReferenced) - PotentiallyReferenced = new PotentiallyReferencedDecls; - PotentiallyReferenced->push_back(std::make_pair(Loc, Decl)); - } - - void addDiagnostic(SourceLocation Loc, const PartialDiagnostic &PD); - - void addRuntimeDiagnostic(const sema::PossiblyUnreachableDiag &PUD); - - void addDelayedDiagnostic(const sema::DelayedDiagnostic &DD); - - void Destroy(); + NumCleanupObjects(NumCleanupObjects) { } }; /// A stack of expression evaluation contexts. @@ -2309,6 +2255,8 @@ public: void DiscardCleanupsInEvaluationContext(); + ExprResult TranformToPotentiallyEvaluated(Expr *E); + void MarkDeclarationReferenced(SourceLocation Loc, Decl *D); void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T); void MarkDeclarationsReferencedInExpr(Expr *E); @@ -3116,8 +3064,7 @@ public: QualType getCurrentThisType(); /// \brief Make sure the value of 'this' is actually available in the current - /// context, if it is a potentially evaluated context. This check can be - /// delayed in PotentiallyPotentiallyEvaluated contexts. + /// context, if it is a potentially evaluated context. void CheckCXXThisCapture(SourceLocation Loc); /// ActOnCXXBoolLiteral - Parse {true,false} literals. diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index a115f185de..4bd4c6f7eb 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -924,10 +924,10 @@ ExprResult Parser::ParseCXXTypeid() { // operand (Clause 5). // // Note that we can't tell whether the expression is an lvalue of a - // polymorphic class type until after we've parsed the expression, so - // we the expression is potentially potentially evaluated. - EnterExpressionEvaluationContext Unevaluated(Actions, - Sema::PotentiallyPotentiallyEvaluated); + // polymorphic class type until after we've parsed the expression; we + // speculatively assume the subexpression is unevaluated, and fix it up + // later. + EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated); Result = ParseExpression(); // Match the ')'. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 1bb4140bd0..0b0b825ed2 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -40,6 +40,7 @@ #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/SemaFixItUtils.h" #include "clang/Sema/Template.h" +#include "TreeTransform.h" using namespace clang; using namespace sema; @@ -1208,10 +1209,6 @@ diagnoseUncapturableValueReference(Sema &S, SourceLocation loc, case Sema::PotentiallyEvaluated: case Sema::PotentiallyEvaluatedIfUsed: break; - - case Sema::PotentiallyPotentiallyEvaluated: - // FIXME: delay these! - break; } // Don't diagnose about capture if we're not actually in code right @@ -9358,38 +9355,53 @@ bool Sema::VerifyIntegerConstantExpression(const Expr *E, llvm::APSInt *Result, return false; } -void Sema::ExpressionEvaluationContextRecord::Destroy() { - delete PotentiallyReferenced; - delete SavedDiag; - delete SavedRuntimeDiag; - delete SavedDelayedDiag; - PotentiallyReferenced = 0; - SavedDiag = 0; - SavedRuntimeDiag = 0; - SavedDelayedDiag = 0; -} +namespace { + // Handle the case where we conclude a expression which we speculatively + // considered to be unevaluated is actually evaluated. + class TransformToPE : public TreeTransform { + typedef TreeTransform BaseTransform; -void Sema::ExpressionEvaluationContextRecord::addDiagnostic( - SourceLocation Loc, const PartialDiagnostic &PD) { - if (!SavedDiag) - SavedDiag = new PotentiallyEmittedDiagnostics; - SavedDiag->push_back(std::make_pair(Loc, PD)); -} + public: + TransformToPE(Sema &SemaRef) : BaseTransform(SemaRef) { } -void Sema::ExpressionEvaluationContextRecord::addRuntimeDiagnostic( - const sema::PossiblyUnreachableDiag &PUD) { - if (!SavedRuntimeDiag) - SavedRuntimeDiag = new PotentiallyEmittedPossiblyUnreachableDiag; - SavedRuntimeDiag->push_back(PUD); -} + // Make sure we redo semantic analysis + bool AlwaysRebuild() { return true; } -void Sema::ExpressionEvaluationContextRecord::addDelayedDiagnostic( - const sema::DelayedDiagnostic &DD) { - if (!SavedDelayedDiag) - SavedDelayedDiag = new PotentiallyEmittedDelayedDiag; - SavedDelayedDiag->push_back(DD); + // We need to special-case DeclRefExprs referring to FieldDecls which + // are not part of a member pointer formation; normal TreeTransforming + // doesn't catch this case because of the way we represent them in the AST. + // FIXME: This is a bit ugly; is it really the best way to handle this + // case? + // + // Error on DeclRefExprs referring to FieldDecls. + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + if (isa(E->getDecl()) && + SemaRef.ExprEvalContexts.back().Context != Sema::Unevaluated) + return SemaRef.Diag(E->getLocation(), + diag::err_invalid_non_static_member_use) + << E->getDecl() << E->getSourceRange(); + + return BaseTransform::TransformDeclRefExpr(E); + } + + // Exception: filter out member pointer formation + ExprResult TransformUnaryOperator(UnaryOperator *E) { + if (E->getOpcode() == UO_AddrOf && E->getType()->isMemberPointerType()) + return E; + + return BaseTransform::TransformUnaryOperator(E); + } + + }; } +ExprResult Sema::TranformToPotentiallyEvaluated(Expr *E) { + ExprEvalContexts.back().Context = + ExprEvalContexts[ExprEvalContexts.size()-2].Context; + if (ExprEvalContexts.back().Context == Unevaluated) + return E; + return TransformToPE(*this).TransformExpr(E); +} void Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext) { @@ -9405,46 +9417,6 @@ void Sema::PopExpressionEvaluationContext() { ExpressionEvaluationContextRecord Rec = ExprEvalContexts.back(); ExprEvalContexts.pop_back(); - if (Rec.Context == PotentiallyPotentiallyEvaluated) { - if (Rec.PotentiallyReferenced) { - // Mark any remaining declarations in the current position of the stack - // as "referenced". If they were not meant to be referenced, semantic - // analysis would have eliminated them (e.g., in ActOnCXXTypeId). - for (PotentiallyReferencedDecls::iterator - I = Rec.PotentiallyReferenced->begin(), - IEnd = Rec.PotentiallyReferenced->end(); - I != IEnd; ++I) - MarkDeclarationReferenced(I->first, I->second); - } - - if (Rec.SavedDiag) { - // Emit any pending diagnostics. - for (PotentiallyEmittedDiagnostics::iterator - I = Rec.SavedDiag->begin(), - IEnd = Rec.SavedDiag->end(); - I != IEnd; ++I) - Diag(I->first, I->second); - } - - if (Rec.SavedDelayedDiag) { - // Emit any pending delayed diagnostics. - for (PotentiallyEmittedDelayedDiag::iterator - I = Rec.SavedDelayedDiag->begin(), - IEnd = Rec.SavedDelayedDiag->end(); - I != IEnd; ++I) - DelayedDiagnostics.add(*I); - } - - if (Rec.SavedRuntimeDiag) { - // Emit any pending runtime diagnostics. - for (PotentiallyEmittedPossiblyUnreachableDiag::iterator - I = Rec.SavedRuntimeDiag->begin(), - IEnd = Rec.SavedRuntimeDiag->end(); - I != IEnd; ++I) - FunctionScopes.back()->PossiblyUnreachableDiags.push_back(*I); - } - } - // When are coming out of an unevaluated context, clear out any // temporaries that we may have created as part of the evaluation of // the expression in that context: they aren't relevant because they @@ -9458,9 +9430,6 @@ void Sema::PopExpressionEvaluationContext() { } else { ExprNeedsCleanups |= Rec.ParentNeedsCleanups; } - - // Destroy the popped expression evaluation record. - Rec.Destroy(); } void Sema::DiscardCleanupsInEvaluationContext() { @@ -9516,13 +9485,6 @@ void Sema::MarkDeclarationReferenced(SourceLocation Loc, Decl *D) { // "used"; handle this below. break; - case PotentiallyPotentiallyEvaluated: - // We are in an expression that may be potentially evaluated; queue this - // declaration reference until we know whether the expression is - // potentially evaluated. - ExprEvalContexts.back().addReferencedDecl(Loc, D); - return; - case PotentiallyEvaluatedIfUsed: // Referenced declarations will only be used if the construct in the // containing expression is used. @@ -9806,11 +9768,6 @@ bool Sema::DiagRuntimeBehavior(SourceLocation Loc, const Stmt *Statement, Diag(Loc, PD); return true; - - case PotentiallyPotentiallyEvaluated: - ExprEvalContexts.back().addRuntimeDiagnostic( - sema::PossiblyUnreachableDiag(PD, Loc, Statement)); - break; } return false; diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index db41f5a3ca..f0b31fd0f1 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -310,7 +310,6 @@ ExprResult Sema::BuildCXXTypeId(QualType TypeInfoType, SourceLocation TypeidLoc, Expr *E, SourceLocation RParenLoc) { - bool isUnevaluatedOperand = true; if (E && !E->isTypeDependent()) { if (E->getType()->isPlaceholderType()) { ExprResult result = CheckPlaceholderExpr(E); @@ -332,7 +331,11 @@ ExprResult Sema::BuildCXXTypeId(QualType TypeInfoType, // polymorphic class type [...] [the] expression is an unevaluated // operand. [...] if (RecordD->isPolymorphic() && E->Classify(Context).isGLValue()) { - isUnevaluatedOperand = false; + // The subexpression is potentially evaluated; switch the context + // and recheck the subexpression. + ExprResult Result = TranformToPotentiallyEvaluated(E); + if (Result.isInvalid()) return ExprError(); + E = Result.take(); // We require a vtable to query the type at run time. MarkVTableUsed(TypeidLoc, RecordD); @@ -352,12 +355,6 @@ ExprResult Sema::BuildCXXTypeId(QualType TypeInfoType, } } - // If this is an unevaluated operand, clear out the set of - // declaration references we have been computing and eliminate any - // temporaries introduced in its computation. - if (isUnevaluatedOperand) - ExprEvalContexts.back().Context = Unevaluated; - return Owned(new (Context) CXXTypeidExpr(TypeInfoType.withConst(), E, SourceRange(TypeidLoc, RParenLoc))); @@ -695,14 +692,7 @@ void Sema::CheckCXXThisCapture(SourceLocation Loc) { continue; } // This context can't implicitly capture 'this'; fail out. - // (We need to delay the diagnostic in the - // PotentiallyPotentiallyEvaluated case because it doesn't apply to - // unevaluated contexts.) - if (ExprEvalContexts.back().Context == PotentiallyPotentiallyEvaluated) - ExprEvalContexts.back() - .addDiagnostic(Loc, PDiag(diag::err_implicit_this_capture)); - else - Diag(Loc, diag::err_implicit_this_capture); + Diag(Loc, diag::err_implicit_this_capture); return; } break; diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp index 6bff219d1e..de35ce2bb3 100644 --- a/lib/Sema/SemaExprMember.cpp +++ b/lib/Sema/SemaExprMember.cpp @@ -74,14 +74,10 @@ enum IMAKind { /// context is not an instance method. IMA_Unresolved_StaticContext, - // The reference is an instance data member access, which is allowed - // because we're in C++11 mode and the context is unevaluated. - IMA_Field_Uneval_StaticContext, - - // The reference is an instance data member access, which may be allowed - // because we're in C++11 mode and the context may be unevaluated - // (i.e. the context is PotentiallyPotentiallyEvaluated). - IMA_Field_PPE_StaticContext, + // The reference refers to a field which is not a member of the containing + // class, which is allowed because we're in C++11 mode and the context is + // unevaluated. + IMA_Field_Uneval_Context, /// All possible referrents are instance members and the current /// context is not an instance method. @@ -158,9 +154,7 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, const Sema::ExpressionEvaluationContextRecord& record = SemaRef.ExprEvalContexts.back(); if (record.Context == Sema::Unevaluated) - return IMA_Field_Uneval_StaticContext; - if (record.Context == Sema::PotentiallyPotentiallyEvaluated) - return IMA_Field_PPE_StaticContext; + return IMA_Field_Uneval_Context; } return IMA_Error_StaticContext; @@ -196,8 +190,7 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, static void DiagnoseInstanceReference(Sema &SemaRef, const CXXScopeSpec &SS, NamedDecl *rep, - const DeclarationNameInfo &nameInfo, - bool DelayPPEDiag = false) { + const DeclarationNameInfo &nameInfo) { SourceLocation Loc = nameInfo.getLoc(); SourceRange Range(Loc); if (SS.isSet()) Range.setBegin(SS.getRange().getBegin()); @@ -206,29 +199,17 @@ static void DiagnoseInstanceReference(Sema &SemaRef, if (CXXMethodDecl *MD = dyn_cast(SemaRef.CurContext)) { if (MD->isStatic()) { // "invalid use of member 'x' in static member function" - if (DelayPPEDiag) - SemaRef.ExprEvalContexts.back().addDiagnostic(Loc, - SemaRef.PDiag(diag::err_invalid_member_use_in_static_method) - << Range << nameInfo.getName()); - else - SemaRef.Diag(Loc, diag::err_invalid_member_use_in_static_method) - << Range << nameInfo.getName(); + SemaRef.Diag(Loc, diag::err_invalid_member_use_in_static_method) + << Range << nameInfo.getName(); return; } } - if (DelayPPEDiag) - SemaRef.ExprEvalContexts.back().addDiagnostic(Loc, - SemaRef.PDiag(diag::err_invalid_non_static_member_use) - << nameInfo.getName() << Range); - else - SemaRef.Diag(Loc, diag::err_invalid_non_static_member_use) - << nameInfo.getName() << Range; + SemaRef.Diag(Loc, diag::err_invalid_non_static_member_use) + << nameInfo.getName() << Range; return; } - assert(!DelayPPEDiag && "Only need to delay diagnostic for fields"); - SemaRef.Diag(Loc, diag::err_member_call_without_object) << Range; } @@ -246,15 +227,10 @@ Sema::BuildPossibleImplicitMemberExpr(const CXXScopeSpec &SS, case IMA_Unresolved: return BuildImplicitMemberExpr(SS, R, TemplateArgs, false); - case IMA_Field_PPE_StaticContext: - DiagnoseInstanceReference(*this, SS, R.getRepresentativeDecl(), - R.getLookupNameInfo(), /*DelayPPEDiag*/true); - // FALL-THROUGH - case IMA_Static: case IMA_Mixed_StaticContext: case IMA_Unresolved_StaticContext: - case IMA_Field_Uneval_StaticContext: + case IMA_Field_Uneval_Context: if (TemplateArgs) return BuildTemplateIdExpr(SS, R, false, *TemplateArgs); return BuildDeclarationNameExpr(SS, R, false); diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index f3582975ec..006cf973d8 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -1057,40 +1057,23 @@ static QualType inferARCLifetimeForPointee(Sema &S, QualType type, } else if (type->isObjCARCImplicitlyUnretainedType()) { implicitLifetime = Qualifiers::OCL_ExplicitNone; - // If we are in an unevaluated context, like sizeof, assume Autoreleasing and - // don't give error. + // If we are in an unevaluated context, like sizeof, skip adding a + // qualification. } else if (S.ExprEvalContexts.back().Context == Sema::Unevaluated || S.ExprEvalContexts.back().Context == Sema::ConstantEvaluated) { - implicitLifetime = Qualifiers::OCL_Autoreleasing; + return type; // If that failed, give an error and recover using __autoreleasing. } else { // These types can show up in private ivars in system headers, so // we need this to not be an error in those cases. Instead we // want to delay. - // - // Also, make sure we delay appropriately in - // PotentiallyPotentiallyEvaluated contexts. if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { - if (S.ExprEvalContexts.back().Context == - Sema::PotentiallyPotentiallyEvaluated) { - S.ExprEvalContexts.back().addDelayedDiagnostic( - sema::DelayedDiagnostic::makeForbiddenType(loc, - diag::err_arc_indirect_no_ownership, type, isReference)); - } else { - S.DelayedDiagnostics.add( - sema::DelayedDiagnostic::makeForbiddenType(loc, - diag::err_arc_indirect_no_ownership, type, isReference)); - } + S.DelayedDiagnostics.add( + sema::DelayedDiagnostic::makeForbiddenType(loc, + diag::err_arc_indirect_no_ownership, type, isReference)); } else { - if (S.ExprEvalContexts.back().Context == - Sema::PotentiallyPotentiallyEvaluated) { - S.ExprEvalContexts.back().addDiagnostic(loc, - S.PDiag(diag::err_arc_indirect_no_ownership) - << type << isReference); - } else { - S.Diag(loc, diag::err_arc_indirect_no_ownership) << type << isReference; - } + S.Diag(loc, diag::err_arc_indirect_no_ownership) << type << isReference; } implicitLifetime = Qualifiers::OCL_Autoreleasing; } diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 58c690d49f..716d9b3f32 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -6878,11 +6878,11 @@ TreeTransform::TransformCXXTypeidExpr(CXXTypeidExpr *E) { E->getLocEnd()); } - // We don't know whether the expression is potentially evaluated until - // after we perform semantic analysis, so the expression is potentially + // We don't know whether the subexpression is potentially evaluated until + // after we perform semantic analysis. We speculatively assume it is + // unevaluated; it will get fixed later if the subexpression is in fact // potentially evaluated. - EnterExpressionEvaluationContext Unevaluated(SemaRef, - Sema::PotentiallyPotentiallyEvaluated); + EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated); ExprResult SubExpr = getDerived().TransformExpr(E->getExprOperand()); if (SubExpr.isInvalid()) diff --git a/test/CXX/expr/expr.prim/expr.prim.general/p12-0x.cpp b/test/CXX/expr/expr.prim/expr.prim.general/p12-0x.cpp index bc052e35b8..59c8660aa1 100644 --- a/test/CXX/expr/expr.prim/expr.prim.general/p12-0x.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.general/p12-0x.cpp @@ -27,3 +27,4 @@ namespace std { class Poly { virtual ~Poly(); }; const std::type_info& k = typeid(S::m); const std::type_info& m = typeid(*(Poly*)S::m); // expected-error {{invalid use of nonstatic data member}} +const std::type_info& n = typeid(*(Poly*)(0*sizeof S::m)); \ No newline at end of file