From: Richard Smith Date: Wed, 6 Nov 2013 19:31:51 +0000 (+0000) Subject: Add a limit to the length of a sequence of 'operator->' functions we will X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=195dd7c8448893e13a0cd8e776520f14cba65b08;p=clang Add a limit to the length of a sequence of 'operator->' functions we will follow when building a class member access expression. Based on a patch by Rahul Jain! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@194161 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/docs/UsersManual.rst b/docs/UsersManual.rst index 4dc8bbfec1..e0a68384ad 100644 --- a/docs/UsersManual.rst +++ b/docs/UsersManual.rst @@ -1299,7 +1299,12 @@ Controlling implementation limits .. option:: -ftemplate-depth=N Sets the limit for recursively nested template instantiations to N. The - default is 1024. + default is 256. + +.. option:: -foperator-arrow-depth=N + + Sets the limit for iterative calls to 'operator->' functions to N. The + default is 256. .. _objc: diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 6289d5a56b..54b74debe7 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -5144,6 +5144,16 @@ def err_capture_block_variable : Error< def err_operator_arrow_circular : Error< "circular pointer delegation detected">; +def err_operator_arrow_depth_exceeded : Error< + "use of 'operator->' on type %0 would invoke a sequence of more than %1 " + "'operator->' calls">; +def note_operator_arrow_here : Note< + "'operator->' declared here produces an object of type %0">; +def note_operator_arrows_suppressed : Note< + "(skipping %0 'operator->'%s0 in backtrace)">; +def note_operator_arrow_depth : Note< + "use -foperator-arrow-depth=N to increase 'operator->' limit">; + def err_pseudo_dtor_base_not_scalar : Error< "object expression of non-scalar type %0 cannot be used in a " "pseudo-destructor expression">; diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def index 72b563dfe3..5a1025cba7 100644 --- a/include/clang/Basic/LangOptions.def +++ b/include/clang/Basic/LangOptions.def @@ -161,6 +161,8 @@ ENUM_LANGOPT(StackProtector, StackProtectorMode, 2, SSPOff, ENUM_LANGOPT(SignedOverflowBehavior, SignedOverflowBehaviorTy, 2, SOB_Undefined, "signed integer overflow handling") +BENIGN_LANGOPT(ArrowDepth, 32, 256, + "maximum number of operator->s to follow") BENIGN_LANGOPT(InstantiationDepth, 32, 256, "maximum template instantiation depth") BENIGN_LANGOPT(ConstexprCallDepth, 32, 512, diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index 6b7dac8e44..85cfdcf394 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -442,6 +442,8 @@ def ftype_visibility : Separate<["-"], "ftype-visibility">, HelpText<"Default type visibility">; def ftemplate_depth : Separate<["-"], "ftemplate-depth">, HelpText<"Maximum depth of recursive template instantiation">; +def foperator_arrow_depth : Separate<["-"], "foperator-arrow-depth">, + HelpText<"Maximum number of 'operator->'s to call for a member access">; def fconstexpr_depth : Separate<["-"], "fconstexpr-depth">, HelpText<"Maximum depth of recursive constexpr function calls">; def fconstexpr_steps : Separate<["-"], "fconstexpr-steps">, diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index cc2de9f914..faaaee1e93 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -776,6 +776,8 @@ def ftemplate_depth_EQ : Joined<["-"], "ftemplate-depth=">, Group; def ftemplate_depth_ : Joined<["-"], "ftemplate-depth-">, Group; def ftemplate_backtrace_limit_EQ : Joined<["-"], "ftemplate-backtrace-limit=">, Group; +def foperator_arrow_depth_EQ : Joined<["-"], "foperator-arrow-depth=">, + Group; def ftest_coverage : Flag<["-"], "ftest-coverage">, Group; def fvectorize : Flag<["-"], "fvectorize">, Group, HelpText<"Enable the loop vectorization passes">; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 5ac2268ee3..7c987191d8 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -2832,6 +2832,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(A->getValue()); } + if (Arg *A = Args.getLastArg(options::OPT_foperator_arrow_depth_EQ)) { + CmdArgs.push_back("-foperator-arrow-depth"); + CmdArgs.push_back(A->getValue()); + } + if (Arg *A = Args.getLastArg(options::OPT_fconstexpr_depth_EQ)) { CmdArgs.push_back("-fconstexpr-depth"); CmdArgs.push_back(A->getValue()); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index d419d8a9ea..c1999e84c4 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1322,6 +1322,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.MathErrno = !Opts.OpenCL && Args.hasArg(OPT_fmath_errno); Opts.InstantiationDepth = getLastArgIntValue(Args, OPT_ftemplate_depth, 256, Diags); + Opts.ArrowDepth = + getLastArgIntValue(Args, OPT_foperator_arrow_depth, 256, Diags); Opts.ConstexprCallDepth = getLastArgIntValue(Args, OPT_fconstexpr_depth, 512, Diags); Opts.ConstexprStepLimit = diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 6f72d6598e..2e71270b4e 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -5149,6 +5149,32 @@ ExprResult Sema::ActOnDecltypeExpression(Expr *E) { return Owned(E); } +/// Note a set of 'operator->' functions that were used for a member access. +static void noteOperatorArrows(Sema &S, + llvm::ArrayRef OperatorArrows) { + unsigned SkipStart = OperatorArrows.size(), SkipCount = 0; + // FIXME: Make this configurable? + unsigned Limit = 9; + if (OperatorArrows.size() > Limit) { + // Produce Limit-1 normal notes and one 'skipping' note. + SkipStart = (Limit - 1) / 2 + (Limit - 1) % 2; + SkipCount = OperatorArrows.size() - (Limit - 1); + } + + for (unsigned I = 0; I < OperatorArrows.size(); /**/) { + if (I == SkipStart) { + S.Diag(OperatorArrows[I]->getLocation(), + diag::note_operator_arrows_suppressed) + << SkipCount; + I += SkipCount; + } else { + S.Diag(OperatorArrows[I]->getLocation(), diag::note_operator_arrow_here) + << OperatorArrows[I]->getCallResultType(); + ++I; + } + } +} + ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SourceLocation OpLoc, tok::TokenKind OpKind, ParsedType &ObjectType, @@ -5181,15 +5207,25 @@ Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SourceLocation OpLoc, // [...] When operator->returns, the operator-> is applied to the value // returned, with the original second operand. if (OpKind == tok::arrow) { + QualType StartingType = BaseType; bool NoArrowOperatorFound = false; bool FirstIteration = true; FunctionDecl *CurFD = dyn_cast(CurContext); // The set of types we've considered so far. llvm::SmallPtrSet CTypes; - SmallVector Locations; + SmallVector OperatorArrows; CTypes.insert(Context.getCanonicalType(BaseType)); while (BaseType->isRecordType()) { + if (OperatorArrows.size() >= getLangOpts().ArrowDepth) { + Diag(OpLoc, diag::err_operator_arrow_depth_exceeded) + << BaseType << getLangOpts().ArrowDepth << Base->getSourceRange(); + noteOperatorArrows(*this, OperatorArrows); + Diag(OpLoc, diag::note_operator_arrow_depth) + << getLangOpts().ArrowDepth; + return ExprError(); + } + Result = BuildOverloadedArrowExpr( S, Base, OpLoc, // When in a template specialization and on the first loop iteration, @@ -5203,7 +5239,7 @@ Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SourceLocation OpLoc, if (NoArrowOperatorFound) { if (FirstIteration) { Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) - << BaseType << 1 << Base->getSourceRange() + << StartingType << 1 << Base->getSourceRange() << FixItHint::CreateReplacement(OpLoc, "."); OpKind = tok::period; break; @@ -5220,13 +5256,12 @@ Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SourceLocation OpLoc, } Base = Result.get(); if (CXXOperatorCallExpr *OpCall = dyn_cast(Base)) - Locations.push_back(OpCall->getDirectCallee()->getLocation()); + OperatorArrows.push_back(OpCall->getDirectCallee()); BaseType = Base->getType(); CanQualType CBaseType = Context.getCanonicalType(BaseType); if (!CTypes.insert(CBaseType)) { - Diag(OpLoc, diag::err_operator_arrow_circular); - for (unsigned i = 0; i < Locations.size(); i++) - Diag(Locations[i], diag::note_declared_at); + Diag(OpLoc, diag::err_operator_arrow_circular) << StartingType; + noteOperatorArrows(*this, OperatorArrows); return ExprError(); } FirstIteration = false; diff --git a/test/SemaCXX/operator-arrow-depth.cpp b/test/SemaCXX/operator-arrow-depth.cpp new file mode 100644 index 0000000000..4e098823ae --- /dev/null +++ b/test/SemaCXX/operator-arrow-depth.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -DMAX=128 -foperator-arrow-depth 128 +// RUN: %clang_cc1 -fsyntax-only -verify %s -DMAX=2 -foperator-arrow-depth 2 +// RUN: %clang -fsyntax-only -Xclang -verify %s -DMAX=10 -foperator-arrow-depth=10 + +template struct B; +template struct A { + B operator->(); // expected-note +{{'operator->' declared here produces an object of type 'B<}} +}; +template struct B { + A operator->(); // expected-note +{{'operator->' declared here produces an object of type 'A<}} +#if MAX != 2 + // expected-note-re@-2 {{(skipping (120|2) 'operator->'s in backtrace)}} +#endif +}; + +struct X { int n; }; +template<> struct B<1> { + X *operator->(); +}; + +A good; +int n = good->n; + +B bad; +int m = bad->n; // expected-error-re {{use of 'operator->' on type 'B<(1|5|64)>' would invoke a sequence of more than (2|10|128) 'operator->' calls}} + // expected-note@-1 {{use -foperator-arrow-depth=N to increase 'operator->' limit}}