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,18 @@ 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 (often 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, and + // extend it to errors. 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 +52,14 @@ /// 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, even if 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. decltype(recovery-expr) Dependent = 4, /// Whether this type is a variably-modified type (C99 6.7.5). VariablyModified = 8, @@ -95,16 +109,17 @@ // 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. + // Depends on a template parameter or an error in some way. + // Validity depends on how the template is instantiated or the error is + // resolved. 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, whose resolution is unknown + /// + /// 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,25 @@ /// 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. +/// +/// 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. In this +/// case, the expression is not type-dependent (unless the known type is itself +/// dependent) /// /// 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,13 +495,16 @@ } 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, and therefore instantiation dependent + // - contains errors (ExprDependence::Error), by definition + // - type-dependent if we don't know the type (fallback to an opaque + // 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; + // FIXME: remove the type-dependent bit from subexpressions, if the + // RecoveryExpr has a non-dependent type. for (auto *S : E->subExpressions()) D |= S->getDependence(); return D; 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 @@ -19180,9 +19180,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();