diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -150,6 +150,7 @@ COMPATIBLE_LANGOPT(RecoveryAST, 1, 0, "Preserve expressions in AST when encountering errors") COMPATIBLE_LANGOPT(RecoveryASTType, 1, 0, "Preserve the type in recovery expressions") +COMPATIBLE_LANGOPT(CDependence, 1, 0, "Build dependent AST nodes in C for better error recovery") BENIGN_LANGOPT(ThreadsafeStatics , 1, 1, "thread-safe static initializers") LANGOPT(POSIXThreads , 1, 0, "POSIX thread support") diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4039,6 +4039,10 @@ "(experimental)">; def fno_recovery_ast_type : Flag<["-"], "fno-recovery-ast-type">; +def fc_dependence : Flag<["-"], "fc-dependence">, + HelpText<"Build dependent AST nodes in C for error recovery">; +def fno_c_dependence : Flag<["-"], "fno-c-dependence">; + let Group = Action_Group in { def Eonly : Flag<["-"], "Eonly">, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5163,6 +5163,14 @@ private: static BinaryOperatorKind ConvertTokenKindToBinaryOpcode(tok::TokenKind Kind); + /// Build a dependent binary operator. + /// + /// In C++, we need to perform unqualified lookups when creating a dependent + /// binary operator, thus this should be only used when the unqalified lookup + /// found nothing. + /// results. + ExprResult CreateDependentBinOp(SourceLocation OpLoc, BinaryOperatorKind Opc, + Expr *LHS, Expr *RHS); public: ExprResult ActOnBinOp(Scope *S, SourceLocation TokLoc, diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2922,6 +2922,8 @@ !Args.hasArg(OPT_fno_concept_satisfaction_caching); if (Args.hasArg(OPT_fconcepts_ts)) Diags.Report(diag::warn_fe_concepts_ts_flag); + Opts.CDependence = + Args.hasFlag(OPT_fc_dependence, OPT_fno_c_dependence, false); // Recovery AST still heavily relies on dependent-type machinery. Opts.RecoveryAST = Args.hasFlag(OPT_frecovery_ast, OPT_fno_recovery_ast, Opts.CPlusPlus); 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 @@ -13669,7 +13669,7 @@ CorrectDelayedTyposInBinOp(Sema &S, BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr) { ExprResult LHS = LHSExpr, RHS = RHSExpr; - if (!S.getLangOpts().CPlusPlus) { + if (!S.getLangOpts().CPlusPlus && !S.getLangOpts().CDependence) { // 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. @@ -14247,6 +14247,19 @@ return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS); } +ExprResult Sema::CreateDependentBinOp(SourceLocation OpLoc, + BinaryOperatorKind Opc, Expr *LHS, + Expr *RHS) { + assert(LHS->isTypeDependent() || RHS->isTypeDependent()); + if (Opc <= BO_Assign || Opc > BO_OrAssign) + return BinaryOperator::Create(Context, LHS, RHS, Opc, Context.DependentTy, + VK_RValue, OK_Ordinary, OpLoc, + CurFPFeatureOverrides()); + return CompoundAssignOperator::Create( + Context, LHS, RHS, Opc, Context.DependentTy, VK_LValue, OK_Ordinary, + OpLoc, CurFPFeatureOverrides(), Context.DependentTy, Context.DependentTy); +} + ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr) { @@ -14349,6 +14362,14 @@ return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); } + if (getLangOpts().CDependence && + (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent())) { + assert(!getLangOpts().CPlusPlus); + assert((LHSExpr->containsErrors() || RHSExpr->containsErrors()) && + "Should only occur in error-recovery path."); + return CreateDependentBinOp(OpLoc, Opc, LHSExpr, RHSExpr); + } + // Build a built-in binary operation. return CreateBuiltinBinOp(OpLoc, Opc, LHSExpr, RHSExpr); } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -13303,18 +13303,8 @@ // If either side is type-dependent, create an appropriate dependent // expression. if (Args[0]->isTypeDependent() || Args[1]->isTypeDependent()) { - if (Fns.empty()) { - // If there are no functions to store, just build a dependent - // BinaryOperator or CompoundAssignment. - if (Opc <= BO_Assign || Opc > BO_OrAssign) - return BinaryOperator::Create( - Context, Args[0], Args[1], Opc, Context.DependentTy, VK_RValue, - OK_Ordinary, OpLoc, CurFPFeatureOverrides()); - return CompoundAssignOperator::Create( - Context, Args[0], Args[1], Opc, Context.DependentTy, VK_LValue, - OK_Ordinary, OpLoc, CurFPFeatureOverrides(), Context.DependentTy, - Context.DependentTy); - } + if (Fns.empty()) + return CreateDependentBinOp(OpLoc, Opc, Args[0], Args[1]); // FIXME: save results of ADL from here? CXXRecordDecl *NamingClass = nullptr; // lookup ignores member operators 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 @@ -1,4 +1,4 @@ -// RUN: not %clang_cc1 -triple x86_64-unknown-unknown -frecovery-ast -fno-recovery-ast-type -ast-dump %s | FileCheck -strict-whitespace %s +// RUN: not %clang_cc1 -triple x86_64-unknown-unknown -frecovery-ast -fno-recovery-ast-type -fc-dependence -ast-dump %s | FileCheck -strict-whitespace %s int some_func(int); @@ -42,11 +42,16 @@ 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 {{.*}} 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 {{.*}} '+=' + // CHECK-NEXT: |-DeclRefExpr {{.*}} 'compoundOp' + // CHECK-NEXT: `-RecoveryExpr {{.*}} contains-errors + // CHECK-NEXT: `-DeclRefExpr {{.*}} 'some_func' + compoundOp += some_func(); } 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 -fc-dependence %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}} +}