diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -665,6 +665,14 @@ const LangOptions& getLangOpts() const { return LangOpts; } + // If this condition is false, typo correction must be performed eagerly + // rather than delayed in many places, as it makes use of dependent types. + // the condition is false for clang's C-only codepath, as it doesn't support + // dependent types yet. + bool isDependenceAllowed() const { + return LangOpts.CPlusPlus || LangOpts.RecoveryAST; + } + const SanitizerBlacklist &getSanitizerBlacklist() const { return *SanitizerBL; } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -24,6 +24,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" +#include "clang/AST/OperationKinds.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" @@ -13683,7 +13684,7 @@ CorrectDelayedTyposInBinOp(Sema &S, BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr) { ExprResult LHS = LHSExpr, RHS = RHSExpr; - if (!S.getLangOpts().CPlusPlus) { + if (!S.Context.isDependenceAllowed()) { // C cannot handle TypoExpr nodes on either side of a binop because it // doesn't handle dependent types properly, so make sure any TypoExprs have // been dealt with before checking the operands. @@ -14364,6 +14365,47 @@ return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); } + if (getLangOpts().RecoveryAST && + (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent())) { + assert(!getLangOpts().CPlusPlus); + assert((LHSExpr->containsErrors() || RHSExpr->containsErrors()) && + "Should only occur in error-recovery path."); + if (BinaryOperator::isCompoundAssignmentOp(Opc)) + // C [6.15.16] p3: + // An assignment expression has the value of the left operand after the + // assignment, but is not an lvalue. + return CompoundAssignOperator::Create( + Context, LHSExpr, RHSExpr, Opc, + LHSExpr->getType().getUnqualifiedType(), VK_RValue, OK_Ordinary, + OpLoc, CurFPFeatureOverrides()); + QualType ResultType; + switch (Opc) { + case BO_Assign: + ResultType = LHSExpr->getType().getUnqualifiedType(); + break; + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: + case BO_EQ: + case BO_NE: + case BO_LAnd: + case BO_LOr: + // These operators have a fixed result type regardless of operands. + ResultType = Context.IntTy; + break; + case BO_Comma: + ResultType = RHSExpr->getType(); + break; + default: + ResultType = Context.DependentTy; + break; + } + return BinaryOperator::Create(Context, LHSExpr, RHSExpr, Opc, ResultType, + VK_RValue, OK_Ordinary, OpLoc, + CurFPFeatureOverrides()); + } + // Build a built-in binary operation. return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr); } diff --git a/clang/test/AST/ast-dump-recovery.c b/clang/test/AST/ast-dump-recovery.c --- a/clang/test/AST/ast-dump-recovery.c +++ b/clang/test/AST/ast-dump-recovery.c @@ -42,11 +42,33 @@ void test2() { int* ptr; - // FIXME: the top-level expr should be a binary operator. - // CHECK: ImplicitCastExpr {{.*}} contains-errors - // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors lvalue - // CHECK-NEXT: |-DeclRefExpr {{.*}} 'ptr' 'int *' - // CHECK-NEXT: `-RecoveryExpr {{.*}} - // CHECK-NEXT: `-DeclRefExpr {{.*}} 'some_func' + // CHECK: BinaryOperator {{.*}} 'int *' contains-errors '=' + // CHECK-NEXT: |-DeclRefExpr {{.*}} 'ptr' 'int *' + // CHECK-NEXT: `-RecoveryExpr {{.*}} + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'some_func' ptr = some_func(); // should not crash + + int compoundOp; + // CHECK: CompoundAssignOperator {{.*}} 'int' contains-errors '+=' + // CHECK-NEXT: |-DeclRefExpr {{.*}} 'compoundOp' + // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'some_func' + compoundOp += some_func(); + + // CHECK: BinaryOperator {{.*}} 'int' contains-errors '||' + // CHECK-NEXT: |-RecoveryExpr {{.*}} + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'some_func' + // CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1 + some_func() || 1; + + // CHECK: BinaryOperator {{.*}} '' contains-errors ',' + // CHECK-NEXT: |-IntegerLiteral {{.*}} 'int' 1 + // CHECK-NEXT: `-RecoveryExpr {{.*}} + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'some_func' + 1, some_func(); + // CHECK: BinaryOperator {{.*}} 'int' contains-errors ',' + // CHECK-NEXT: |-RecoveryExpr {{.*}} '' + // CHECK-NEXT: | `-DeclRefExpr {{.*}} 'some_func' + // CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1 + some_func(), 1; } diff --git a/clang/test/Sema/error-dependence.c b/clang/test/Sema/error-dependence.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/error-dependence.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -frecovery-ast -fno-recovery-ast-type %s + +int call(int); // expected-note {{'call' declared here}} + +void test1(int s) { + // verify "assigning to 'int' from incompatible type ''" is + // not emitted. + s = call(); // expected-error {{too few arguments to function call}} +}