diff --git a/clang/include/clang/AST/DependenceFlags.h b/clang/include/clang/AST/DependenceFlags.h --- a/clang/include/clang/AST/DependenceFlags.h +++ b/clang/include/clang/AST/DependenceFlags.h @@ -16,8 +16,17 @@ struct ExprDependenceScope { enum ExprDependence : uint8_t { UnexpandedPack = 1, + // This expr depends in any way on + // - a template parameter, it implies that the resolution of this expr may + // cause instantiation to fail; + // - or an error (usually in a non-template context); + // + // Note that C++ standard doesn't define the instantiation-dependent term, + // we follow the formal definition coming from the Itanium C++ ABI. Instantiation = 2, + // The type of this expr depends on a template parameter, or an error. Type = 4, + // The value of this expr depends on a template parameter, or an error. Value = 8, // clang extension: this expr contains or references an error, and is @@ -42,10 +51,15 @@ /// Whether this type contains an unexpanded parameter pack /// (for C++11 variadic templates) UnexpandedPack = 1, - /// Whether this type somehow involves a template parameter, even - /// if the resolution of the type does not depend on a template parameter. + /// Whether this type somehow involves + /// - a template parameter, evenif the resolution of the type does not + /// depend on a template parameter. + /// - or an error. Instantiation = 2, - /// Whether this type is a dependent type (C++ [temp.dep.type]). + /// Whether this type + /// - is a dependent type (C++ [temp.dep.type]) + /// - or it somehow involves an error, e.g. denoted by + /// decltype(recovery-expr) where recovery-expr is contains-errors. Dependent = 4, /// Whether this type is a variably-modified type (C99 6.7.5). VariablyModified = 8, @@ -96,15 +110,15 @@ // Contains a template parameter pack that wasn't expanded. UnexpandedPack = 1, // Uses a template parameter, even if it doesn't affect the result. - // Validity depends on the template parameter. + // Validity depends on the template parameter, or an error. Instantiation = 2, - // Expression type depends on template context. + // Expression type depends on template context, or an error. // Value and Instantiation should also be set. Type = 4, - // Expression value depends on template context. + // Expression value depends on template context, or an error. // Instantiation should also be set. Value = 8, - // Depends on template context. + // Depends on template context, or an error. // The type/value distinction is only meaningful for expressions. Dependent = Type | Value, // Includes an error, and depends on how it is resolved. diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -157,9 +157,11 @@ return static_cast(ExprBits.Dependent); } - /// isValueDependent - Determines whether this expression is - /// value-dependent (C++ [temp.dep.constexpr]). For example, the - /// array bound of "Chars" in the following example is + /// Determines whether the value of this expression depends on + /// - a template parameter (C++ [temp.dep.constexpr]) + /// - or an error + /// + /// For example, the array bound of "Chars" in the following example is /// value-dependent. /// @code /// template struct meta_string; @@ -168,10 +170,12 @@ return static_cast(getDependence() & ExprDependence::Value); } - /// isTypeDependent - Determines whether this expression is - /// type-dependent (C++ [temp.dep.expr]), which means that its type - /// could change from one template instantiation to the next. For - /// example, the expressions "x" and "x + y" are type-dependent in + /// Determines whether the type of this expression depends on + /// - a template paramter (C++ [temp.dep.expr], which means that its type + /// could change from one template instantiation to the next) + /// - or an error + /// + /// For example, the expressions "x" and "x + y" are type-dependent in /// the following code, but "y" is not type-dependent: /// @code /// template @@ -184,8 +188,10 @@ } /// Whether this expression is instantiation-dependent, meaning that - /// it depends in some way on a template parameter, even if neither its type - /// nor (constant) value can change due to the template instantiation. + /// it depends in some way on + /// - a template parameter (even if neither its type nor (constant) value + /// can change due to the template instantiation) + /// - or an error /// /// In the following example, the expression \c sizeof(sizeof(T() + T())) is /// instantiation-dependent (since it involves a template parameter \c T), but @@ -200,6 +206,12 @@ /// } /// \endcode /// + /// \code + /// void func(int) { + /// func(); // the expression is instantiation-dependent, because it depends + /// // on an error. + /// } + /// \endcode bool isInstantiationDependent() const { return static_cast(getDependence() & ExprDependence::Instantiation); } @@ -6212,19 +6224,22 @@ /// subexpressions of some expression that we could not construct and source /// range covered by the expression. /// -/// By default, RecoveryExpr is type-, value- and instantiation-dependent to -/// take advantage of existing machinery to deal with dependent code in C++, -/// e.g. RecoveryExpr is preserved in `decltype()` as part of the -/// `DependentDecltypeType`. In addition to that, clang does not report most -/// errors on dependent expressions, so we get rid of bogus errors for free. -/// However, note that unlike other dependent expressions, RecoveryExpr can be -/// produced in non-template contexts. -/// In addition, we will preserve the type in RecoveryExpr when the type is -/// known, e.g. preserving the return type for a broken non-overloaded function -/// call, a overloaded call where all candidates have the same return type. +/// By default, RecoveryExpr uses dependence-bits to take advantage of existing +/// machinery to deal with dependent code in C++, e.g. RecoveryExpr is preserved +/// in `decltype()` as part of the `DependentDecltypeType`. In +/// addition to that, clang does not report most errors on dependent +/// expressions, so we get rid of bogus errors for free. However, note that +/// unlike other dependent expressions, RecoveryExpr can be produced in +/// non-template contexts. In addition, we will preserve the type in +/// RecoveryExpr when the type is known, e.g. preserving the return type for a +/// broken non-overloaded function call, a overloaded call where all candidates +/// have the same return type. /// /// One can also reliably suppress all bogus errors on expressions containing /// recovery expressions by examining results of Expr::containsErrors(). +/// +/// FIXME: RecoveryExpr is currently generated by default in C++ mode only, as +/// dependence isn't handled properly on several C-only codepaths. class RecoveryExpr final : public Expr, private llvm::TrailingObjects { public: diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -495,11 +495,11 @@ } ExprDependence clang::computeDependence(RecoveryExpr *E) { - // Mark the expression as value- and instantiation- dependent to reuse - // existing suppressions for dependent code, e.g. avoiding - // constant-evaluation. - // FIXME: drop type+value+instantiation once Error is sufficient to suppress - // bogus dianostics. + // RecoveryExpr is + // - always value-dependent, instantiation-dependent and contains-errors; + // - type-dependent if we don't know the type (fallback to an opequa + // dependent type), or the type is known and dependent, or it has + // type-dependent subexpressions; auto D = toExprDependence(E->getType()->getDependence()) | ExprDependence::ValueInstantiation | ExprDependence::Error; for (auto *S : E->subExpressions()) 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 @@ -19210,9 +19210,6 @@ ExprResult Sema::CreateRecoveryExpr(SourceLocation Begin, SourceLocation End, ArrayRef SubExprs, QualType T) { - // FIXME: enable it for C++, RecoveryExpr is type-dependent to suppress - // bogus diagnostics and this trick does not work in C. - // FIXME: use containsErrors() to suppress unwanted diags in C. if (!Context.getLangOpts().RecoveryAST) return ExprError();