This is an archive of the discontinued LLVM Phabricator instance.

[AST][RecoveryAST] Preserve type for member call expr if argments are not matched.
ClosedPublic

Authored by hokein on Nov 29 2020, 11:48 PM.

Diff Detail

Event Timeline

hokein created this revision.Nov 29 2020, 11:48 PM
Herald added a project: Restricted Project. · View Herald TranscriptNov 29 2020, 11:48 PM
hokein requested review of this revision.Nov 29 2020, 11:48 PM

The thrust of this change is great, I'm just having trouble being sure there are no bad side-effects.


I almost left a comment here saying we might want to recover here... before noticing that we recover all the way up at ParsePostfixExpressionSuffix().

It may be worth a comment on the function implementation that RecoveryExpr fallback is done at a higher level.
But on this note, I think we have to consider the effect on *other* callsites.
For example Sema::tryExprAsCall checks if the result is non-null in order to produce "function must be callled, did you mean to call with no arguments"... but now I think we're returning non-null even if args are required. The fix there is to check containsErrors too.

There are a bunch of "internal" uses to Sema::BuildCallExpr (which calls this function), like coroutines, decomposition, pseudo-object etc that might need to be checked.

Finally BuildRecoveryCallExpr in SemaOverload itself calls BuildCallExpr, and maybe we can end up with a RecoveryExpr wrapping a RecoveryExpr.


I'm not sure what the most principled thing to do here is. Recovering (only) at a higher level seems sensible, but then we lack a way to propagate the type. Checking for containsErrors() where needed seems fragile. Adding a new state to ExprResult (null/error/Expr/QualType) is a pretty big change that may not be useful in general.

I'm not opposed to this patch if we carefully audit all callers but this doesn't feel great. Maybe an ugly "allowRecovery" flag to BuildCallExpr is the right compromise, at least it forces us to be explicit.

hokein updated this revision to Diff 310450.Dec 9 2020, 1:15 AM

address comments

  • add a allowRecovery flag to avoid bad side-effect on other callsides;
  • recover more cases;
hokein added a comment.Dec 9 2020, 1:22 AM

The thrust of this change is great, I'm just having trouble being sure there are no bad side-effects.


I almost left a comment here saying we might want to recover here... before noticing that we recover all the way up at ParsePostfixExpressionSuffix().

It may be worth a comment on the function implementation that RecoveryExpr fallback is done at a higher level.
But on this note, I think we have to consider the effect on *other* callsites.
For example Sema::tryExprAsCall checks if the result is non-null in order to produce "function must be callled, did you mean to call with no arguments"... but now I think we're returning non-null even if args are required. The fix there is to check containsErrors too.

There are a bunch of "internal" uses to Sema::BuildCallExpr (which calls this function), like coroutines, decomposition, pseudo-object etc that might need to be checked.

Finally BuildRecoveryCallExpr in SemaOverload itself calls BuildCallExpr, and maybe we can end up with a RecoveryExpr wrapping a RecoveryExpr.


I'm not sure what the most principled thing to do here is. Recovering (only) at a higher level seems sensible, but then we lack a way to propagate the type. Checking for containsErrors() where needed seems fragile. Adding a new state to ExprResult (null/error/Expr/QualType) is a pretty big change that may not be useful in general.

I'm not opposed to this patch if we carefully audit all callers but this doesn't feel great. Maybe an ugly "allowRecovery" flag to BuildCallExpr is the right compromise, at least it forces us to be explicit.

This is a good point, thanks for raising it.

Recovering (only) at a higher level seems sensible, but then we lack a way to propagate the type.

This looks reasonable. Currently we already perform the recovery when a CallExpr node is failed to build (in ParseExpr.cpp), but the type is always dependent. We could add some heuristics there to propagate the type as the Fn is known (but we might end up with duplicated logics as those in SemaOverload.cpp)

so yeah, adding a flag to BuildCallExpr and other related methods seems like a right compromise, it doesn't hurt the existing diagnostics while allowing us to preserve the type, and https://reviews.llvm.org/D80109 also needs that.

sammccall accepted this revision.Dec 9 2020, 4:56 AM

Some case errors on the allowRecovery variable name throughout.

clang/include/clang/Sema/Sema.h
3730

nit: AllowRecovery (case)

5242

(and here)

clang/lib/Sema/SemaExpr.cpp
6313

/*IsExecConfig=*/false, /*AllowRecovery=*/true

clang/lib/Sema/SemaOverload.cpp
14218

This would be a good place to add a comment
// We only try to build a recovery expr at this level if we can preserve the return type, otherwise we return ExprError() and let the caller recover.

clang/test/AST/ast-dump-recovery.cpp
45

Is this a leftover?

This revision is now accepted and ready to land.Dec 9 2020, 4:56 AM

(It seems like there are probably other callsites to these functions that could be updated to pass AllowRecovery=true. If so, those can easily be separate patches of course)

hokein updated this revision to Diff 311142.Dec 11 2020, 1:27 AM
hokein marked an inline comment as done.

address comments.

This revision was landed with ongoing or failed builds.Dec 11 2020, 1:38 AM
This revision was automatically updated to reflect the committed changes.
hokein marked 4 inline comments as done.