[cfe-dev] C++ default arguments patch, rework Parse-Sema interaction for function parameters

Doug Gregor doug.gregor at gmail.com
Tue Apr 8 14:55:59 PDT 2008


On Tue, Apr 8, 2008 at 1:32 AM, Chris Lattner <clattner at apple.com> wrote:
> On Apr 7, 2008, at 6:11 AM, Doug Gregor wrote:
> > That's reasonable. The updated patch contains a new node
> > CXXDefaultArgExpr; most of the changes in this patch just deal with
> > that new node in the statement printer, serialization, code generator,
> > etc.
> >
>
>  Looks great, I like this approach.  Instead of dwelling on details and
> forcing you to keep the patch up to date, I committed it here (after
> resolving a few conflicts that came up in the short time since you sent it
> out!):
>
> http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20080407/005079.html

Thanks! I'm following this up with another patch (attached) that fixes
the remaining nits below. It also implements proper checking for
ill-formed code like

  void f(int x, int y = x);

and, when in non-C99 mode, warns about this as an extension:

  typedef void T;
  void f(T);

>  I'm not sure about that.  clang tracks typedefs (and lack thereof) well
> enough that Sema can decide whether it was spelled "(void)" or "(T)".  Also,
> we do have to handle the "(T)" case for C99.  It seems to me that handling
> this in the parser would require handling it in both the parser and Sema.

Okay. Implemented in Sema, now.

>  +    virtual SourceRange getSourceRange() const {
>  +      return Param->getDefaultArg()->getSourceRange();
>  +    }
>
>  I don't think that this is right.  The source range of the
> CXXDefaultArgExpr should be empty, not the source range of the input.  The
> default argument doesn't occur in the source, so it shouldn't have a
> location.

Okay.

>  +++ lib/AST/ExprCXX.cpp (working copy)
>
>  +// CXXDefaultArgExpr
>  +Stmt::child_iterator CXXDefaultArgExpr::child_begin() {
>  +  return reinterpret_cast<Stmt**>(Param->getDefaultArg());
>  +}
>
>  This is subtly wrong.  The child iteration stuff should walk the expression
> tree in the current function, it shouldn't "jump the gap here".  I suspect
> that -ast-dump will have problems with CXXDefaultArgExpr's due to this.  I'd
> just have CXXDefaultArgExpr just return an empty child range.

Okay, understood.

>
>   +void
>  +Sema::ActOnParamDefaultArgument(DeclTy *param, SourceLocation EqualLoc,
>  +                                ExprTy *defarg) {
>  ...
>  +    // Some default arguments were missing. Clear out all of the
>  +    // default arguments up to (and including) the last missing
>  +    // default argument, so that we leave the function parameters
>  +    // in a semantically valid state.
>  +    for (p = 0; p <= LastMissingDefaultArg; ++p) {
>  +      ParmVarDecl *Param = FD->getParamDecl(p);
>  +      if (Param->getDefaultArg()) {
>  +        delete Param->getDefaultArg();
>  +        Param->setDefaultArg(0);
>  +      }
>  +    }
>
>  Should these ParmVarDecl's be marked as erroneous?  This might be a good
> idea if there are secondary errors that can occur because they are missing
> default initializers.  I can't think of any specific examples off the top of
> my head though, your call.

Secondary errors could occur as the result of a function call that
relies on those default arguments, but there isn't much we can do
about that. I say we just leave the parameters alone: other than not
having default arguments any more, there isn't anything wrong with
them. (Most importantly: the AST is still in a perfectly consistent
state after removing default arguments)

>  +++ lib/Sema/SemaExpr.cpp       (working copy)
>
>    // Make the call expr early, before semantic checks.  This guarantees
> cleanup
>    // of arguments and function on error.
>  -  llvm::OwningPtr<CallExpr> TheCall(new CallExpr(Fn, Args, NumArgs,
>  +  if (getLangOptions().CPlusPlus && FDecl && NumArgs <
> FDecl->getNumParams())
>  +    NumArgsPassed = FDecl->getNumParams();
>  +  llvm::OwningPtr<CallExpr> TheCall(new CallExpr(Fn, Args, NumArgsPassed,
>                                                   Context.BoolTy,
> RParenLoc));
>
>  I don't think this is safe.  The CallExpr ctor reads the range
> [Args,Args+NumArgsPassed), so changing NumArgsPassed in this way makes it
> potentially read beyond the end of the args array.

/me smacks forehead, fixes bug

>
>  +    // If too few arguments are available (and we don't have default
>  +    // arguments for the remaining parameters), don't make the call.
>  +    if (NumArgs < NumArgsInProto) {
>  +      if (getLangOptions().CPlusPlus &&
>  +          FDecl &&
>  +          FDecl->getParamDecl(NumArgs)->getDefaultArg()) {
>  +        // Use default arguments for missing arguments
>  +        NumArgsToCheck = NumArgsInProto;
>  +      } else
>  +        return Diag(RParenLoc, diag::err_typecheck_call_too_few_args,
>  +                    Fn->getSourceRange());
>  +    }
>
>  This logic is reasonable, but what do you think about adding a new method
> to FunctionDecl, something like "unsigned getMinRequiredArguments() const"
> or something like that, which would scan the argument list for default
> arguments.  I think this is a generally useful method, and could simplify
> this down to:
>
>  if (NumArgs < NumArgsInProto) {
>   if (FDecl && NumArgs >= FDecl->getMinRequiredArguments())
>      ...
>
>  which would be simpler :).  It could also be used to simplify the logic
> above.

Sure, although I'm going to have to point out that we're replacing a
constant-time check (is a particular pointer non-NULL) with a loop
that spins through the parameter list :)

  - Doug
-------------- next part --------------
A non-text attachment was scrubbed...
Name: clang-default-arg-fixes.patch
Type: text/x-patch
Size: 16175 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20080408/952a4c52/attachment.bin>


More information about the cfe-dev mailing list