aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clang/docs/ReleaseNotes.rst4
-rw-r--r--clang/include/clang/AST/ASTLambda.h15
-rw-r--r--clang/include/clang/AST/Decl.h32
-rw-r--r--clang/include/clang/AST/DeclCXX.h24
-rw-r--r--clang/include/clang/AST/Expr.h10
-rw-r--r--clang/include/clang/AST/Stmt.h1
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td86
-rw-r--r--clang/include/clang/Sema/DeclSpec.h2
-rw-r--r--clang/include/clang/Sema/ScopeInfo.h4
-rw-r--r--clang/include/clang/Sema/Sema.h87
-rw-r--r--clang/lib/AST/ASTImporter.cpp2
-rw-r--r--clang/lib/AST/ASTStructuralEquivalence.cpp2
-rw-r--r--clang/lib/AST/ComputeDependence.cpp9
-rw-r--r--clang/lib/AST/Decl.cpp14
-rw-r--r--clang/lib/AST/DeclCXX.cpp46
-rw-r--r--clang/lib/AST/DeclPrinter.cpp4
-rw-r--r--clang/lib/AST/Expr.cpp2
-rw-r--r--clang/lib/AST/ExprClassification.cpp18
-rw-r--r--clang/lib/AST/ExprConstant.cpp30
-rw-r--r--clang/lib/AST/Interp/ByteCodeEmitter.cpp2
-rw-r--r--clang/lib/AST/ItaniumMangle.cpp4
-rw-r--r--clang/lib/AST/JSONNodeDumper.cpp3
-rw-r--r--clang/lib/AST/Mangle.cpp2
-rw-r--r--clang/lib/AST/MicrosoftMangle.cpp12
-rw-r--r--clang/lib/AST/TextNodeDumper.cpp4
-rw-r--r--clang/lib/Analysis/Consumed.cpp4
-rw-r--r--clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp4
-rw-r--r--clang/lib/Analysis/FlowSensitive/Transfer.cpp4
-rw-r--r--clang/lib/Analysis/ThreadSafetyCommon.cpp4
-rw-r--r--clang/lib/CodeGen/CGCall.cpp4
-rw-r--r--clang/lib/CodeGen/CGClass.cpp12
-rw-r--r--clang/lib/CodeGen/CGDebugInfo.cpp4
-rw-r--r--clang/lib/CodeGen/CGExpr.cpp47
-rw-r--r--clang/lib/CodeGen/CGExprCXX.cpp11
-rw-r--r--clang/lib/CodeGen/CGOpenMPRuntime.cpp2
-rw-r--r--clang/lib/CodeGen/CGVTables.cpp2
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.cpp17
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.h2
-rw-r--r--clang/lib/CodeGen/CodeGenModule.cpp4
-rw-r--r--clang/lib/Parse/ParseDecl.cpp38
-rw-r--r--clang/lib/Parse/ParseTentative.cpp11
-rw-r--r--clang/lib/Sema/DeclSpec.cpp12
-rw-r--r--clang/lib/Sema/HLSLExternalSemaSource.cpp10
-rw-r--r--clang/lib/Sema/ScopeInfo.cpp8
-rw-r--r--clang/lib/Sema/SemaChecking.cpp49
-rw-r--r--clang/lib/Sema/SemaCoroutine.cpp16
-rw-r--r--clang/lib/Sema/SemaDecl.cpp57
-rw-r--r--clang/lib/Sema/SemaDeclAttr.cpp12
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp283
-rw-r--r--clang/lib/Sema/SemaExceptionSpec.cpp64
-rw-r--r--clang/lib/Sema/SemaExpr.cpp161
-rw-r--r--clang/lib/Sema/SemaExprCXX.cpp98
-rw-r--r--clang/lib/Sema/SemaExprMember.cpp65
-rw-r--r--clang/lib/Sema/SemaExprObjC.cpp5
-rw-r--r--clang/lib/Sema/SemaInit.cpp7
-rw-r--r--clang/lib/Sema/SemaLambda.cpp44
-rw-r--r--clang/lib/Sema/SemaOverload.cpp899
-rw-r--r--clang/lib/Sema/SemaStmt.cpp2
-rw-r--r--clang/lib/Sema/SemaTemplate.cpp17
-rw-r--r--clang/lib/Sema/SemaTemplateDeduction.cpp140
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiate.cpp2
-rw-r--r--clang/lib/Sema/SemaTemplateInstantiateDecl.cpp2
-rw-r--r--clang/lib/Sema/SemaType.cpp34
-rw-r--r--clang/lib/Serialization/ASTReaderDecl.cpp1
-rw-r--r--clang/lib/Serialization/ASTReaderStmt.cpp1
-rw-r--r--clang/lib/Serialization/ASTWriterDecl.cpp25
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngine.cpp6
-rw-r--r--clang/lib/StaticAnalyzer/Core/LoopWidening.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/SValBuilder.cpp2
-rw-r--r--clang/test/Analysis/cxx2b-deducing-this.cpp62
-rw-r--r--clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p1.cpp6
-rw-r--r--clang/test/CXX/drs/dr25xx.cpp57
-rw-r--r--clang/test/CXX/drs/dr26xx.cpp28
-rw-r--r--clang/test/CXX/drs/dr5xx.cpp22
-rw-r--r--clang/test/CXX/special/class.copy/p25-0x.cpp48
-rw-r--r--clang/test/CodeGenCXX/cxx2b-deducing-this-cc.cpp40
-rw-r--r--clang/test/CodeGenCXX/cxx2b-deducing-this.cpp111
-rw-r--r--clang/test/CodeGenCXX/cxx2b-mangle-deducing-this.cpp64
-rw-r--r--clang/test/CodeGenCXX/microsoft-abi-explicit-object-parameters.cpp70
-rw-r--r--clang/test/FixIt/fixit-deducing-this.cpp32
-rw-r--r--clang/test/SemaCXX/cxx2b-deducing-this-compat.cpp9
-rw-r--r--clang/test/SemaCXX/cxx2b-deducing-this-constexpr.cpp56
-rw-r--r--clang/test/SemaCXX/cxx2b-deducing-this-coro.cpp57
-rw-r--r--clang/test/SemaCXX/cxx2b-deducing-this.cpp544
-rwxr-xr-xclang/www/cxx_dr_status.html10
-rwxr-xr-xclang/www/cxx_status.html2
86 files changed, 3028 insertions, 803 deletions
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8f5a67e..df4299d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -91,6 +91,10 @@ C++20 Feature Support
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
+- Implemented `P0847R7: Deducing this <https://wg21.link/P0847R7>`_. Some related core issues were also
+ implemented (`CWG2553 <https://wg21.link/CWG2553>`_, `CWG2554 <https://wg21.link/CWG2554>`_,
+ `CWG2653 <https://wg21.link/CWG2653>`_, `CWG2687 <https://wg21.link/CWG2687>`_). Because the
+ support for this feature is still experimental, the feature test macro ``__cpp_explicit_this_parameter``
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/ASTLambda.h b/clang/include/clang/AST/ASTLambda.h
index 230e0c8..646cb57 100644
--- a/clang/include/clang/AST/ASTLambda.h
+++ b/clang/include/clang/AST/ASTLambda.h
@@ -35,6 +35,21 @@ inline bool isLambdaCallOperator(const DeclContext *DC) {
return isLambdaCallOperator(cast<CXXMethodDecl>(DC));
}
+inline bool isLambdaCallWithExplicitObjectParameter(const DeclContext *DC) {
+ return isLambdaCallOperator(DC) &&
+ cast<CXXMethodDecl>(DC)->isExplicitObjectMemberFunction();
+}
+
+inline bool isLambdaCallWithImplicitObjectParameter(const DeclContext *DC) {
+ return isLambdaCallOperator(DC) &&
+ // FIXME: Checking for a null type is not great
+ // but lambdas with invalid captures or whose closure parameter list
+ // have not fully been parsed may have a call operator whose type is
+ // null.
+ !cast<CXXMethodDecl>(DC)->getType().isNull() &&
+ !cast<CXXMethodDecl>(DC)->isExplicitObjectMemberFunction();
+}
+
inline bool isGenericLambdaCallOperatorSpecialization(const CXXMethodDecl *MD) {
if (!MD) return false;
const CXXRecordDecl *LambdaClass = MD->getParent();
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index c51b33c..3b1e3ab 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1809,6 +1809,18 @@ public:
ParmVarDeclBits.IsKNRPromoted = promoted;
}
+ bool isExplicitObjectParameter() const {
+ return ExplicitObjectParameterIntroducerLoc.isValid();
+ }
+
+ void setExplicitObjectParameterLoc(SourceLocation Loc) {
+ ExplicitObjectParameterIntroducerLoc = Loc;
+ }
+
+ SourceLocation getExplicitObjectParamThisLoc() const {
+ return ExplicitObjectParameterIntroducerLoc;
+ }
+
Expr *getDefaultArg();
const Expr *getDefaultArg() const {
return const_cast<ParmVarDecl *>(this)->getDefaultArg();
@@ -1875,7 +1887,10 @@ public:
static bool classofKind(Kind K) { return K == ParmVar; }
private:
+ friend class ASTDeclReader;
+
enum { ParameterIndexSentinel = (1 << NumParameterIndexBits) - 1 };
+ SourceLocation ExplicitObjectParameterIntroducerLoc;
void setParameterIndex(unsigned parameterIndex) {
if (parameterIndex >= ParameterIndexSentinel) {
@@ -2640,6 +2655,23 @@ public:
/// parameters have default arguments (in C++).
unsigned getMinRequiredArguments() const;
+ /// Returns the minimum number of non-object arguments needed to call this
+ /// function. This produces the same value as getMinRequiredArguments except
+ /// it does not count the explicit object argument, if any.
+ unsigned getMinRequiredExplicitArguments() const;
+
+ bool hasCXXExplicitFunctionObjectParameter() const;
+
+ unsigned getNumNonObjectParams() const;
+
+ const ParmVarDecl *getNonObjectParameter(unsigned I) const {
+ return getParamDecl(hasCXXExplicitFunctionObjectParameter() ? I + 1 : I);
+ }
+
+ ParmVarDecl *getNonObjectParameter(unsigned I) {
+ return getParamDecl(hasCXXExplicitFunctionObjectParameter() ? I + 1 : I);
+ }
+
/// Determine whether this function has a single parameter, or multiple
/// parameters where all but the first have default arguments.
///
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index afec815..aa3e332 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -2061,6 +2061,17 @@ public:
bool isStatic() const;
bool isInstance() const { return !isStatic(); }
+ /// [C++2b][dcl.fct]/p7
+ /// An explicit object member function is a non-static
+ /// member function with an explicit object parameter. e.g.,
+ /// void func(this SomeType);
+ bool isExplicitObjectMemberFunction() const;
+
+ /// [C++2b][dcl.fct]/p7
+ /// An implicit object member function is a non-static
+ /// member function without an explicit object parameter.
+ bool isImplicitObjectMemberFunction() const;
+
/// Returns true if the given operator is implicitly static in a record
/// context.
static bool isStaticOverloadedOperator(OverloadedOperatorKind OOK) {
@@ -2169,14 +2180,19 @@ public:
/// Return the type of the object pointed by \c this.
///
/// See getThisType() for usage restriction.
- QualType getThisObjectType() const;
+
+ QualType getFunctionObjectParameterReferenceType() const;
+ QualType getFunctionObjectParameterType() const {
+ return getFunctionObjectParameterReferenceType().getNonReferenceType();
+ }
+
+ unsigned getNumExplicitParams() const {
+ return getNumParams() - (isExplicitObjectMemberFunction() ? 1 : 0);
+ }
static QualType getThisType(const FunctionProtoType *FPT,
const CXXRecordDecl *Decl);
- static QualType getThisObjectType(const FunctionProtoType *FPT,
- const CXXRecordDecl *Decl);
-
Qualifiers getMethodQualifiers() const {
return getType()->castAs<FunctionProtoType>()->getMethodQuals();
}
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index fcda738..b69c616 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -1449,6 +1449,16 @@ public:
DeclRefExprBits.IsImmediateEscalating = Set;
}
+ bool isCapturedByCopyInLambdaWithExplicitObjectParameter() const {
+ return DeclRefExprBits.CapturedByCopyInLambdaWithExplicitObjectParameter;
+ }
+
+ void setCapturedByCopyInLambdaWithExplicitObjectParameter(
+ bool Set, const ASTContext &Context) {
+ DeclRefExprBits.CapturedByCopyInLambdaWithExplicitObjectParameter = Set;
+ setDependence(computeDependence(this, Context));
+ }
+
static bool classof(const Stmt *T) {
return T->getStmtClass() == DeclRefExprClass;
}
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 87ffebc..69a7644 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -383,6 +383,7 @@ protected:
unsigned HasFoundDecl : 1;
unsigned HadMultipleCandidates : 1;
unsigned RefersToEnclosingVariableOrCapture : 1;
+ unsigned CapturedByCopyInLambdaWithExplicitObjectParameter : 1;
unsigned NonOdrUseReason : 2;
unsigned IsImmediateEscalating : 1;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 29362df..8c3815b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4681,12 +4681,14 @@ def note_ovl_candidate_non_deduced_mismatch_qualified : Note<
// Note that we don't treat templates differently for this diagnostic.
def note_ovl_candidate_arity : Note<"candidate "
"%sub{select_ovl_candidate_kind}0,1,2 not viable: "
- "requires%select{ at least| at most|}3 %4 argument%s4, but %5 "
+ "requires%select{ at least| at most|}3 %4 "
+ "%select{|non-object }6argument%s4, but %5 "
"%plural{1:was|:were}5 provided">;
def note_ovl_candidate_arity_one : Note<"candidate "
"%sub{select_ovl_candidate_kind}0,1,2 not viable: "
"%select{requires at least|allows at most single|requires single}3 "
+ "%select{|non-object }6"
"argument %4, but %plural{0:no|:%5}5 arguments were provided">;
def note_ovl_candidate_deleted : Note<
@@ -4869,7 +4871,7 @@ def err_ovl_deleted_object_call : Error<
"call to deleted function call operator in type %0">;
def note_ovl_surrogate_cand : Note<"conversion candidate of type %0">;
def err_member_call_without_object : Error<
- "call to non-static member function without an object argument">;
+ "call to %select{non-static|explicit}0 member function without an object argument">;
// C++ Address of Overloaded Function
def err_addr_ovl_no_viable : Error<
@@ -7289,14 +7291,16 @@ def note_logical_not_silence_with_parens : Note<
"add parentheses around left hand side expression to silence this warning">;
def err_invalid_this_use : Error<
- "invalid use of 'this' outside of a non-static member function">;
+ "invalid use of 'this' %select{outside of a non-static member function"
+ "|in a function with an explicit object parameter}0">;
def err_this_static_member_func : Error<
"'this' cannot be%select{| implicitly}0 used in a static member function "
"declaration">;
-def err_invalid_member_use_in_static_method : Error<
- "invalid use of member %0 in static member function">;
+def err_invalid_member_use_in_method : Error<
+ "invalid use of member %0 in %select{static|explicit object}1 member function">;
+
def err_invalid_qualified_function_type : Error<
- "%select{non-member function|static member function|deduction guide}0 "
+ "%select{non-member function|static member function|explicit object member function|deduction guide}0 "
"%select{of type %2 |}1cannot have '%3' qualifier">;
def err_compound_qualified_function_type : Error<
"%select{block pointer|pointer|reference}0 to function type %select{%2 |}1"
@@ -7304,6 +7308,26 @@ def err_compound_qualified_function_type : Error<
def err_qualified_function_typeid : Error<
"type operand %0 of 'typeid' cannot have '%1' qualifier">;
+def err_cxx20_deducing_this : Error<
+ "explicit object parameters are incompatible with C++ standards before C++2b">;
+def err_explicit_object_default_arg: Error<
+ "the explicit object parameter cannot have a default argument">;
+def err_explicit_object_parameter_pack: Error<
+ "the explicit object parameter cannot be a function parameter pack">;
+def err_explicit_object_parameter_must_be_first: Error<
+ "an explicit object parameter can only appear as the first parameter "
+ "of the %select{function|lambda}0">;
+def err_explicit_object_parameter_nonmember: Error<
+ "an explicit object parameter cannot appear in a "
+ "%select{static|virtual|non-member}0 %select{function|lambda}1">;
+def err_explicit_object_parameter_constructor: Error<
+ "an explicit object parameter cannot appear in a %select{constructor|destructor}0">;
+def err_explicit_object_parameter_mutable: Error<
+ "a lambda with an explicit object parameter cannot be mutable">;
+def err_invalid_explicit_object_type_in_lambda: Error<
+ "invalid explicit object parameter type %0 in lambda with capture; "
+ "the type must be the same as, or derived from, the lambda">;
+
def err_ref_qualifier_overload : Error<
"cannot overload a member function %select{without a ref-qualifier|with "
"ref-qualifier '&'|with ref-qualifier '&&'}0 with a member function %select{"
@@ -8525,53 +8549,65 @@ def err_call_function_incomplete_return : Error<
def err_call_incomplete_argument : Error<
"argument type %0 is incomplete">;
def err_typecheck_call_too_few_args : Error<
- "too few %select{|||execution configuration }0arguments to "
+ "too few %select{|||execution configuration }0"
+ "%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected %1, have %2">;
def err_typecheck_call_too_few_args_one : Error<
- "too few %select{|||execution configuration }0arguments to "
+ "too few %select{|||execution configuration }0"
+ "%select{|non-object }2arguments to "
"%select{function|block|method|kernel function}0 call, "
"single argument %1 was not specified">;
def err_typecheck_call_too_few_args_at_least : Error<
- "too few %select{|||execution configuration }0arguments to "
+ "too few %select{|||execution configuration }0"
+ "%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected at least %1, have %2">;
def err_typecheck_call_too_few_args_at_least_one : Error<
- "too few %select{|||execution configuration }0arguments to "
+ "too few %select{|||execution configuration }0"
+ "%select{|non-object }2arguments to "
"%select{function|block|method|kernel function}0 call, "
"at least argument %1 must be specified">;
def err_typecheck_call_too_few_args_suggest : Error<
- "too few %select{|||execution configuration }0arguments to "
+ "too few %select{|||execution configuration }0"
+ "%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
- "expected %1, have %2; did you mean %3?">;
+ "expected %1, have %2; did you mean %4?">;
def err_typecheck_call_too_few_args_at_least_suggest : Error<
- "too few %select{|||execution configuration }0arguments to "
+ "too few %select{|||execution configuration }0"
+ "%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
- "expected at least %1, have %2; did you mean %3?">;
+ "expected at least %1, have %2; did you mean %4?">;
def err_typecheck_call_too_many_args : Error<
- "too many %select{|||execution configuration }0arguments to "
+ "too many %select{|||execution configuration }0"
+ "%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected %1, have %2">;
def err_typecheck_call_too_many_args_one : Error<
- "too many %select{|||execution configuration }0arguments to "
+ "too many %select{|||execution configuration }0"
+ "%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected single argument %1, have %2 arguments">;
def err_typecheck_call_too_many_args_at_most : Error<
- "too many %select{|||execution configuration }0arguments to "
+ "too many %select{|||execution configuration }0"
+ "%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected at most %1, have %2">;
def err_typecheck_call_too_many_args_at_most_one : Error<
"too many %select{|||execution configuration }0arguments to "
"%select{function|block|method|kernel function}0 call, "
- "expected at most single argument %1, have %2 arguments">;
+ "expected at most single %select{|non-object }3argument %1, "
+ "have %2%select{|non-object}3 arguments">;
def err_typecheck_call_too_many_args_suggest : Error<
- "too many %select{|||execution configuration }0arguments to "
+ "too many %select{|||execution configuration }0"
+ "%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
- "expected %1, have %2; did you mean %3?">;
+ "expected %1, have %2; did you mean %4?">;
def err_typecheck_call_too_many_args_at_most_suggest : Error<
- "too many %select{|||execution configuration }0arguments to "
+ "too many %select{|||execution configuration }0"
+ "%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
- "expected at most %1, have %2; did you mean %3?">;
+ "expected at most %1, have %2; did you mean %4?">;
def err_arc_typecheck_convert_incompatible_pointer : Error<
"incompatible pointer types passing retainable parameter of type %0"
@@ -9389,10 +9425,10 @@ def warn_cxx98_compat_explicit_conversion_functions : Warning<
// C++11 defaulted functions
def err_defaulted_special_member_params : Error<
- "an explicitly-defaulted %select{|copy |move }0constructor cannot "
+ "an explicitly-defaulted %sub{select_special_member_kind}0 cannot "
"have default arguments">;
def err_defaulted_special_member_variadic : Error<
- "an explicitly-defaulted %select{|copy |move }0constructor cannot "
+ "an explicitly-defaulted %sub{select_special_member_kind}0 cannot "
"be variadic">;
def err_defaulted_special_member_return_type : Error<
"explicitly-defaulted %select{copy|move}0 assignment operator must "
@@ -9455,7 +9491,7 @@ def err_defaulted_comparison_template : Error<
"comparison operator template cannot be defaulted">;
def err_defaulted_comparison_num_args : Error<
"%select{non-member|member}0 %sub{select_defaulted_comparison_kind}1"
- " comparison operator must have %select{2|1}0 parameters">;
+ " must have %select{2|1}0 parameters">;
def err_defaulted_comparison_param : Error<
"invalid parameter type for defaulted %sub{select_defaulted_comparison_kind}0"
"; found %1, expected %2%select{| or %4}3">;
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index ff5e2e2..4561cca 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -2674,6 +2674,8 @@ public:
/// redeclaration time if the decl is static.
bool isStaticMember();
+ bool isExplicitObjectMemberFunction();
+
/// Returns true if this declares a constructor or a destructor.
bool isCtorOrDtor();
diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h
index 26c0387..361108f 100644
--- a/clang/include/clang/Sema/ScopeInfo.h
+++ b/clang/include/clang/Sema/ScopeInfo.h
@@ -847,6 +847,8 @@ public:
/// is known.
bool AfterParameterList = true;
+ ParmVarDecl *ExplicitObjectParameter = nullptr;
+
/// Source range covering the lambda introducer [...].
SourceRange IntroducerRange;
@@ -1042,6 +1044,8 @@ public:
void visitPotentialCaptures(
llvm::function_ref<void(ValueDecl *, Expr *)> Callback) const;
+
+ bool lambdaCaptureShouldBeConst() const;
};
FunctionScopeInfo::WeakObjectProfileTy::WeakObjectProfileTy()
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index e13524b..5bef033 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2183,20 +2183,17 @@ public:
const FunctionProtoType *Old, SourceLocation OldLoc,
const FunctionProtoType *New, SourceLocation NewLoc);
bool handlerCanCatch(QualType HandlerType, QualType ExceptionType);
- bool CheckExceptionSpecSubset(const PartialDiagnostic &DiagID,
- const PartialDiagnostic &NestedDiagID,
- const PartialDiagnostic &NoteID,
- const PartialDiagnostic &NoThrowDiagID,
- const FunctionProtoType *Superset,
- SourceLocation SuperLoc,
- const FunctionProtoType *Subset,
- SourceLocation SubLoc);
- bool CheckParamExceptionSpec(const PartialDiagnostic &NestedDiagID,
- const PartialDiagnostic &NoteID,
- const FunctionProtoType *Target,
- SourceLocation TargetLoc,
- const FunctionProtoType *Source,
- SourceLocation SourceLoc);
+ bool CheckExceptionSpecSubset(
+ const PartialDiagnostic &DiagID, const PartialDiagnostic &NestedDiagID,
+ const PartialDiagnostic &NoteID, const PartialDiagnostic &NoThrowDiagID,
+ const FunctionProtoType *Superset, bool SkipSupersetFirstParameter,
+ SourceLocation SuperLoc, const FunctionProtoType *Subset,
+ bool SkipSubsetFirstParameter, SourceLocation SubLoc);
+ bool CheckParamExceptionSpec(
+ const PartialDiagnostic &NestedDiagID, const PartialDiagnostic &NoteID,
+ const FunctionProtoType *Target, bool SkipTargetFirstParameter,
+ SourceLocation TargetLoc, const FunctionProtoType *Source,
+ bool SkipSourceFirstParameter, SourceLocation SourceLoc);
TypeResult ActOnTypeName(Scope *S, Declarator &D);
@@ -3023,7 +3020,8 @@ public:
Attr *getImplicitCodeSegOrSectionAttrForFunction(const FunctionDecl *FD,
bool IsDefinition);
void CheckFunctionOrTemplateParamDeclarator(Scope *S, Declarator &D);
- Decl *ActOnParamDeclarator(Scope *S, Declarator &D);
+ Decl *ActOnParamDeclarator(Scope *S, Declarator &D,
+ SourceLocation ExplicitThisLoc = {});
ParmVarDecl *BuildParmVarDeclForTypedef(DeclContext *DC,
SourceLocation Loc,
QualType T);
@@ -3797,8 +3795,12 @@ public:
NamedDecl *&OldDecl,
bool UseMemberUsingDeclRules);
bool IsOverload(FunctionDecl *New, FunctionDecl *Old,
- bool UseMemberUsingDeclRules, bool ConsiderCudaAttrs = true,
- bool ConsiderRequiresClauses = true);
+ bool UseMemberUsingDeclRules, bool ConsiderCudaAttrs = true);
+
+ // Checks whether MD constitutes an override the base class method BaseMD.
+ // When checking for overrides, the object object members are ignored.
+ bool IsOverride(FunctionDecl *MD, FunctionDecl *BaseMD,
+ bool UseMemberUsingDeclRules, bool ConsiderCudaAttrs = true);
// Calculates whether the expression Constraint depends on an enclosing
// template, for the purposes of [temp.friend] p9.
@@ -3856,6 +3858,12 @@ public:
QualType &ConvertedType);
bool IsBlockPointerConversion(QualType FromType, QualType ToType,
QualType& ConvertedType);
+
+ bool FunctionParamTypesAreEqual(ArrayRef<QualType> Old,
+ ArrayRef<QualType> New,
+ unsigned *ArgPos = nullptr,
+ bool Reversed = false);
+
bool FunctionParamTypesAreEqual(const FunctionProtoType *OldType,
const FunctionProtoType *NewType,
unsigned *ArgPos = nullptr,
@@ -3896,10 +3904,11 @@ public:
ExprResult Init,
bool TopLevelOfInitList = false,
bool AllowExplicit = false);
- ExprResult PerformObjectArgumentInitialization(Expr *From,
- NestedNameSpecifier *Qualifier,
- NamedDecl *FoundDecl,
- CXXMethodDecl *Method);
+ ExprResult InitializeExplicitObjectArgument(Sema &S, Expr *Obj,
+ FunctionDecl *Fun);
+ ExprResult PerformImplicitObjectArgumentInitialization(
+ Expr *From, NestedNameSpecifier *Qualifier, NamedDecl *FoundDecl,
+ CXXMethodDecl *Method);
/// Check that the lifetime of the initializer (and its subobjects) is
/// sufficient for initializing the entity, and perform lifetime extension
@@ -4221,9 +4230,8 @@ public:
QualType DestTypeForComplaining = QualType(),
unsigned DiagIDForComplaining = 0);
- Expr *FixOverloadedFunctionReference(Expr *E,
- DeclAccessPair FoundDecl,
- FunctionDecl *Fn);
+ ExprResult FixOverloadedFunctionReference(Expr *E, DeclAccessPair FoundDecl,
+ FunctionDecl *Fn);
ExprResult FixOverloadedFunctionReference(ExprResult,
DeclAccessPair FoundDecl,
FunctionDecl *Fn);
@@ -4799,8 +4807,8 @@ public:
/// Adjust the calling convention of a method to be the ABI default if it
/// wasn't specified explicitly. This handles method types formed from
/// function type typedefs and typename template arguments.
- void adjustMemberFunctionCC(QualType &T, bool IsStatic, bool IsCtorOrDtor,
- SourceLocation Loc);
+ void adjustMemberFunctionCC(QualType &T, bool HasThisPointer,
+ bool IsCtorOrDtor, SourceLocation Loc);
// Check if there is an explicit attribute, but only look through parens.
// The intent is to look for an attribute on the current declarator, but not
@@ -5796,6 +5804,10 @@ public:
Expr *Input, bool IsAfterAmp = false);
bool isQualifiedMemberAccess(Expr *E);
+ bool CheckUseOfCXXMethodAsAddressOfOperand(SourceLocation OpLoc,
+ const Expr *Op,
+ const CXXMethodDecl *MD);
+
QualType CheckAddressOfOperand(ExprResult &Operand, SourceLocation OpLoc);
bool CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N);
@@ -7236,6 +7248,8 @@ public:
StorageClass SC, ArrayRef<ParmVarDecl *> Params,
bool HasExplicitResultType);
+ void DiagnoseInvalidExplicitObjectParameterInLambda(CXXMethodDecl *Method);
+
/// Perform initialization analysis of the init-capture and perform
/// any implicit conversions such as an lvalue-to-rvalue conversion if
/// not being used to initialize a reference.
@@ -7916,6 +7930,13 @@ public:
void DefineDefaultedComparison(SourceLocation Loc, FunctionDecl *FD,
DefaultedComparisonKind DCK);
+ void CheckExplicitObjectMemberFunction(Declarator &D, DeclarationName Name,
+ QualType R, bool IsLambda,
+ DeclContext *DC = nullptr);
+ void CheckExplicitObjectMemberFunction(DeclContext *DC, Declarator &D,
+ DeclarationName Name, QualType R);
+ void CheckExplicitObjectLambda(Declarator &D);
+
//===--------------------------------------------------------------------===//
// C++ Derived Classes
//
@@ -7967,6 +7988,10 @@ public:
bool CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
const CXXMethodDecl *Old);
+ // Check that the overriding method has no explicit object parameter.
+ bool CheckExplicitObjectOverride(CXXMethodDecl *New,
+ const CXXMethodDecl *Old);
+
/// CheckOverridingFunctionExceptionSpec - Checks whether the exception
/// spec is a subset of base spec.
bool CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
@@ -9210,6 +9235,7 @@ public:
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info,
bool PartialOverloading, bool AggregateDeductionCandidate,
+ QualType ObjectType, Expr::Classification ObjectClassification,
llvm::function_ref<bool(ArrayRef<QualType>)> CheckNonDependent);
TemplateDeductionResult
@@ -9220,11 +9246,10 @@ public:
sema::TemplateDeductionInfo &Info,
bool IsAddressOfFunction = false);
- TemplateDeductionResult
- DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
- QualType ToType,
- CXXConversionDecl *&Specialization,
- sema::TemplateDeductionInfo &Info);
+ TemplateDeductionResult DeduceTemplateArguments(
+ FunctionTemplateDecl *FunctionTemplate, QualType ObjectType,
+ Expr::Classification ObjectClassification, QualType ToType,
+ CXXConversionDecl *&Specialization, sema::TemplateDeductionInfo &Info);
TemplateDeductionResult
DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index c7c2aec..b7240e6 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -4521,6 +4521,8 @@ ExpectedDecl ASTNodeImporter::VisitImplicitParamDecl(ImplicitParamDecl *D) {
Error ASTNodeImporter::ImportDefaultArgOfParmVarDecl(
const ParmVarDecl *FromParam, ParmVarDecl *ToParam) {
ToParam->setHasInheritedDefaultArg(FromParam->hasInheritedDefaultArg());
+ ToParam->setExplicitObjectParameterLoc(
+ FromParam->getExplicitObjectParamThisLoc());
ToParam->setKNRPromoted(FromParam->isKNRPromoted());
if (FromParam->hasUninstantiatedDefaultArg()) {
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 5b98d14..8ad142f 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -1403,6 +1403,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
Method1->getAccess() == Method2->getAccess() &&
Method1->getOverloadedOperator() == Method2->getOverloadedOperator() &&
Method1->isStatic() == Method2->isStatic() &&
+ Method1->isImplicitObjectMemberFunction() ==
+ Method2->isImplicitObjectMemberFunction() &&
Method1->isConst() == Method2->isConst() &&
Method1->isVolatile() == Method2->isVolatile() &&
Method1->isVirtual() == Method2->isVirtual() &&
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 09df540..097753f 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -484,6 +484,10 @@ ExprDependence clang::computeDependence(DeclRefExpr *E, const ASTContext &Ctx) {
// - an identifier associated by name lookup with one or more declarations
// declared with a dependent type
+ // - an identifier associated by name lookup with an entity captured by
+ // copy ([expr.prim.lambda.capture])
+ // in a lambda-expression that has an explicit object parameter whose
+ // type is dependent ([dcl.fct]),
//
// [The "or more" case is not modeled as a DeclRefExpr. There are a bunch
// more bullets here that we handle by treating the declaration as having a
@@ -493,6 +497,11 @@ ExprDependence clang::computeDependence(DeclRefExpr *E, const ASTContext &Ctx) {
else if (Type->isInstantiationDependentType())
Deps |= ExprDependence::Instantiation;
+ // - an identifier associated by name lookup with an entity captured by
+ // copy ([expr.prim.lambda.capture])
+ if (E->isCapturedByCopyInLambdaWithExplicitObjectParameter())
+ Deps |= ExprDependence::Type;
+
// - a conversion-function-id that specifies a dependent type
if (Decl->getDeclName().getNameKind() ==
DeclarationName::CXXConversionFunctionName) {
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index dc02706..b88df1e 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3652,6 +3652,20 @@ unsigned FunctionDecl::getMinRequiredArguments() const {
return NumRequiredArgs;
}
+bool FunctionDecl::hasCXXExplicitFunctionObjectParameter() const {
+ return getNumParams() != 0 && getParamDecl(0)->isExplicitObjectParameter();
+}
+
+unsigned FunctionDecl::getNumNonObjectParams() const {
+ return getNumParams() -
+ static_cast<unsigned>(hasCXXExplicitFunctionObjectParameter());
+}
+
+unsigned FunctionDecl::getMinRequiredExplicitArguments() const {
+ return getMinRequiredArguments() -
+ static_cast<unsigned>(hasCXXExplicitFunctionObjectParameter());
+}
+
bool FunctionDecl::hasOneParamOrDefaultArgs() const {
return getNumParams() == 1 ||
(getNumParams() > 1 &&
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 9d9c338..42bab4e 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -838,7 +838,7 @@ void CXXRecordDecl::addedMember(Decl *D) {
SMKind |= SMF_CopyAssignment;
const auto *ParamTy =
- Method->getParamDecl(0)->getType()->getAs<ReferenceType>();
+ Method->getNonObjectParameter(0)->getType()->getAs<ReferenceType>();
if (!ParamTy || ParamTy->getPointeeType().isConstQualified())
data().HasDeclaredCopyAssignmentWithConstParam = true;
}
@@ -2409,6 +2409,17 @@ bool CXXMethodDecl::isUsualDeallocationFunction(
return Result;
}
+bool CXXMethodDecl::isExplicitObjectMemberFunction() const {
+ // C++2b [dcl.fct]p6:
+ // An explicit object member function is a non-static member
+ // function with an explicit object parameter
+ return !isStatic() && hasCXXExplicitFunctionObjectParameter();
+}
+
+bool CXXMethodDecl::isImplicitObjectMemberFunction() const {
+ return !isStatic() && !hasCXXExplicitFunctionObjectParameter();
+}
+
bool CXXMethodDecl::isCopyAssignmentOperator() const {
// C++0x [class.copy]p17:
// A user-declared copy assignment operator X::operator= is a non-static
@@ -2416,11 +2427,12 @@ bool CXXMethodDecl::isCopyAssignmentOperator() const {
// type X, X&, const X&, volatile X& or const volatile X&.
if (/*operator=*/getOverloadedOperator() != OO_Equal ||
/*non-static*/ isStatic() ||
- /*non-template*/getPrimaryTemplate() || getDescribedFunctionTemplate() ||
- getNumParams() != 1)
+
+ /*non-template*/ getPrimaryTemplate() || getDescribedFunctionTemplate() ||
+ getNumExplicitParams() != 1)
return false;
- QualType ParamType = getParamDecl(0)->getType();
+ QualType ParamType = getNonObjectParameter(0)->getType();
if (const auto *Ref = ParamType->getAs<LValueReferenceType>())
ParamType = Ref->getPointeeType();
@@ -2437,10 +2449,10 @@ bool CXXMethodDecl::isMoveAssignmentOperator() const {
// X&&, const X&&, volatile X&&, or const volatile X&&.
if (getOverloadedOperator() != OO_Equal || isStatic() ||
getPrimaryTemplate() || getDescribedFunctionTemplate() ||
- getNumParams() != 1)
+ getNumExplicitParams() != 1)
return false;
- QualType ParamType = getParamDecl(0)->getType();
+ QualType ParamType = getNonObjectParameter(0)->getType();
if (!ParamType->isRValueReferenceType())
return false;
ParamType = ParamType->getPointeeType();
@@ -2496,12 +2508,6 @@ QualType CXXMethodDecl::getThisType(const FunctionProtoType *FPT,
: C.getPointerType(ObjectTy);
}
-QualType CXXMethodDecl::getThisObjectType(const FunctionProtoType *FPT,
- const CXXRecordDecl *Decl) {
- ASTContext &C = Decl->getASTContext();
- return ::getThisObjectType(C, FPT, Decl);
-}
-
QualType CXXMethodDecl::getThisType() const {
// C++ 9.3.2p1: The type of this in a member function of a class X is X*.
// If the member function is declared const, the type of this is const X*,
@@ -2513,11 +2519,17 @@ QualType CXXMethodDecl::getThisType() const {
getParent());
}
-QualType CXXMethodDecl::getThisObjectType() const {
- // Ditto getThisType.
- assert(isInstance() && "No 'this' for static methods!");
- return CXXMethodDecl::getThisObjectType(
- getType()->castAs<FunctionProtoType>(), getParent());
+QualType CXXMethodDecl::getFunctionObjectParameterReferenceType() const {
+ if (isExplicitObjectMemberFunction())
+ return parameters()[0]->getType();
+
+ ASTContext &C = getParentASTContext();
+ const FunctionProtoType *FPT = getType()->castAs<FunctionProtoType>();
+ QualType Type = ::getThisObjectType(C, FPT, getParent());
+ RefQualifierKind RK = FPT->getRefQualifier();
+ if (RK == RefQualifierKind::RQ_RValue)
+ return C.getRValueReferenceType(Type);
+ return C.getLValueReferenceType(Type);
}
bool CXXMethodDecl::hasInlineBody() const {
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 748ed37..daa219b 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -962,6 +962,10 @@ void DeclPrinter::VisitLabelDecl(LabelDecl *D) {
void DeclPrinter::VisitVarDecl(VarDecl *D) {
prettyPrintPragmas(D);
+ if (const auto *Param = dyn_cast<ParmVarDecl>(D);
+ Param && Param->isExplicitObjectParameter())
+ Out << "this ";
+
std::string LeftSide;
llvm::raw_string_ostream LeftSideStream(LeftSide);
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 4f38373..a718d32 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -491,6 +491,7 @@ DeclRefExpr::DeclRefExpr(const ASTContext &Ctx, ValueDecl *D,
DeclRefExprBits.HadMultipleCandidates = false;
DeclRefExprBits.RefersToEnclosingVariableOrCapture =
RefersToEnclosingVariableOrCapture;
+ DeclRefExprBits.CapturedByCopyInLambdaWithExplicitObjectParameter = false;
DeclRefExprBits.NonOdrUseReason = NOUR;
DeclRefExprBits.IsImmediateEscalating = false;
DeclRefExprBits.Loc = L;
@@ -518,6 +519,7 @@ DeclRefExpr::DeclRefExpr(const ASTContext &Ctx,
= (TemplateArgs || TemplateKWLoc.isValid()) ? 1 : 0;
DeclRefExprBits.RefersToEnclosingVariableOrCapture =
RefersToEnclosingVariableOrCapture;
+ DeclRefExprBits.CapturedByCopyInLambdaWithExplicitObjectParameter = false;
DeclRefExprBits.NonOdrUseReason = NOUR;
if (TemplateArgs) {
auto Deps = TemplateArgumentDependence::None;
diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp
index 12193b7..ffa7c68 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -465,8 +465,13 @@ static Cl::Kinds ClassifyDecl(ASTContext &Ctx, const Decl *D) {
// lvalue unless it's a reference type (C++ [temp.param]p6), so we need to
// special-case this.
- if (isa<CXXMethodDecl>(D) && cast<CXXMethodDecl>(D)->isInstance())
- return Cl::CL_MemberFunction;
+ if (const auto *M = dyn_cast<CXXMethodDecl>(D)) {
+ if (M->isImplicitObjectMemberFunction())
+ return Cl::CL_MemberFunction;
+ if (M->isStatic())
+ return Cl::CL_LValue;
+ return Cl::CL_PRValue;
+ }
bool islvalue;
if (const auto *NTTParm = dyn_cast<NonTypeTemplateParmDecl>(D))
@@ -551,8 +556,13 @@ static Cl::Kinds ClassifyMemberExpr(ASTContext &Ctx, const MemberExpr *E) {
// -- If it refers to a static member function [...], then E1.E2 is an
// lvalue; [...]
// -- Otherwise [...] E1.E2 is a prvalue.
- if (const auto *Method = dyn_cast<CXXMethodDecl>(Member))
- return Method->isStatic() ? Cl::CL_LValue : Cl::CL_MemberFunction;
+ if (const auto *Method = dyn_cast<CXXMethodDecl>(Member)) {
+ if (Method->isStatic())
+ return Cl::CL_LValue;
+ if (Method->isImplicitObjectMemberFunction())
+ return Cl::CL_MemberFunction;
+ return Cl::CL_PRValue;
+ }
// -- If E2 is a member enumerator [...], the expression E1.E2 is a prvalue.
// So is everything else we haven't handled yet.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index f7a99bda..a142ea7 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -1945,9 +1945,9 @@ APValue *EvalInfo::createHeapAlloc(const Expr *E, QualType T, LValue &LV) {
/// Produce a string describing the given constexpr call.
void CallStackFrame::describe(raw_ostream &Out) const {
unsigned ArgIndex = 0;
- bool IsMemberCall = isa<CXXMethodDecl>(Callee) &&
- !isa<CXXConstructorDecl>(Callee) &&
- cast<CXXMethodDecl>(Callee)->isInstance();
+ bool IsMemberCall =
+ isa<CXXMethodDecl>(Callee) && !isa<CXXConstructorDecl>(Callee) &&
+ cast<CXXMethodDecl>(Callee)->isImplicitObjectMemberFunction();
if (!IsMemberCall)
Callee->getNameForDiagnostic(Out, Info.Ctx.getPrintingPolicy(),
@@ -4711,6 +4711,9 @@ static bool EvaluateObjectArgument(EvalInfo &Info, const Expr *Object,
if (Object->getType()->isLiteralType(Info.Ctx))
return EvaluateTemporary(Object, This, Info);
+ if (Object->getType()->isRecordType() && Object->isPRValue())
+ return EvaluateTemporary(Object, This, Info);
+
Info.FFDiag(Object, diag::note_constexpr_nonliteral) << Object->getType();
return false;
}
@@ -7789,15 +7792,18 @@ public:
if (OCE && OCE->isAssignmentOp()) {
assert(Args.size() == 2 && "wrong number of arguments in assignment");
Call = Info.CurrentCall->createCall(FD);
- if (!EvaluateArgs(isa<CXXMethodDecl>(FD) ? Args.slice(1) : Args, Call,
- Info, FD, /*RightToLeft=*/true))
+ bool HasThis = false;
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
+ HasThis = MD->isImplicitObjectMemberFunction();
+ if (!EvaluateArgs(HasThis ? Args.slice(1) : Args, Call, Info, FD,
+ /*RightToLeft=*/true))
return false;
}
// Overloaded operator calls to member functions are represented as normal
// calls with '*this' as the first argument.
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
- if (MD && !MD->isStatic()) {
+ if (MD && MD->isImplicitObjectMemberFunction()) {
// FIXME: When selecting an implicit conversion for an overloaded
// operator delete, we sometimes try to evaluate calls to conversion
// operators without a 'this' parameter!
@@ -7881,7 +7887,7 @@ public:
CovariantAdjustmentPath);
if (!FD)
return false;
- } else {
+ } else if (NamedMember && NamedMember->isImplicitObjectMemberFunction()) {
// Check that the 'this' pointer points to an object of the right type.
// FIXME: If this is an assignment operator call, we may need to change
// the active union member before we check this.
@@ -16284,7 +16290,8 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx,
#ifndef NDEBUG
auto *MD = dyn_cast<CXXMethodDecl>(Callee);
assert(MD && "Don't provide `this` for non-methods.");
- assert(!MD->isStatic() && "Don't provide `this` for static methods.");
+ assert(MD->isImplicitObjectMemberFunction() &&
+ "Don't provide `this` for methods without an implicit object.");
#endif
if (!This->isValueDependent() &&
EvaluateObjectArgument(Info, This, ThisVal) &&
@@ -16379,9 +16386,10 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
HandleConstructorCall(&VIE, This, Args, CD, Info, Scratch);
} else {
SourceLocation Loc = FD->getLocation();
- HandleFunctionCall(Loc, FD, (MD && MD->isInstance()) ? &This : nullptr,
- &VIE, Args, CallRef(), FD->getBody(), Info, Scratch,
- /*ResultSlot=*/nullptr);
+ HandleFunctionCall(
+ Loc, FD, (MD && MD->isImplicitObjectMemberFunction()) ? &This : nullptr,
+ &VIE, Args, CallRef(), FD->getBody(), Info, Scratch,
+ /*ResultSlot=*/nullptr);
}
return Diags.empty();
diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp
index 23c28b4..ea4e48c 100644
--- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp
+++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp
@@ -44,7 +44,7 @@ ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
// InterpStack when calling the function.
bool HasThisPointer = false;
if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl)) {
- if (MD->isInstance()) {
+ if (MD->isImplicitObjectMemberFunction()) {
HasThisPointer = true;
ParamTypes.push_back(PT_Ptr);
ParamOffsets.push_back(ParamOffset);
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 830a35b..a2a001e 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -1721,7 +1721,7 @@ void CXXNameMangler::mangleUnqualifiedName(
// If we have a member function, we need to include the 'this' pointer.
if (const auto *MD = dyn_cast<CXXMethodDecl>(ND))
- if (!MD->isStatic())
+ if (MD->isImplicitObjectMemberFunction())
Arity++;
}
[[fallthrough]];
@@ -1781,6 +1781,8 @@ void CXXNameMangler::mangleNestedName(GlobalDecl GD,
Qualifiers MethodQuals = Method->getMethodQualifiers();
// We do not consider restrict a distinguishing attribute for overloading
// purposes so we must not mangle it.
+ if (Method->isExplicitObjectMemberFunction())
+ Out << 'H';
MethodQuals.removeRestrict();
mangleQualifiers(MethodQuals);
mangleRefQualifier(Method->getRefQualifier());
diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index e67c2c7..beb0701 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -876,6 +876,9 @@ void JSONNodeDumper::VisitUsingShadowDecl(const UsingShadowDecl *USD) {
void JSONNodeDumper::VisitVarDecl(const VarDecl *VD) {
VisitNamedDecl(VD);
JOS.attribute("type", createQualType(VD->getType()));
+ if (const auto *P = dyn_cast<ParmVarDecl>(VD))
+ attributeOnlyIfTrue("explicitObjectParameter",
+ P->isExplicitObjectParameter());
StorageClass SC = VD->getStorageClass();
if (SC != SC_None)
diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp
index 53af9fc..64c9719 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -225,7 +225,7 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
assert(!Proto->isVariadic());
unsigned ArgWords = 0;
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD))
- if (!MD->isStatic())
+ if (MD->isImplicitObjectMemberFunction())
++ArgWords;
uint64_t DefaultPtrWidth = TI.getPointerWidth(LangAS::Default);
for (const auto &AT : Proto->param_types()) {
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index 79175c7..7db18cf 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -2636,7 +2636,7 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(D)) {
if (MD->getParent()->isLambda())
IsInLambda = true;
- if (MD->isInstance())
+ if (MD->isImplicitObjectMemberFunction())
HasThisQuals = true;
if (isa<CXXDestructorDecl>(MD)) {
IsStructor = true;
@@ -2745,6 +2745,10 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
} else {
// Happens for function pointer type arguments for example.
for (unsigned I = 0, E = Proto->getNumParams(); I != E; ++I) {
+ // Explicit object parameters are prefixed by "_V".
+ if (I == 0 && D && D->getParamDecl(I)->isExplicitObjectParameter())
+ Out << "_V";
+
mangleFunctionArgumentType(Proto->getParamType(I), Range);
// Mangle each pass_object_size parameter as if it's a parameter of enum
// type passed directly after the parameter with the pass_object_size
@@ -2810,7 +2814,7 @@ void MicrosoftCXXNameMangler::mangleFunctionClass(const FunctionDecl *FD) {
case AS_none:
llvm_unreachable("Unsupported access specifier");
case AS_private:
- if (MD->isStatic())
+ if (!MD->isImplicitObjectMemberFunction())
Out << 'C';
else if (IsVirtual)
Out << 'E';
@@ -2818,7 +2822,7 @@ void MicrosoftCXXNameMangler::mangleFunctionClass(const FunctionDecl *FD) {
Out << 'A';
break;
case AS_protected:
- if (MD->isStatic())
+ if (!MD->isImplicitObjectMemberFunction())
Out << 'K';
else if (IsVirtual)
Out << 'M';
@@ -2826,7 +2830,7 @@ void MicrosoftCXXNameMangler::mangleFunctionClass(const FunctionDecl *FD) {
Out << 'I';
break;
case AS_public:
- if (MD->isStatic())
+ if (!MD->isImplicitObjectMemberFunction())
Out << 'S';
else if (IsVirtual)
Out << 'U';
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 233dc6c..15eb6c6 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1949,6 +1949,10 @@ void TextNodeDumper::VisitFieldDecl(const FieldDecl *D) {
void TextNodeDumper::VisitVarDecl(const VarDecl *D) {
dumpNestedNameSpecifier(D->getQualifier());
dumpName(D);
+ if (const auto *P = dyn_cast<ParmVarDecl>(D);
+ P && P->isExplicitObjectParameter())
+ OS << " this";
+
dumpType(D->getType());
dumpTemplateSpecializationKind(D->getTemplateSpecializationKind());
StorageClass SC = D->getStorageClass();
diff --git a/clang/lib/Analysis/Consumed.cpp b/clang/lib/Analysis/Consumed.cpp
index d90a8a9..d01c7f6 100644
--- a/clang/lib/Analysis/Consumed.cpp
+++ b/clang/lib/Analysis/Consumed.cpp
@@ -771,7 +771,7 @@ void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr(
void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
CXXConstructorDecl *Constructor = Call->getConstructor();
- QualType ThisType = Constructor->getThisObjectType();
+ QualType ThisType = Constructor->getFunctionObjectParameterType();
if (!isConsumableType(ThisType))
return;
@@ -1199,7 +1199,7 @@ void ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC,
const FunctionDecl *D) {
QualType ReturnType;
if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
- ReturnType = Constructor->getThisObjectType();
+ ReturnType = Constructor->getFunctionObjectParameterType();
} else
ReturnType = D->getCallResultType();
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index 1ea4092..66d98c9 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -452,8 +452,8 @@ Environment::Environment(DataflowAnalysisContext &DACtx,
MethodDecl = dyn_cast<CXXMethodDecl>(Parent->getDeclContext());
// FIXME: Initialize the ThisPointeeLoc of lambdas too.
- if (MethodDecl && !MethodDecl->isStatic()) {
- QualType ThisPointeeType = MethodDecl->getThisObjectType();
+ if (MethodDecl && MethodDecl->isImplicitObjectMemberFunction()) {
+ QualType ThisPointeeType = MethodDecl->getFunctionObjectParameterType();
ThisPointeeLoc =
&cast<RecordValue>(createValue(ThisPointeeType))->getLoc();
}
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index 6c92b84..bb26e9f 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -525,7 +525,9 @@ public:
// The assignment operators are different from the type of the destination
// in this model (i.e. in one of their base classes). This must be very
// rare and we just bail.
- if (Method->getThisObjectType().getCanonicalType().getUnqualifiedType() !=
+ if (Method->getFunctionObjectParameterType()
+ .getCanonicalType()
+ .getUnqualifiedType() !=
LocDst->getType().getCanonicalType().getUnqualifiedType())
return;
diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp
index 63cc668..2fe0f85 100644
--- a/clang/lib/Analysis/ThreadSafetyCommon.cpp
+++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp
@@ -154,7 +154,9 @@ CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
// If the attribute has no arguments, then assume the argument is "this".
if (!AttrExp)
return CapabilityExpr(
- Self, ClassifyDiagnostic(cast<CXXMethodDecl>(D)->getThisObjectType()),
+ Self,
+ ClassifyDiagnostic(
+ cast<CXXMethodDecl>(D)->getFunctionObjectParameterType()),
false);
else // For most attributes.
return translateAttrExpr(AttrExp, &Ctx);
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 3fe8d0a..93e1657 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -298,7 +298,7 @@ CodeGenTypes::arrangeCXXMethodDeclaration(const CXXMethodDecl *MD) {
setCUDAKernelCallingConvention(FT, CGM, MD);
auto prototype = FT.getAs<FunctionProtoType>();
- if (MD->isInstance()) {
+ if (MD->isImplicitObjectMemberFunction()) {
// The abstract case is perfectly fine.
const CXXRecordDecl *ThisType = TheCXXABI.getThisArgumentTypeForMethod(MD);
return arrangeCXXMethodType(ThisType, prototype.getTypePtr(), MD);
@@ -448,7 +448,7 @@ CodeGenTypes::arrangeCXXConstructorCall(const CallArgList &args,
const CGFunctionInfo &
CodeGenTypes::arrangeFunctionDeclaration(const FunctionDecl *FD) {
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD))
- if (MD->isInstance())
+ if (MD->isImplicitObjectMemberFunction())
return arrangeCXXMethodDeclaration(MD);
CanQualType FTy = FD->getType()->getCanonicalTypeUnqualified();
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index e7ed3ec..57a424c 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -138,7 +138,7 @@ Address CodeGenFunction::LoadCXXThisAddress() {
CXXThisAlignment = CGM.getClassPointerAlignment(MD->getParent());
}
- llvm::Type *Ty = ConvertType(MD->getThisObjectType());
+ llvm::Type *Ty = ConvertType(MD->getFunctionObjectParameterType());
return Address(LoadCXXThis(), Ty, CXXThisAlignment, KnownNonNull);
}
@@ -510,7 +510,7 @@ namespace {
const CXXDestructorDecl *D = BaseClass->getDestructor();
// We are already inside a destructor, so presumably the object being
// destroyed should have the expected type.
- QualType ThisTy = D->getThisObjectType();
+ QualType ThisTy = D->getFunctionObjectParameterType();
Address Addr =
CGF.GetAddressOfDirectBaseInCompleteClass(CGF.LoadCXXThisAddress(),
DerivedClass, BaseClass,
@@ -1456,7 +1456,7 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
RunCleanupsScope DtorEpilogue(*this);
EnterDtorCleanups(Dtor, Dtor_Deleting);
if (HaveInsertPoint()) {
- QualType ThisTy = Dtor->getThisObjectType();
+ QualType ThisTy = Dtor->getFunctionObjectParameterType();
EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false,
/*Delegating=*/false, LoadCXXThisAddress(), ThisTy);
}
@@ -1490,7 +1490,7 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
EnterDtorCleanups(Dtor, Dtor_Complete);
if (!isTryBody) {
- QualType ThisTy = Dtor->getThisObjectType();
+ QualType ThisTy = Dtor->getFunctionObjectParameterType();
EmitCXXDestructorCall(Dtor, Dtor_Base, /*ForVirtualBase=*/false,
/*Delegating=*/false, LoadCXXThisAddress(), ThisTy);
break;
@@ -2114,7 +2114,7 @@ void CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D,
CallArgList Args;
Address This = ThisAVS.getAddress();
LangAS SlotAS = ThisAVS.getQualifiers().getAddressSpace();
- LangAS ThisAS = D->getThisObjectType().getAddressSpace();
+ LangAS ThisAS = D->getFunctionObjectParameterType().getAddressSpace();
llvm::Value *ThisPtr = This.getPointer();
if (SlotAS != ThisAS) {
@@ -2453,7 +2453,7 @@ namespace {
void Emit(CodeGenFunction &CGF, Flags flags) override {
// We are calling the destructor from within the constructor.
// Therefore, "this" should have the expected type.
- QualType ThisTy = Dtor->getThisObjectType();
+ QualType ThisTy = Dtor->getFunctionObjectParameterType();
CGF.EmitCXXDestructorCall(Dtor, Type, /*ForVirtualBase=*/false,
/*Delegating=*/true, Addr, ThisTy);
}
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 36e2928..c73a63e 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2130,14 +2130,14 @@ CGDebugInfo::CollectTemplateParams(std::optional<TemplateArgs> OArgs,
// attribute, i.e. that value is not available at the host side.
if (!CGM.getLangOpts().CUDA || CGM.getLangOpts().CUDAIsDevice ||
!D->hasAttr<CUDADeviceAttr>()) {
- const CXXMethodDecl *MD;
// Variable pointer template parameters have a value that is the address
// of the variable.
if (const auto *VD = dyn_cast<VarDecl>(D))
V = CGM.GetAddrOfGlobalVar(VD);
// Member function pointers have special support for building them,
// though this is currently unsupported in LLVM CodeGen.
- else if ((MD = dyn_cast<CXXMethodDecl>(D)) && MD->isInstance())
+ else if (const auto *MD = dyn_cast<CXXMethodDecl>(D);
+ MD && MD->isImplicitObjectMemberFunction())
V = CGM.getCXXABI().EmitMemberFunctionPointer(MD);
else if (const auto *FD = dyn_cast<FunctionDecl>(D))
V = CGM.GetAddrOfFunction(FD);
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 7b4389c..1b6a2c1 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2647,9 +2647,8 @@ static LValue EmitFunctionDeclLValue(CodeGenFunction &CGF, const Expr *E,
static LValue EmitCapturedFieldLValue(CodeGenFunction &CGF, const FieldDecl *FD,
llvm::Value *ThisValue) {
- QualType TagType = CGF.getContext().getTagDeclType(FD->getParent());
- LValue LV = CGF.MakeNaturalAlignAddrLValue(ThisValue, TagType);
- return CGF.EmitLValueForField(LV, FD);
+
+ return CGF.EmitLValueForLambdaField(FD, ThisValue);
}
/// Named Registers are named metadata pointing to the register name
@@ -4262,17 +4261,38 @@ LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) {
/// Given that we are currently emitting a lambda, emit an l-value for
/// one of its members.
-LValue CodeGenFunction::EmitLValueForLambdaField(const FieldDecl *Field) {
- if (CurCodeDecl) {
- assert(cast<CXXMethodDecl>(CurCodeDecl)->getParent()->isLambda());
- assert(cast<CXXMethodDecl>(CurCodeDecl)->getParent() == Field->getParent());
+///
+LValue CodeGenFunction::EmitLValueForLambdaField(const FieldDecl *Field,
+ llvm::Value *ThisValue) {
+ bool HasExplicitObjectParameter = false;
+ if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(CurCodeDecl)) {
+ HasExplicitObjectParameter = MD->isExplicitObjectMemberFunction();
+ assert(MD->getParent()->isLambda());
+ assert(MD->getParent() == Field->getParent());
+ }
+ LValue LambdaLV;
+ if (HasExplicitObjectParameter) {
+ const VarDecl *D = cast<CXXMethodDecl>(CurCodeDecl)->getParamDecl(0);
+ auto It = LocalDeclMap.find(D);
+ assert(It != LocalDeclMap.end() && "explicit parameter not loaded?");
+ Address AddrOfExplicitObject = It->getSecond();
+ if (D->getType()->isReferenceType())
+ LambdaLV = EmitLoadOfReferenceLValue(AddrOfExplicitObject, D->getType(),
+ AlignmentSource::Decl);
+ else
+ LambdaLV = MakeNaturalAlignAddrLValue(AddrOfExplicitObject.getPointer(),
+ D->getType().getNonReferenceType());
+ } else {
+ QualType LambdaTagType = getContext().getTagDeclType(Field->getParent());
+ LambdaLV = MakeNaturalAlignAddrLValue(ThisValue, LambdaTagType);
}
- QualType LambdaTagType =
- getContext().getTagDeclType(Field->getParent());
- LValue LambdaLV = MakeNaturalAlignAddrLValue(CXXABIThisValue, LambdaTagType);
return EmitLValueForField(LambdaLV, Field);
}
+LValue CodeGenFunction::EmitLValueForLambdaField(const FieldDecl *Field) {
+ return EmitLValueForLambdaField(Field, CXXABIThisValue);
+}
+
/// Get the field index in the debug info. The debug info structure/union
/// will ignore the unnamed bitfields.
unsigned CodeGenFunction::getDebugInfoFIndex(const RecordDecl *Rec,
@@ -4987,9 +5007,12 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E,
if (const auto *CE = dyn_cast<CUDAKernelCallExpr>(E))
return EmitCUDAKernelCallExpr(CE, ReturnValue);
+ // A CXXOperatorCallExpr is created even for explicit object methods, but
+ // these should be treated like static function call.
if (const auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
- if (const CXXMethodDecl *MD =
- dyn_cast_or_null<CXXMethodDecl>(CE->getCalleeDecl()))
+ if (const auto *MD =
+ dyn_cast_if_present<CXXMethodDecl>(CE->getCalleeDecl());
+ MD && MD->isImplicitObjectMemberFunction())
return EmitCXXOperatorMemberCallExpr(CE, MD, ReturnValue);
CGCallee callee = EmitCallee(E->getCallee());
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 34d4f37..2e7059c 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -41,7 +41,7 @@ commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, GlobalDecl GD,
assert(CE == nullptr || isa<CXXMemberCallExpr>(CE) ||
isa<CXXOperatorCallExpr>(CE));
- assert(MD->isInstance() &&
+ assert(MD->isImplicitObjectMemberFunction() &&
"Trying to emit a member or operator call expr on a static method!");
// Push the this ptr.
@@ -66,7 +66,12 @@ commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, GlobalDecl GD,
Args.addFrom(*RtlArgs);
} else if (CE) {
// Special case: skip first argument of CXXOperatorCall (it is "this").
- unsigned ArgsToSkip = isa<CXXOperatorCallExpr>(CE) ? 1 : 0;
+ unsigned ArgsToSkip = 0;
+ if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(CE)) {
+ if (const auto *M = dyn_cast<CXXMethodDecl>(Op->getCalleeDecl()))
+ ArgsToSkip =
+ static_cast<unsigned>(!M->isExplicitObjectMemberFunction());
+ }
CGF.EmitCallArgs(Args, FPT, drop_begin(CE->arguments(), ArgsToSkip),
CE->getDirectCallee());
} else {
@@ -484,7 +489,7 @@ RValue
CodeGenFunction::EmitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *E,
const CXXMethodDecl *MD,
ReturnValueSlot ReturnValue) {
- assert(MD->isInstance() &&
+ assert(MD->isImplicitObjectMemberFunction() &&
"Trying to emit a member call expr on a static method!");
return EmitCXXMemberOrOperatorMemberCallExpr(
E, MD, ReturnValue, /*HasQualifier=*/false, /*Qualifier=*/nullptr,
diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
index b4e482c..8faee62 100644
--- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp
+++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -8260,7 +8260,7 @@ public:
// of tofrom.
// Emit this[:1]
CombinedInfo.Pointers.push_back(PartialStruct.Base.getPointer());
- QualType Ty = MD->getThisObjectType();
+ QualType Ty = MD->getFunctionObjectParameterType();
llvm::Value *Size =
CGF.Builder.CreateIntCast(CGF.getTypeSize(Ty), CGF.Int64Ty,
/*isSigned=*/true);
diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index 23cfcdd..2f1ac37b 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -201,7 +201,7 @@ CodeGenFunction::GenerateVarArgsThunk(llvm::Function *Fn,
// Find the first store of "this", which will be to the alloca associated
// with "this".
Address ThisPtr =
- Address(&*AI, ConvertTypeForMem(MD->getThisObjectType()),
+ Address(&*AI, ConvertTypeForMem(MD->getFunctionObjectParameterType()),
CGM.getClassPointerAlignment(MD->getParent()));
llvm::BasicBlock *EntryBB = &Fn->front();
llvm::BasicBlock::iterator ThisStore =
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index bf83171..3eb4cb8 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1167,12 +1167,13 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
EmitFunctionProlog(*CurFnInfo, CurFn, Args);
- if (isa_and_nonnull<CXXMethodDecl>(D) &&
- cast<CXXMethodDecl>(D)->isInstance()) {
- CGM.getCXXABI().EmitInstanceFunctionProlog(*this);
- const CXXMethodDecl *MD = cast<CXXMethodDecl>(D);
- if (MD->getParent()->isLambda() &&
- MD->getOverloadedOperator() == OO_Call) {
+ if (const CXXMethodDecl *MD = dyn_cast_if_present<CXXMethodDecl>(D);
+ MD && !MD->isStatic()) {
+ bool IsInLambda =
+ MD->getParent()->isLambda() && MD->getOverloadedOperator() == OO_Call;
+ if (MD->isImplicitObjectMemberFunction())
+ CGM.getCXXABI().EmitInstanceFunctionProlog(*this);
+ if (IsInLambda) {
// We're in a lambda; figure out the captures.
MD->getParent()->getCaptureFields(LambdaCaptureFields,
LambdaThisCaptureField);
@@ -1202,7 +1203,7 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
VLASizeMap[VAT->getSizeExpr()] = ExprArg;
}
}
- } else {
+ } else if (MD->isImplicitObjectMemberFunction()) {
// Not in a lambda; just use 'this' from the method.
// FIXME: Should we generate a new load for each use of 'this'? The
// fast register allocator would be happier...
@@ -1313,7 +1314,7 @@ QualType CodeGenFunction::BuildFunctionArgList(GlobalDecl GD,
QualType ResTy = FD->getReturnType();
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
- if (MD && MD->isInstance()) {
+ if (MD && MD->isImplicitObjectMemberFunction()) {
if (CGM.getCXXABI().HasThisReturn(GD))
ResTy = MD->getThisType();
else if (CGM.getCXXABI().hasMostDerivedReturn(GD))
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 60f2f21..471aad9 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4017,6 +4017,8 @@ public:
const ObjCIvarDecl *Ivar);
LValue EmitLValueForField(LValue Base, const FieldDecl* Field);
LValue EmitLValueForLambdaField(const FieldDecl *Field);
+ LValue EmitLValueForLambdaField(const FieldDecl *Field,
+ llvm::Value *ThisValue);
/// EmitLValueForFieldInitialization - Like EmitLValueForField, except that
/// if the Field is a reference, this will return the address of the reference
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 925f511..93c3f8d 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -2246,8 +2246,8 @@ static bool requiresMemberFunctionPointerTypeMetadata(CodeGenModule &CGM,
// Only functions whose address can be taken with a member function pointer
// need this sort of type metadata.
- return !MD->isStatic() && !MD->isVirtual() && !isa<CXXConstructorDecl>(MD) &&
- !isa<CXXDestructorDecl>(MD);
+ return MD->isImplicitObjectMemberFunction() && !MD->isVirtual() &&
+ !isa<CXXConstructorDecl, CXXDestructorDecl>(MD);
}
SmallVector<const CXXRecordDecl *, 0>
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 9bda4ec..a149159 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -5835,8 +5835,7 @@ bool Parser::isDeclarationSpecifier(
bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
DeclSpec::FriendSpecified IsFriend,
const ParsedTemplateInfo *TemplateInfo) {
- TentativeParsingAction TPA(*this);
-
+ RevertingTentativeParsingAction TPA(*this);
// Parse the C++ scope specifier.
CXXScopeSpec SS;
if (TemplateInfo && TemplateInfo->TemplateParams)
@@ -5845,7 +5844,6 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
/*ObjectHasErrors=*/false,
/*EnteringContext=*/true)) {
- TPA.Revert();
return false;
}
@@ -5857,7 +5855,6 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
} else if (Tok.is(tok::annot_template_id)) {
ConsumeAnnotationToken();
} else {
- TPA.Revert();
return false;
}
@@ -5867,7 +5864,6 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
// Current class name must be followed by a left parenthesis.
if (Tok.isNot(tok::l_paren)) {
- TPA.Revert();
return false;
}
ConsumeParen();
@@ -5876,7 +5872,6 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
// that we have a constructor.
if (Tok.is(tok::r_paren) ||
(Tok.is(tok::ellipsis) && NextToken().is(tok::r_paren))) {
- TPA.Revert();
return true;
}
@@ -5885,7 +5880,6 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
if (getLangOpts().CPlusPlus11 &&
isCXX11AttributeSpecifier(/*Disambiguate*/ false,
/*OuterMightBeMessageSend*/ true)) {
- TPA.Revert();
return true;
}
@@ -5906,9 +5900,17 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
// If we parsed a scope specifier as well as friend,
// we might be parsing a friend constructor.
bool IsConstructor = false;
- if (isDeclarationSpecifier(IsFriend && !SS.isSet()
- ? ImplicitTypenameContext::No
- : ImplicitTypenameContext::Yes))
+ ImplicitTypenameContext ITC = IsFriend && !SS.isSet()
+ ? ImplicitTypenameContext::No
+ : ImplicitTypenameContext::Yes;
+ // Constructors cannot have this parameters, but we support that scenario here
+ // to improve diagnostic.
+ if (Tok.is(tok::kw_this)) {
+ ConsumeToken();
+ return isDeclarationSpecifier(ITC);
+ }
+
+ if (isDeclarationSpecifier(ITC))
IsConstructor = true;
else if (Tok.is(tok::identifier) ||
(Tok.is(tok::annot_cxxscope) && NextToken().is(tok::identifier))) {
@@ -5977,8 +5979,6 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
break;
}
}
-
- TPA.Revert();
return IsConstructor;
}
@@ -7356,6 +7356,7 @@ void Parser::ParseFunctionDeclaratorIdentifierList(
/// '=' assignment-expression
/// [GNU] declaration-specifiers abstract-declarator[opt] attributes
/// [C++11] attribute-specifier-seq parameter-declaration
+/// [C++2b] attribute-specifier-seq 'this' parameter-declaration
///
void Parser::ParseParameterDeclarationClause(
DeclaratorContext DeclaratorCtx, ParsedAttributes &FirstArgAttrs,
@@ -7420,9 +7421,16 @@ void Parser::ParseParameterDeclarationClause(
SourceLocation DSStart = Tok.getLocation();
+ // Parse a C++23 Explicit Object Parameter
+ // We do that in all language modes to produce a better diagnostic.
+ SourceLocation ThisLoc;
+ if (getLangOpts().CPlusPlus && Tok.is(tok::kw_this))
+ ThisLoc = ConsumeToken();
+
ParseDeclarationSpecifiers(DS, /*TemplateInfo=*/ParsedTemplateInfo(),
AS_none, DeclSpecContext::DSC_normal,
/*LateAttrs=*/nullptr, AllowImplicitTypename);
+
DS.takeAttributesFrom(ArgDeclSpecAttrs);
// Parse the declarator. This is "PrototypeContext" or
@@ -7436,6 +7444,9 @@ void Parser::ParseParameterDeclarationClause(
: DeclaratorContext::Prototype);
ParseDeclarator(ParmDeclarator);
+ if (ThisLoc.isValid())
+ ParmDeclarator.SetRangeBegin(ThisLoc);
+
// Parse GNU attributes, if present.
MaybeParseGNUAttributes(ParmDeclarator);
if (getLangOpts().HLSL)
@@ -7505,7 +7516,8 @@ void Parser::ParseParameterDeclarationClause(
}
// Inform the actions module about the parameter declarator, so it gets
// added to the current scope.
- Decl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDeclarator);
+ Decl *Param =
+ Actions.ActOnParamDeclarator(getCurScope(), ParmDeclarator, ThisLoc);
// Parse the default argument, if any. We parse the default
// arguments in all dialects; the semantic analysis in
// ActOnParamDefaultArgument will reject the default argument in
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 03c19bf..8b653b1 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1571,6 +1571,17 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
case tok::kw___vector:
return TPResult::True;
+ case tok::kw_this: {
+ // Try to parse a C++23 Explicit Object Parameter
+ // We do that in all language modes to produce a better diagnostic.
+ if (getLangOpts().CPlusPlus) {
+ RevertingTentativeParsingAction PA(*this);
+ ConsumeToken();
+ return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult,
+ InvalidAsDeclSpec);
+ }
+ return TPResult::False;
+ }
case tok::annot_template_id: {
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
// If lookup for the template-name found nothing, don't assume we have a
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index d59778b..2d0d575 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -420,6 +420,18 @@ bool Declarator::isStaticMember() {
getName().OperatorFunctionId.Operator));
}
+bool Declarator::isExplicitObjectMemberFunction() {
+ if (!isFunctionDeclarator())
+ return false;
+ DeclaratorChunk::FunctionTypeInfo &Fun = getFunctionTypeInfo();
+ if (Fun.NumParams) {
+ auto *P = dyn_cast_or_null<ParmVarDecl>(Fun.Params[0].Param);
+ if (P && P->isExplicitObjectParameter())
+ return true;
+ }
+ return false;
+}
+
bool Declarator::isCtorOrDtor() {
return (getName().getKind() == UnqualifiedIdKind::IK_ConstructorName) ||
(getName().getKind() == UnqualifiedIdKind::IK_DestructorName);
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index af24d72..fd86a5f 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -175,8 +175,8 @@ struct BuiltinTypeDeclBuilder {
SourceLocation(), FPOptionsOverride());
CXXThisExpr *This = CXXThisExpr::Create(
- AST, SourceLocation(),
- Constructor->getThisObjectType(), true);
+ AST, SourceLocation(), Constructor->getFunctionObjectParameterType(),
+ true);
Expr *Handle = MemberExpr::CreateImplicit(AST, This, false, Fields["h"],
Fields["h"]->getType(), VK_LValue,
OK_Ordinary);
@@ -260,9 +260,9 @@ struct BuiltinTypeDeclBuilder {
auto FnProtoLoc = TSInfo->getTypeLoc().getAs<FunctionProtoTypeLoc>();
FnProtoLoc.setParam(0, IdxParam);
- auto *This = CXXThisExpr::Create(
- AST, SourceLocation(),
- MethodDecl->getThisObjectType(), true);
+ auto *This =
+ CXXThisExpr::Create(AST, SourceLocation(),
+ MethodDecl->getFunctionObjectParameterType(), true);
auto *HandleAccess = MemberExpr::CreateImplicit(
AST, This, false, Handle, Handle->getType(), VK_LValue, OK_Ordinary);
diff --git a/clang/lib/Sema/ScopeInfo.cpp b/clang/lib/Sema/ScopeInfo.cpp
index 92ce513..ce90451 100644
--- a/clang/lib/Sema/ScopeInfo.cpp
+++ b/clang/lib/Sema/ScopeInfo.cpp
@@ -248,6 +248,14 @@ void LambdaScopeInfo::visitPotentialCaptures(
}
}
+bool LambdaScopeInfo::lambdaCaptureShouldBeConst() const {
+ if (ExplicitObjectParameter)
+ return ExplicitObjectParameter->getType()
+ .getNonReferenceType()
+ .isConstQualified();
+ return !Mutable;
+}
+
FunctionScopeInfo::~FunctionScopeInfo() { }
BlockScopeInfo::~BlockScopeInfo() { }
CapturedRegionScopeInfo::~CapturedRegionScopeInfo() { }
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 6a3b5fa..0b9bee4 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -126,7 +126,7 @@ static bool checkArgCountAtLeast(Sema &S, CallExpr *Call,
return S.Diag(Call->getEndLoc(), diag::err_typecheck_call_too_few_args)
<< 0 /*function call*/ << MinArgCount << ArgCount
- << Call->getSourceRange();
+ << /*is non object*/ 0 << Call->getSourceRange();
}
/// Checks that a call expression's argument count is at most the desired
@@ -139,7 +139,7 @@ static bool checkArgCountAtMost(Sema &S, CallExpr *Call, unsigned MaxArgCount) {
return S.Diag(Call->getEndLoc(),
diag::err_typecheck_call_too_many_args_at_most)
<< 0 /*function call*/ << MaxArgCount << ArgCount
- << Call->getSourceRange();
+ << /*is non object*/ 0 << Call->getSourceRange();
}
/// Checks that a call expression's argument count is in the desired range. This
@@ -168,7 +168,7 @@ static bool checkArgCount(Sema &S, CallExpr *Call, unsigned DesiredArgCount) {
return S.Diag(Range.getBegin(), diag::err_typecheck_call_too_many_args)
<< 0 /*function call*/ << DesiredArgCount << ArgCount
- << Call->getArg(1)->getSourceRange();
+ << /*is non object*/ 0 << Call->getArg(1)->getSourceRange();
}
static bool convertArgumentToType(Sema &S, Expr *&Value, QualType Ty) {
@@ -217,7 +217,7 @@ static bool SemaBuiltinMSVCAnnotation(Sema &S, CallExpr *TheCall) {
// We need at least one argument.
if (TheCall->getNumArgs() < 1) {
S.Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args_at_least)
- << 0 << 1 << TheCall->getNumArgs()
+ << 0 << 1 << TheCall->getNumArgs() << /*is non object*/ 0
<< TheCall->getCallee()->getSourceRange();
return true;
}
@@ -1578,7 +1578,7 @@ static bool SemaOpenCLBuiltinEnqueueKernel(Sema &S, CallExpr *TheCall) {
if (NumArgs < 4) {
S.Diag(TheCall->getBeginLoc(),
diag::err_typecheck_call_too_few_args_at_least)
- << 0 << 4 << NumArgs;
+ << 0 << 4 << NumArgs << /*is non object*/ 0;
return true;
}
@@ -6924,8 +6924,9 @@ void Sema::CheckConstructorCall(FunctionDecl *FDecl, QualType ThisType,
Proto->isVariadic() ? VariadicConstructor : VariadicDoesNotApply;
auto *Ctor = cast<CXXConstructorDecl>(FDecl);
- CheckArgAlignment(Loc, FDecl, "'this'", Context.getPointerType(ThisType),
- Context.getPointerType(Ctor->getThisObjectType()));
+ CheckArgAlignment(
+ Loc, FDecl, "'this'", Context.getPointerType(ThisType),
+ Context.getPointerType(Ctor->getFunctionObjectParameterType()));
checkCall(FDecl, Proto, /*ThisArg=*/nullptr, Args, /*IsMemberFunction=*/true,
Loc, SourceRange(), CallType);
@@ -6945,14 +6946,16 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
unsigned NumArgs = TheCall->getNumArgs();
Expr *ImplicitThis = nullptr;
- if (IsMemberOperatorCall && !FDecl->isStatic()) {
+ if (IsMemberOperatorCall && !FDecl->isStatic() &&
+ !FDecl->hasCXXExplicitFunctionObjectParameter()) {
// If this is a call to a non-static member operator, hide the first
// argument from checkCall.
// FIXME: Our choice of AST representation here is less than ideal.
ImplicitThis = Args[0];
++Args;
--NumArgs;
- } else if (IsMemberFunction && !FDecl->isStatic())
+ } else if (IsMemberFunction && !FDecl->isStatic() &&
+ !FDecl->hasCXXExplicitFunctionObjectParameter())
ImplicitThis =
cast<CXXMemberCallExpr>(TheCall)->getImplicitObjectArgument();
@@ -6965,8 +6968,8 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
ThisType = Context.getPointerType(ThisType);
}
- QualType ThisTypeFromDecl =
- Context.getPointerType(cast<CXXMethodDecl>(FDecl)->getThisObjectType());
+ QualType ThisTypeFromDecl = Context.getPointerType(
+ cast<CXXMethodDecl>(FDecl)->getFunctionObjectParameterType());
CheckArgAlignment(TheCall->getRParenLoc(), FDecl, "'this'", ThisType,
ThisTypeFromDecl);
@@ -7293,13 +7296,13 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
if (Args.size() < AdjustedNumArgs) {
Diag(CallRange.getEnd(), diag::err_typecheck_call_too_few_args)
<< 0 << AdjustedNumArgs << static_cast<unsigned>(Args.size())
- << ExprRange;
+ << /*is non object*/ 0 << ExprRange;
return ExprError();
} else if (Args.size() > AdjustedNumArgs) {
Diag(Args[AdjustedNumArgs]->getBeginLoc(),
diag::err_typecheck_call_too_many_args)
<< 0 << AdjustedNumArgs << static_cast<unsigned>(Args.size())
- << ExprRange;
+ << /*is non object*/ 0 << ExprRange;
return ExprError();
}
@@ -7662,7 +7665,8 @@ bool Sema::BuiltinWasmRefNullExtern(CallExpr *TheCall) {
bool Sema::BuiltinWasmRefNullFunc(CallExpr *TheCall) {
if (TheCall->getNumArgs() != 0) {
Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_many_args)
- << 0 /*function call*/ << 0 << TheCall->getNumArgs();
+ << 0 /*function call*/ << /*expected*/ 0 << TheCall->getNumArgs()
+ << /*is non object*/ 0;
return true;
}
@@ -7695,7 +7699,8 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) {
// Ensure that we have at least one argument to do type inference from.
if (TheCall->getNumArgs() < 1) {
Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args_at_least)
- << 0 << 1 << TheCall->getNumArgs() << Callee->getSourceRange();
+ << 0 << 1 << TheCall->getNumArgs() << /*is non object*/ 0
+ << Callee->getSourceRange();
return ExprError();
}
@@ -7971,7 +7976,7 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) {
// have at least that many.
if (TheCall->getNumArgs() < 1+NumFixed) {
Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args_at_least)
- << 0 << 1 + NumFixed << TheCall->getNumArgs()
+ << 0 << 1 + NumFixed << TheCall->getNumArgs() << /*is non object*/ 0
<< Callee->getSourceRange();
return ExprError();
}
@@ -8361,7 +8366,8 @@ bool Sema::SemaBuiltinVAStartARMMicrosoft(CallExpr *Call) {
if (Call->getNumArgs() < 3)
return Diag(Call->getEndLoc(),
diag::err_typecheck_call_too_few_args_at_least)
- << 0 /*function call*/ << 3 << Call->getNumArgs();
+ << 0 /*function call*/ << 3 << Call->getNumArgs()
+ << /*is non object*/ 0;
// Type-check the first argument normally.
if (checkBuiltinArgument(*this, Call, 0))
@@ -8622,7 +8628,7 @@ ExprResult Sema::SemaBuiltinShuffleVector(CallExpr *TheCall) {
return ExprError(Diag(TheCall->getEndLoc(),
diag::err_typecheck_call_too_few_args_at_least)
<< 0 /*function call*/ << 2 << TheCall->getNumArgs()
- << TheCall->getSourceRange());
+ << /*is non object*/ 0 << TheCall->getSourceRange());
// Determine which of the following types of shufflevector we're checking:
// 1) unary, vector mask: (lhs, mask)
@@ -8742,7 +8748,8 @@ bool Sema::SemaBuiltinPrefetch(CallExpr *TheCall) {
if (NumArgs > 3)
return Diag(TheCall->getEndLoc(),
diag::err_typecheck_call_too_many_args_at_most)
- << 0 /*function call*/ << 3 << NumArgs << TheCall->getSourceRange();
+ << 0 /*function call*/ << 3 << NumArgs << /*is non object*/ 0
+ << TheCall->getSourceRange();
// Argument 0 is checked for us and the remaining arguments must be
// constant integers.
@@ -8881,13 +8888,13 @@ bool Sema::SemaBuiltinOSLogFormat(CallExpr *TheCall) {
if (NumArgs < NumRequiredArgs) {
return Diag(TheCall->getEndLoc(), diag::err_typecheck_call_too_few_args)
<< 0 /* function call */ << NumRequiredArgs << NumArgs
- << TheCall->getSourceRange();
+ << /*is non object*/ 0 << TheCall->getSourceRange();
}
if (NumArgs >= NumRequiredArgs + 0x100) {
return Diag(TheCall->getEndLoc(),
diag::err_typecheck_call_too_many_args_at_most)
<< 0 /* function call */ << (NumRequiredArgs + 0xff) << NumArgs
- << TheCall->getSourceRange();
+ << /*is non object*/ 0 << TheCall->getSourceRange();
}
unsigned i = 0;
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index cfbe176..d2b0922 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -70,7 +70,7 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD,
// If the function is a non-static member function, add the type
// of the implicit object parameter before the formal parameters.
if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
- if (MD->isInstance()) {
+ if (MD->isImplicitObjectMemberFunction()) {
// [over.match.funcs]4
// For non-static member functions, the type of the implicit object
// parameter is
@@ -78,7 +78,7 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD,
// ref-qualifier or with the & ref-qualifier
// -- "rvalue reference to cv X" for functions declared with the &&
// ref-qualifier
- QualType T = MD->getThisObjectType();
+ QualType T = MD->getFunctionObjectParameterType();
T = FnType->getRefQualifier() == RQ_RValue
? S.Context.getRValueReferenceType(T)
: S.Context.getLValueReferenceType(T, /*SpelledAsLValue*/ true);
@@ -564,10 +564,10 @@ VarDecl *Sema::buildCoroutinePromise(SourceLocation Loc) {
assert(isa<FunctionDecl>(CurContext) && "not in a function scope");
auto *FD = cast<FunctionDecl>(CurContext);
bool IsThisDependentType = [&] {
- if (auto *MD = dyn_cast_or_null<CXXMethodDecl>(FD))
- return MD->isInstance() && MD->getThisObjectType()->isDependentType();
- else
- return false;
+ if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(FD))
+ return MD->isImplicitObjectMemberFunction() &&
+ MD->getThisType()->isDependentType();
+ return false;
}();
QualType T = FD->getType()->isDependentType() || IsThisDependentType
@@ -592,7 +592,7 @@ VarDecl *Sema::buildCoroutinePromise(SourceLocation Loc) {
// Add implicit object parameter.
if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
- if (MD->isInstance() && !isLambdaCallOperator(MD)) {
+ if (MD->isImplicitObjectMemberFunction() && !isLambdaCallOperator(MD)) {
ExprResult ThisExpr = ActOnCXXThis(Loc);
if (ThisExpr.isInvalid())
return nullptr;
@@ -1367,7 +1367,7 @@ bool CoroutineStmtBuilder::makeReturnOnAllocFailure() {
static bool collectPlacementArgs(Sema &S, FunctionDecl &FD, SourceLocation Loc,
SmallVectorImpl<Expr *> &PlacementArgs) {
if (auto *MD = dyn_cast<CXXMethodDecl>(&FD)) {
- if (MD->isInstance() && !isLambdaCallOperator(MD)) {
+ if (MD->isImplicitObjectMemberFunction() && !isLambdaCallOperator(MD)) {
ExprResult ThisExpr = S.ActOnCXXThis(Loc);
if (ThisExpr.isInvalid())
return false;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 9c2f1e8..0a301e5 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -8924,13 +8924,11 @@ bool Sema::AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) {
CXXMethodDecl *BaseMD =
dyn_cast<CXXMethodDecl>(BaseND->getCanonicalDecl());
if (!BaseMD || !BaseMD->isVirtual() ||
- IsOverload(MD, BaseMD, /*UseMemberUsingDeclRules=*/false,
- /*ConsiderCudaAttrs=*/true,
- // C++2a [class.virtual]p2 does not consider requires
- // clauses when overriding.
- /*ConsiderRequiresClauses=*/false))
+ IsOverride(MD, BaseMD, /*UseMemberUsingDeclRules=*/false,
+ /*ConsiderCudaAttrs=*/true))
+ continue;
+ if (!CheckExplicitObjectOverride(MD, BaseMD))
continue;
-
if (Overridden.insert(BaseMD).second) {
MD->addOverriddenMethod(BaseMD);
CheckOverridingFunctionReturnType(MD, BaseMD);
@@ -9265,6 +9263,8 @@ static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
}
Expr *TrailingRequiresClause = D.getTrailingRequiresClause();
+ SemaRef.CheckExplicitObjectMemberFunction(DC, D, Name, R);
+
if (Name.getNameKind() == DeclarationName::CXXConstructorName) {
// This is a C++ constructor declaration.
assert(DC->isRecord() &&
@@ -9766,8 +9766,9 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
<< DeclSpec::getSpecifierName(TSCS);
if (D.isFirstDeclarationOfMember())
- adjustMemberFunctionCC(R, D.isStaticMember(), D.isCtorOrDtor(),
- D.getIdentifierLoc());
+ adjustMemberFunctionCC(
+ R, !(D.isStaticMember() || D.isExplicitObjectMemberFunction()),
+ D.isCtorOrDtor(), D.getIdentifierLoc());
bool isFriend = false;
FunctionTemplateDecl *FunctionTemplate = nullptr;
@@ -11971,7 +11972,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
// struct B { struct Y { ~Y(); }; using X = Y; };
// template struct A<B>;
if (NewFD->getFriendObjectKind() == Decl::FriendObjectKind::FOK_None ||
- !Destructor->getThisObjectType()->isDependentType()) {
+ !Destructor->getFunctionObjectParameterType()->isDependentType()) {
CXXRecordDecl *Record = Destructor->getParent();
QualType ClassType = Context.getTypeDeclType(Record);
@@ -14918,9 +14919,32 @@ void Sema::CheckFunctionOrTemplateParamDeclarator(Scope *S, Declarator &D) {
}
}
+static void CheckExplicitObjectParameter(Sema &S, ParmVarDecl *P,
+ SourceLocation ExplicitThisLoc) {
+ if (!ExplicitThisLoc.isValid())
+ return;
+ assert(S.getLangOpts().CPlusPlus &&
+ "explicit parameter in non-cplusplus mode");
+ if (!S.getLangOpts().CPlusPlus23)
+ S.Diag(ExplicitThisLoc, diag::err_cxx20_deducing_this)
+ << P->getSourceRange();
+
+ // C++2b [dcl.fct/7] An explicit object parameter shall not be a function
+ // parameter pack.
+ if (P->isParameterPack()) {
+ S.Diag(P->getBeginLoc(), diag::err_explicit_object_parameter_pack)
+ << P->getSourceRange();
+ return;
+ }
+ P->setExplicitObjectParameterLoc(ExplicitThisLoc);
+ if (LambdaScopeInfo *LSI = S.getCurLambda())
+ LSI->ExplicitObjectParameter = P;
+}
+
/// ActOnParamDeclarator - Called from Parser::ParseFunctionDeclarator()
/// to introduce parameters into function prototype scope.
-Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D) {
+Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D,
+ SourceLocation ExplicitThisLoc) {
const DeclSpec &DS = D.getDeclSpec();
// Verify C99 6.7.5.3p2: The only SCS allowed is 'register'.
@@ -14998,6 +15022,8 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D) {
if (D.isInvalidType())
New->setInvalidDecl();
+ CheckExplicitObjectParameter(*this, New, ExplicitThisLoc);
+
assert(S->isFunctionPrototypeScope());
assert(S->getFunctionPrototypeDepth() >= 1);
New->setScopeInfo(S->getFunctionPrototypeDepth() - 1,
@@ -15395,6 +15421,8 @@ LambdaScopeInfo *Sema::RebuildLambdaScopeInfo(CXXMethodDecl *CallOperator) {
LSI->IntroducerRange = DNI.getCXXOperatorNameRange();
LSI->Mutable = !CallOperator->isConst();
+ if (CallOperator->isExplicitObjectMemberFunction())
+ LSI->ExplicitObjectParameter = CallOperator->getParamDecl(0);
// Add the captures to the LSI so they can be noted as already
// captured within tryCaptureVar.
@@ -18781,10 +18809,13 @@ static bool AreSpecialMemberFunctionsSameKind(ASTContext &Context,
if (CSM == Sema::CXXDefaultConstructor)
return bool(M1->getDescribedFunctionTemplate()) ==
bool(M2->getDescribedFunctionTemplate());
- if (!Context.hasSameType(M1->getParamDecl(0)->getType(),
- M2->getParamDecl(0)->getType()))
+ // FIXME: better resolve CWG
+ // https://cplusplus.github.io/CWG/issues/2787.html
+ if (!Context.hasSameType(M1->getNonObjectParameter(0)->getType(),
+ M2->getNonObjectParameter(0)->getType()))
return false;
- if (!Context.hasSameType(M1->getThisType(), M2->getThisType()))
+ if (!Context.hasSameType(M1->getFunctionObjectParameterReferenceType(),
+ M2->getFunctionObjectParameterReferenceType()))
return false;
return true;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 2d7e699..ed0b4d2 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1234,7 +1234,7 @@ static void handleConsumableAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
static bool checkForConsumableClass(Sema &S, const CXXMethodDecl *MD,
const ParsedAttr &AL) {
- QualType ThisType = MD->getThisObjectType();
+ QualType ThisType = MD->getFunctionObjectParameterType();
if (const CXXRecordDecl *RD = ThisType->getAsCXXRecordDecl()) {
if (!RD->hasAttr<ConsumableAttr>()) {
@@ -1336,23 +1336,23 @@ static void handleReturnTypestateAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
// FIXME: This check is currently being done in the analysis. It can be
// enabled here only after the parser propagates attributes at
// template specialization definition, not declaration.
- //QualType ReturnType;
+ // QualType ReturnType;
//
- //if (const ParmVarDecl *Param = dyn_cast<ParmVarDecl>(D)) {
+ // if (const ParmVarDecl *Param = dyn_cast<ParmVarDecl>(D)) {
// ReturnType = Param->getType();
//
//} else if (const CXXConstructorDecl *Constructor =
// dyn_cast<CXXConstructorDecl>(D)) {
- // ReturnType = Constructor->getThisObjectType();
+ // ReturnType = Constructor->getFunctionObjectParameterType();
//
//} else {
//
// ReturnType = cast<FunctionDecl>(D)->getCallResultType();
//}
//
- //const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl();
+ // const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl();
//
- //if (!RD || !RD->hasAttr<ConsumableAttr>()) {
+ // if (!RD || !RD->hasAttr<ConsumableAttr>()) {
// S.Diag(Attr.getLoc(), diag::warn_return_state_for_unconsumable_type) <<
// ReturnType.getAsString();
// return;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 302e944..0a48d33 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7692,7 +7692,7 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
unsigned ExpectedParams = 1;
if (CSM == CXXDefaultConstructor || CSM == CXXDestructor)
ExpectedParams = 0;
- if (MD->getNumParams() != ExpectedParams) {
+ if (MD->getNumExplicitParams() != ExpectedParams) {
// This checks for default arguments: a copy or move constructor with a
// default argument is classified as a default constructor, and assignment
// operations and destructors can't have default arguments.
@@ -7721,10 +7721,12 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
if (CSM == CXXCopyAssignment || CSM == CXXMoveAssignment) {
// Check for return type matching.
ReturnType = Type->getReturnType();
+ QualType ThisType = MD->getFunctionObjectParameterType();
QualType DeclType = Context.getTypeDeclType(RD);
DeclType = Context.getElaboratedType(ETK_None, nullptr, DeclType, nullptr);
- DeclType = Context.getAddrSpaceQualType(DeclType, MD->getMethodQualifiers().getAddressSpace());
+ DeclType = Context.getAddrSpaceQualType(
+ DeclType, ThisType.getQualifiers().getAddressSpace());
QualType ExpectedReturnType = Context.getLValueReferenceType(DeclType);
if (!Context.hasSameType(ReturnType, ExpectedReturnType)) {
@@ -7734,7 +7736,7 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
}
// A defaulted special member cannot have cv-qualifiers.
- if (Type->getMethodQuals().hasConst() || Type->getMethodQuals().hasVolatile()) {
+ if (ThisType.isConstQualified() || ThisType.isVolatileQualified()) {
if (DeleteOnTypeMismatch)
ShouldDeleteForTypeMismatch = true;
else {
@@ -7746,7 +7748,10 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
}
// Check for parameter type matching.
- QualType ArgType = ExpectedParams ? Type->getParamType(0) : QualType();
+ QualType ArgType =
+ ExpectedParams
+ ? Type->getParamType(MD->isExplicitObjectMemberFunction() ? 1 : 0)
+ : QualType();
bool HasConstParam = false;
if (ExpectedParams && ArgType->isReferenceType()) {
// Argument must be reference to possibly-const T.
@@ -7840,8 +7845,8 @@ bool Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
FunctionProtoType::ExtProtoInfo EPI = Type->getExtProtoInfo();
EPI.ExceptionSpec.Type = EST_Unevaluated;
EPI.ExceptionSpec.SourceDecl = MD;
- MD->setType(Context.getFunctionType(
- ReturnType, llvm::ArrayRef(&ArgType, ExpectedParams), EPI));
+ MD->setType(
+ Context.getFunctionType(ReturnType, Type->getParamTypes(), EPI));
}
}
@@ -8492,7 +8497,8 @@ private:
ExprPair getCompleteObject() {
unsigned Param = 0;
ExprResult LHS;
- if (isa<CXXMethodDecl>(FD)) {
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD);
+ MD && MD->isImplicitObjectMemberFunction()) {
// LHS is '*this'.
LHS = S.ActOnCXXThis(Loc);
if (!LHS.isInvalid())
@@ -8798,15 +8804,22 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
// If we're out-of-class, this is the class we're comparing.
if (!RD)
RD = MD->getParent();
-
- if (!MD->isConst()) {
- SourceLocation InsertLoc;
- if (FunctionTypeLoc Loc = MD->getFunctionTypeLoc())
- InsertLoc = getLocForEndOfToken(Loc.getRParenLoc());
+ QualType T = MD->getFunctionObjectParameterType();
+ if (!T.isConstQualified()) {
+ SourceLocation Loc, InsertLoc;
+ if (MD->isExplicitObjectMemberFunction()) {
+ Loc = MD->getParamDecl(0)->getBeginLoc();
+ InsertLoc = getLocForEndOfToken(
+ MD->getParamDecl(0)->getExplicitObjectParamThisLoc());
+ } else {
+ Loc = MD->getLocation();
+ if (FunctionTypeLoc Loc = MD->getFunctionTypeLoc())
+ InsertLoc = Loc.getRParenLoc();
+ }
// Don't diagnose an implicit 'operator=='; we will have diagnosed the
// corresponding defaulted 'operator<=>' already.
if (!MD->isImplicit()) {
- Diag(MD->getLocation(), diag::err_defaulted_comparison_non_const)
+ Diag(Loc, diag::err_defaulted_comparison_non_const)
<< (int)DCK << FixItHint::CreateInsertion(InsertLoc, " const");
}
@@ -8830,7 +8843,9 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
}
}
- if (FD->getNumParams() != (IsMethod ? 1 : 2)) {
+ if ((FD->getNumParams() -
+ (unsigned)FD->hasCXXExplicitFunctionObjectParameter()) !=
+ (IsMethod ? 1 : 2)) {
// Let's not worry about using a variadic template pack here -- who would do
// such a thing?
Diag(FD->getLocation(), diag::err_defaulted_comparison_num_args)
@@ -8840,6 +8855,8 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
const ParmVarDecl *KnownParm = nullptr;
for (const ParmVarDecl *Param : FD->parameters()) {
+ if (Param->isExplicitObjectParameter())
+ continue;
QualType ParmTy = Param->getType();
if (!KnownParm) {
@@ -9223,9 +9240,9 @@ struct SpecialMemberVisitor {
llvm_unreachable("invalid special member kind");
}
- if (MD->getNumParams()) {
+ if (MD->getNumExplicitParams()) {
if (const ReferenceType *RT =
- MD->getParamDecl(0)->getType()->getAs<ReferenceType>())
+ MD->getNonObjectParameter(0)->getType()->getAs<ReferenceType>())
ConstArg = RT->getPointeeType().isConstQualified();
}
}
@@ -10105,7 +10122,7 @@ bool Sema::SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM,
case CXXCopyConstructor:
case CXXCopyAssignment: {
- const ParmVarDecl *Param0 = MD->getParamDecl(0);
+ const ParmVarDecl *Param0 = MD->getNonObjectParameter(0);
const ReferenceType *RT = Param0->getType()->getAs<ReferenceType>();
// When ClangABICompat14 is true, CXX copy constructors will only be trivial
@@ -10136,7 +10153,7 @@ bool Sema::SpecialMemberIsTrivial(CXXMethodDecl *MD, CXXSpecialMember CSM,
case CXXMoveConstructor:
case CXXMoveAssignment: {
// Trivial move operations always have non-cv-qualified parameters.
- const ParmVarDecl *Param0 = MD->getParamDecl(0);
+ const ParmVarDecl *Param0 = MD->getNonObjectParameter(0);
const RValueReferenceType *RT =
Param0->getType()->getAs<RValueReferenceType>();
if (!RT || RT->getPointeeType().getCVRQualifiers()) {
@@ -11102,15 +11119,25 @@ void Sema::CheckConversionDeclarator(Declarator &D, QualType &R,
<< SourceRange(D.getIdentifierLoc()) << 0;
D.setInvalidType();
}
-
const auto *Proto = R->castAs<FunctionProtoType>();
-
// Make sure we don't have any parameters.
- if (Proto->getNumParams() > 0) {
- Diag(D.getIdentifierLoc(), diag::err_conv_function_with_params);
+ DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo();
+ unsigned NumParam = Proto->getNumParams();
+ // [C++2b]
+ // A conversion function shall have no non-object parameters.
+ if (NumParam == 1) {
+ DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo();
+ if (const auto *First =
+ dyn_cast_if_present<ParmVarDecl>(FTI.Params[0].Param);
+ First && First->isExplicitObjectParameter())
+ NumParam--;
+ }
+
+ if (NumParam != 0) {
+ Diag(D.getIdentifierLoc(), diag::err_conv_function_with_params);
// Delete the parameters.
- D.getFunctionTypeInfo().freeParams();
+ FTI.freeParams();
D.setInvalidType();
} else if (Proto->isVariadic()) {
Diag(D.getIdentifierLoc(), diag::err_conv_function_variadic);
@@ -11268,6 +11295,87 @@ Decl *Sema::ActOnConversionDeclarator(CXXConversionDecl *Conversion) {
return Conversion;
}
+void Sema::CheckExplicitObjectMemberFunction(DeclContext *DC, Declarator &D,
+ DeclarationName Name, QualType R) {
+ CheckExplicitObjectMemberFunction(D, Name, R, false, DC);
+}
+
+void Sema::CheckExplicitObjectLambda(Declarator &D) {
+ CheckExplicitObjectMemberFunction(D, {}, {}, true);
+}
+
+void Sema::CheckExplicitObjectMemberFunction(Declarator &D,
+ DeclarationName Name, QualType R,
+ bool IsLambda, DeclContext *DC) {
+ if (!D.isFunctionDeclarator())
+ return;
+
+ DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo();
+ if (FTI.NumParams == 0)
+ return;
+ ParmVarDecl *ExplicitObjectParam = nullptr;
+ for (unsigned Idx = 0; Idx < FTI.NumParams; Idx++) {
+ const auto &ParamInfo = FTI.Params[Idx];
+ if (!ParamInfo.Param)
+ continue;
+ ParmVarDecl *Param = cast<ParmVarDecl>(ParamInfo.Param);
+ if (!Param->isExplicitObjectParameter())
+ continue;
+ if (Idx == 0) {
+ ExplicitObjectParam = Param;
+ continue;
+ } else {
+ Diag(Param->getLocation(),
+ diag::err_explicit_object_parameter_must_be_first)
+ << IsLambda << Param->getSourceRange();
+ }
+ }
+ if (!ExplicitObjectParam)
+ return;
+
+ if (ExplicitObjectParam->hasDefaultArg()) {
+ Diag(ExplicitObjectParam->getLocation(),
+ diag::err_explicit_object_default_arg)
+ << ExplicitObjectParam->getSourceRange();
+ }
+
+ if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static) {
+ Diag(ExplicitObjectParam->getBeginLoc(),
+ diag::err_explicit_object_parameter_nonmember)
+ << D.getSourceRange() << /*static=*/0 << IsLambda;
+ }
+
+ if (D.getDeclSpec().isVirtualSpecified()) {
+ Diag(ExplicitObjectParam->getBeginLoc(),
+ diag::err_explicit_object_parameter_nonmember)
+ << D.getSourceRange() << /*virtual=*/1 << IsLambda;
+ }
+
+ if (IsLambda && FTI.hasMutableQualifier()) {
+ Diag(ExplicitObjectParam->getBeginLoc(),
+ diag::err_explicit_object_parameter_mutable)
+ << D.getSourceRange();
+ }
+
+ if (IsLambda)
+ return;
+
+ if (!DC || !DC->isRecord()) {
+ Diag(ExplicitObjectParam->getLocation(),
+ diag::err_explicit_object_parameter_nonmember)
+ << D.getSourceRange() << /*non-member=*/2 << IsLambda;
+ return;
+ }
+
+ // CWG2674: constructors and destructors cannot have explicit parameters.
+ if (Name.getNameKind() == DeclarationName::CXXConstructorName ||
+ Name.getNameKind() == DeclarationName::CXXDestructorName)
+ Diag(ExplicitObjectParam->getBeginLoc(),
+ diag::err_explicit_object_parameter_constructor)
+ << (Name.getNameKind() == DeclarationName::CXXDestructorName)
+ << D.getSourceRange();
+}
+
namespace {
/// Utility class to accumulate and print a diagnostic listing the invalid
/// specifier(s) on a declaration.
@@ -14862,12 +14970,11 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
SmallVector<Stmt*, 8> Statements;
// The parameter for the "other" object, which we are copying from.
- ParmVarDecl *Other = CopyAssignOperator->getParamDecl(0);
+ ParmVarDecl *Other = CopyAssignOperator->getNonObjectParameter(0);
Qualifiers OtherQuals = Other->getType().getQualifiers();
QualType OtherRefType = Other->getType();
- if (const LValueReferenceType *OtherRef
- = OtherRefType->getAs<LValueReferenceType>()) {
- OtherRefType = OtherRef->getPointeeType();
+ if (OtherRefType->isLValueReferenceType()) {
+ OtherRefType = OtherRefType->getPointeeType();
OtherQuals = OtherRefType.getQualifiers();
}
@@ -14879,8 +14986,26 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
// Builds a DeclRefExpr for the "other" object.
RefBuilder OtherRef(Other, OtherRefType);
- // Builds the "this" pointer.
- ThisBuilder This;
+ // Builds the function object parameter.
+ std::optional<ThisBuilder> This;
+ std::optional<DerefBuilder> DerefThis;
+ std::optional<RefBuilder> ExplicitObject;
+ bool IsArrow = false;
+ QualType ObjectType;
+ if (CopyAssignOperator->isExplicitObjectMemberFunction()) {
+ ObjectType = CopyAssignOperator->getParamDecl(0)->getType();
+ if (ObjectType->isReferenceType())
+ ObjectType = ObjectType->getPointeeType();
+ ExplicitObject.emplace(CopyAssignOperator->getParamDecl(0), ObjectType);
+ } else {
+ ObjectType = getCurrentThisType();
+ This.emplace();
+ DerefThis.emplace(*This);
+ IsArrow = !LangOpts.HLSL;
+ }
+ ExprBuilder &ObjectParameter =
+ ExplicitObject ? static_cast<ExprBuilder &>(*ExplicitObject)
+ : static_cast<ExprBuilder &>(*This);
// Assign base classes.
bool Invalid = false;
@@ -14902,11 +15027,11 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
VK_LValue, BasePath);
// Dereference "this".
- DerefBuilder DerefThis(This);
- CastBuilder To(DerefThis,
- Context.getQualifiedType(
- BaseType, CopyAssignOperator->getMethodQualifiers()),
- VK_LValue, BasePath);
+ CastBuilder To(
+ ExplicitObject ? static_cast<ExprBuilder &>(*ExplicitObject)
+ : static_cast<ExprBuilder &>(*DerefThis),
+ Context.getQualifiedType(BaseType, ObjectType.getQualifiers()),
+ VK_LValue, BasePath);
// Build the copy.
StmtResult Copy = buildSingleCopyAssign(*this, Loc, BaseType,
@@ -14972,10 +15097,7 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
MemberLookup.resolveKind();
MemberBuilder From(OtherRef, OtherRefType, /*IsArrow=*/false, MemberLookup);
-
- MemberBuilder To(This, getCurrentThisType(), /*IsArrow=*/!LangOpts.HLSL,
- MemberLookup);
-
+ MemberBuilder To(ObjectParameter, ObjectType, IsArrow, MemberLookup);
// Build the copy of this field.
StmtResult Copy = buildSingleCopyAssign(*this, Loc, FieldType,
To, From,
@@ -14992,15 +15114,11 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
if (!Invalid) {
// Add a "return *this;"
- Expr *ThisExpr = nullptr;
- if (!LangOpts.HLSL) {
- ExprResult ThisObj =
- CreateBuiltinUnaryOp(Loc, UO_Deref, This.build(*this, Loc));
- ThisExpr = ThisObj.get();
- } else {
- ThisExpr = This.build(*this, Loc);
- }
-
+ Expr *ThisExpr =
+ (ExplicitObject ? static_cast<ExprBuilder &>(*ExplicitObject)
+ : LangOpts.HLSL ? static_cast<ExprBuilder &>(*This)
+ : static_cast<ExprBuilder &>(*DerefThis))
+ .build(*this, Loc);
StmtResult Return = BuildReturnStmt(Loc, ThisExpr);
if (Return.isInvalid())
Invalid = true;
@@ -15231,7 +15349,7 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation,
SmallVector<Stmt*, 8> Statements;
// The parameter for the "other" object, which we are move from.
- ParmVarDecl *Other = MoveAssignOperator->getParamDecl(0);
+ ParmVarDecl *Other = MoveAssignOperator->getNonObjectParameter(0);
QualType OtherRefType =
Other->getType()->castAs<RValueReferenceType>()->getPointeeType();
@@ -15245,8 +15363,23 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation,
// Cast to rvalue.
MoveCastBuilder MoveOther(OtherRef);
- // Builds the "this" pointer.
- ThisBuilder This;
+ // Builds the function object parameter.
+ std::optional<ThisBuilder> This;
+ std::optional<DerefBuilder> DerefThis;
+ std::optional<RefBuilder> ExplicitObject;
+ QualType ObjectType;
+ if (MoveAssignOperator->isExplicitObjectMemberFunction()) {
+ ObjectType = MoveAssignOperator->getParamDecl(0)->getType();
+ if (ObjectType->isReferenceType())
+ ObjectType = ObjectType->getPointeeType();
+ ExplicitObject.emplace(MoveAssignOperator->getParamDecl(0), ObjectType);
+ } else {
+ ObjectType = getCurrentThisType();
+ This.emplace();
+ DerefThis.emplace(*This);
+ }
+ ExprBuilder &ObjectParameter =
+ ExplicitObject ? *ExplicitObject : static_cast<ExprBuilder &>(*This);
// Assign base classes.
bool Invalid = false;
@@ -15274,14 +15407,13 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation,
// appropriately-qualified base type.
CastBuilder From(OtherRef, BaseType, VK_XValue, BasePath);
- // Dereference "this".
- DerefBuilder DerefThis(This);
-
// Implicitly cast "this" to the appropriately-qualified base type.
- CastBuilder To(DerefThis,
- Context.getQualifiedType(
- BaseType, MoveAssignOperator->getMethodQualifiers()),
- VK_LValue, BasePath);
+ // Dereference "this".
+ CastBuilder To(
+ ExplicitObject ? static_cast<ExprBuilder &>(*ExplicitObject)
+ : static_cast<ExprBuilder &>(*DerefThis),
+ Context.getQualifiedType(BaseType, ObjectType.getQualifiers()),
+ VK_LValue, BasePath);
// Build the move.
StmtResult Move = buildSingleCopyAssign(*this, Loc, BaseType,
@@ -15346,8 +15478,8 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation,
MemberLookup.resolveKind();
MemberBuilder From(MoveOther, OtherRefType,
/*IsArrow=*/false, MemberLookup);
- MemberBuilder To(This, getCurrentThisType(),
- /*IsArrow=*/true, MemberLookup);
+ MemberBuilder To(ObjectParameter, ObjectType, /*IsArrow=*/!ExplicitObject,
+ MemberLookup);
assert(!From.build(*this, Loc)->isLValue() && // could be xvalue or prvalue
"Member reference with rvalue base must be rvalue except for reference "
@@ -15369,10 +15501,12 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation,
if (!Invalid) {
// Add a "return *this;"
- ExprResult ThisObj =
- CreateBuiltinUnaryOp(Loc, UO_Deref, This.build(*this, Loc));
+ Expr *ThisExpr =
+ (ExplicitObject ? static_cast<ExprBuilder &>(*ExplicitObject)
+ : static_cast<ExprBuilder &>(*DerefThis))
+ .build(*this, Loc);
- StmtResult Return = BuildReturnStmt(Loc, ThisObj.get());
+ StmtResult Return = BuildReturnStmt(Loc, ThisExpr);
if (Return.isInvalid())
Invalid = true;
else
@@ -15691,7 +15825,9 @@ void Sema::DefineImplicitLambdaToFunctionPointerConversion(
CXXRecordDecl *Lambda = Conv->getParent();
FunctionDecl *CallOp = Lambda->getLambdaCallOperator();
FunctionDecl *Invoker =
- CallOp->isStatic() ? CallOp : Lambda->getLambdaStaticInvoker(CC);
+ CallOp->hasCXXExplicitFunctionObjectParameter() || CallOp->isStatic()
+ ? CallOp
+ : Lambda->getLambdaStaticInvoker(CC);
if (auto *TemplateArgs = Conv->getTemplateSpecializationArgs()) {
CallOp = InstantiateFunctionDeclaration(
@@ -16297,8 +16433,11 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) {
// [...] Operator functions cannot have more or fewer parameters
// than the number required for the corresponding operator, as
// described in the rest of this subclause.
- unsigned NumParams = FnDecl->getNumParams()
- + (isa<CXXMethodDecl>(FnDecl)? 1 : 0);
+ unsigned NumParams = FnDecl->getNumParams() +
+ (isa<CXXMethodDecl>(FnDecl) &&
+ !FnDecl->hasCXXExplicitFunctionObjectParameter()
+ ? 1
+ : 0);
if (Op != OO_Call && Op != OO_Subscript &&
((NumParams == 1 && !CanBeUnaryOperator) ||
(NumParams == 2 && !CanBeBinaryOperator) || (NumParams < 1) ||
@@ -18106,6 +18245,20 @@ bool Sema::CheckOverridingFunctionAttributes(const CXXMethodDecl *New,
return true;
}
+bool Sema::CheckExplicitObjectOverride(CXXMethodDecl *New,
+ const CXXMethodDecl *Old) {
+ // CWG2553
+ // A virtual function shall not be an explicit object member function.
+ if (!New->isExplicitObjectMemberFunction())
+ return true;
+ Diag(New->getParamDecl(0)->getBeginLoc(),
+ diag::err_explicit_object_parameter_nonmember)
+ << New->getSourceRange() << /*virtual*/ 1 << /*IsLambda*/ false;
+ Diag(Old->getLocation(), diag::note_overridden_virtual_function);
+ New->setInvalidDecl();
+ return false;
+}
+
bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New,
const CXXMethodDecl *Old) {
QualType NewTy = New->getType()->castAs<FunctionType>()->getReturnType();
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index 72304e5..75730ea 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -769,14 +769,12 @@ bool Sema::handlerCanCatch(QualType HandlerType, QualType ExceptionType) {
/// CheckExceptionSpecSubset - Check whether the second function type's
/// exception specification is a subset (or equivalent) of the first function
/// type. This is used by override and pointer assignment checks.
-bool Sema::CheckExceptionSpecSubset(const PartialDiagnostic &DiagID,
- const PartialDiagnostic &NestedDiagID,
- const PartialDiagnostic &NoteID,
- const PartialDiagnostic &NoThrowDiagID,
- const FunctionProtoType *Superset,
- SourceLocation SuperLoc,
- const FunctionProtoType *Subset,
- SourceLocation SubLoc) {
+bool Sema::CheckExceptionSpecSubset(
+ const PartialDiagnostic &DiagID, const PartialDiagnostic &NestedDiagID,
+ const PartialDiagnostic &NoteID, const PartialDiagnostic &NoThrowDiagID,
+ const FunctionProtoType *Superset, bool SkipSupersetFirstParameter,
+ SourceLocation SuperLoc, const FunctionProtoType *Subset,
+ bool SkipSubsetFirstParameter, SourceLocation SubLoc) {
// Just auto-succeed under -fno-exceptions.
if (!getLangOpts().CXXExceptions)
@@ -816,8 +814,9 @@ bool Sema::CheckExceptionSpecSubset(const PartialDiagnostic &DiagID,
// done.
if ((SuperCanThrow == CT_Can && SuperEST != EST_Dynamic) ||
SubCanThrow == CT_Cannot)
- return CheckParamExceptionSpec(NestedDiagID, NoteID, Superset, SuperLoc,
- Subset, SubLoc);
+ return CheckParamExceptionSpec(NestedDiagID, NoteID, Superset,
+ SkipSupersetFirstParameter, SuperLoc, Subset,
+ SkipSubsetFirstParameter, SubLoc);
// Allow __declspec(nothrow) to be missing on redeclaration as an extension in
// some cases.
@@ -869,8 +868,9 @@ bool Sema::CheckExceptionSpecSubset(const PartialDiagnostic &DiagID,
}
}
// We've run half the gauntlet.
- return CheckParamExceptionSpec(NestedDiagID, NoteID, Superset, SuperLoc,
- Subset, SubLoc);
+ return CheckParamExceptionSpec(NestedDiagID, NoteID, Superset,
+ SkipSupersetFirstParameter, SuperLoc, Subset,
+ SkipSupersetFirstParameter, SubLoc);
}
static bool
@@ -894,12 +894,11 @@ CheckSpecForTypesEquivalent(Sema &S, const PartialDiagnostic &DiagID,
/// assignment and override compatibility check. We do not check the parameters
/// of parameter function pointers recursively, as no sane programmer would
/// even be able to write such a function type.
-bool Sema::CheckParamExceptionSpec(const PartialDiagnostic &DiagID,
- const PartialDiagnostic &NoteID,
- const FunctionProtoType *Target,
- SourceLocation TargetLoc,
- const FunctionProtoType *Source,
- SourceLocation SourceLoc) {
+bool Sema::CheckParamExceptionSpec(
+ const PartialDiagnostic &DiagID, const PartialDiagnostic &NoteID,
+ const FunctionProtoType *Target, bool SkipTargetFirstParameter,
+ SourceLocation TargetLoc, const FunctionProtoType *Source,
+ bool SkipSourceFirstParameter, SourceLocation SourceLoc) {
auto RetDiag = DiagID;
RetDiag << 0;
if (CheckSpecForTypesEquivalent(
@@ -910,14 +909,16 @@ bool Sema::CheckParamExceptionSpec(const PartialDiagnostic &DiagID,
// We shouldn't even be testing this unless the arguments are otherwise
// compatible.
- assert(Target->getNumParams() == Source->getNumParams() &&
+ assert((Target->getNumParams() - (unsigned)SkipTargetFirstParameter) ==
+ (Source->getNumParams() - (unsigned)SkipSourceFirstParameter) &&
"Functions have different argument counts.");
for (unsigned i = 0, E = Target->getNumParams(); i != E; ++i) {
auto ParamDiag = DiagID;
ParamDiag << 1;
if (CheckSpecForTypesEquivalent(
*this, ParamDiag, PDiag(),
- Target->getParamType(i), TargetLoc, Source->getParamType(i),
+ Target->getParamType(i + (SkipTargetFirstParameter ? 1 : 0)),
+ TargetLoc, Source->getParamType(SkipSourceFirstParameter ? 1 : 0),
SourceLoc))
return true;
}
@@ -958,9 +959,10 @@ bool Sema::CheckExceptionSpecCompatibility(Expr *From, QualType ToType) {
// void (*q)(void (*) throw(int)) = p;
// }
// ... because it might be instantiated with T=int.
- return CheckExceptionSpecSubset(
- PDiag(DiagID), PDiag(NestedDiagID), PDiag(), PDiag(), ToFunc,
- From->getSourceRange().getBegin(), FromFunc, SourceLocation()) &&
+ return CheckExceptionSpecSubset(PDiag(DiagID), PDiag(NestedDiagID), PDiag(),
+ PDiag(), ToFunc, 0,
+ From->getSourceRange().getBegin(), FromFunc,
+ 0, SourceLocation()) &&
!getLangOpts().CPlusPlus17;
}
@@ -989,14 +991,14 @@ bool Sema::CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New,
unsigned DiagID = diag::err_override_exception_spec;
if (getLangOpts().MSVCCompat)
DiagID = diag::ext_override_exception_spec;
- return CheckExceptionSpecSubset(PDiag(DiagID),
- PDiag(diag::err_deep_exception_specs_differ),
- PDiag(diag::note_overridden_virtual_function),
- PDiag(diag::ext_override_exception_spec),
- Old->getType()->castAs<FunctionProtoType>(),
- Old->getLocation(),
- New->getType()->castAs<FunctionProtoType>(),
- New->getLocation());
+ return CheckExceptionSpecSubset(
+ PDiag(DiagID), PDiag(diag::err_deep_exception_specs_differ),
+ PDiag(diag::note_overridden_virtual_function),
+ PDiag(diag::ext_override_exception_spec),
+ Old->getType()->castAs<FunctionProtoType>(),
+ Old->hasCXXExplicitFunctionObjectParameter(), Old->getLocation(),
+ New->getType()->castAs<FunctionProtoType>(),
+ New->hasCXXExplicitFunctionObjectParameter(), New->getLocation());
}
static CanThrowResult canSubStmtsThrow(Sema &Self, const Stmt *S) {
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 92496b03..2ed31a9 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2444,7 +2444,7 @@ bool Sema::DiagnoseDependentMemberLookup(const LookupResult &R) {
// FIXME: Is this special case necessary? We could allow the caller to
// diagnose this.
if (isDefaultArgument && ((*R.begin())->isCXXInstanceMember())) {
- Diag(R.getNameLoc(), diag::err_member_call_without_object);
+ Diag(R.getNameLoc(), diag::err_member_call_without_object) << 0;
return true;
}
@@ -3217,11 +3217,11 @@ Sema::PerformObjectMemberConversion(Expr *From,
FromRecordType = FromType;
}
} else if (const auto *Method = dyn_cast<CXXMethodDecl>(Member)) {
- if (Method->isStatic())
+ if (!Method->isImplicitObjectMemberFunction())
return From;
DestType = Method->getThisType().getNonReferenceType();
- DestRecordType = Method->getThisObjectType();
+ DestRecordType = Method->getFunctionObjectParameterType();
if (FromType->getAs<PointerType>()) {
FromRecordType = FromType->getPointeeType();
@@ -6503,6 +6503,9 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
// C99 6.5.2.2p7 - the arguments are implicitly converted, as if by
// assignment, to the types of the corresponding parameter, ...
+ bool HasExplicitObjectParameter =
+ FDecl && FDecl->hasCXXExplicitFunctionObjectParameter();
+ unsigned ExplicitObjectParameterOffset = HasExplicitObjectParameter ? 1 : 0;
unsigned NumParams = Proto->getNumParams();
bool Invalid = false;
unsigned MinArgs = FDecl ? FDecl->getMinRequiredArguments() : NumParams;
@@ -6521,21 +6524,29 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
MinArgs == NumParams && !Proto->isVariadic()
? diag::err_typecheck_call_too_few_args_suggest
: diag::err_typecheck_call_too_few_args_at_least_suggest;
- diagnoseTypo(TC, PDiag(diag_id) << FnKind << MinArgs
- << static_cast<unsigned>(Args.size())
- << TC.getCorrectionRange());
- } else if (MinArgs == 1 && FDecl && FDecl->getParamDecl(0)->getDeclName())
+ diagnoseTypo(
+ TC, PDiag(diag_id)
+ << FnKind << MinArgs - ExplicitObjectParameterOffset
+ << static_cast<unsigned>(Args.size()) -
+ ExplicitObjectParameterOffset
+ << HasExplicitObjectParameter << TC.getCorrectionRange());
+ } else if (MinArgs - ExplicitObjectParameterOffset == 1 && FDecl &&
+ FDecl->getParamDecl(ExplicitObjectParameterOffset)
+ ->getDeclName())
Diag(RParenLoc,
MinArgs == NumParams && !Proto->isVariadic()
? diag::err_typecheck_call_too_few_args_one
: diag::err_typecheck_call_too_few_args_at_least_one)
- << FnKind << FDecl->getParamDecl(0) << Fn->getSourceRange();
+ << FnKind << FDecl->getParamDecl(ExplicitObjectParameterOffset)
+ << HasExplicitObjectParameter << Fn->getSourceRange();
else
Diag(RParenLoc, MinArgs == NumParams && !Proto->isVariadic()
? diag::err_typecheck_call_too_few_args
: diag::err_typecheck_call_too_few_args_at_least)
- << FnKind << MinArgs << static_cast<unsigned>(Args.size())
- << Fn->getSourceRange();
+ << FnKind << MinArgs - ExplicitObjectParameterOffset
+ << static_cast<unsigned>(Args.size()) -
+ ExplicitObjectParameterOffset
+ << HasExplicitObjectParameter << Fn->getSourceRange();
// Emit the location of the prototype.
if (!TC && FDecl && !FDecl->getBuiltinID() && !IsExecConfig)
@@ -6560,17 +6571,23 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
MinArgs == NumParams && !Proto->isVariadic()
? diag::err_typecheck_call_too_many_args_suggest
: diag::err_typecheck_call_too_many_args_at_most_suggest;
- diagnoseTypo(TC, PDiag(diag_id) << FnKind << NumParams
- << static_cast<unsigned>(Args.size())
- << TC.getCorrectionRange());
- } else if (NumParams == 1 && FDecl &&
- FDecl->getParamDecl(0)->getDeclName())
+ diagnoseTypo(
+ TC, PDiag(diag_id)
+ << FnKind << NumParams - ExplicitObjectParameterOffset
+ << static_cast<unsigned>(Args.size()) -
+ ExplicitObjectParameterOffset
+ << HasExplicitObjectParameter << TC.getCorrectionRange());
+ } else if (NumParams - ExplicitObjectParameterOffset == 1 && FDecl &&
+ FDecl->getParamDecl(ExplicitObjectParameterOffset)
+ ->getDeclName())
Diag(Args[NumParams]->getBeginLoc(),
MinArgs == NumParams
? diag::err_typecheck_call_too_many_args_one
: diag::err_typecheck_call_too_many_args_at_most_one)
- << FnKind << FDecl->getParamDecl(0)
- << static_cast<unsigned>(Args.size()) << Fn->getSourceRange()
+ << FnKind << FDecl->getParamDecl(ExplicitObjectParameterOffset)
+ << static_cast<unsigned>(Args.size()) -
+ ExplicitObjectParameterOffset
+ << HasExplicitObjectParameter << Fn->getSourceRange()
<< SourceRange(Args[NumParams]->getBeginLoc(),
Args.back()->getEndLoc());
else
@@ -6578,8 +6595,10 @@ Sema::ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
MinArgs == NumParams
? diag::err_typecheck_call_too_many_args
: diag::err_typecheck_call_too_many_args_at_most)
- << FnKind << NumParams << static_cast<unsigned>(Args.size())
- << Fn->getSourceRange()
+ << FnKind << NumParams - ExplicitObjectParameterOffset
+ << static_cast<unsigned>(Args.size()) -
+ ExplicitObjectParameterOffset
+ << HasExplicitObjectParameter << Fn->getSourceRange()
<< SourceRange(Args[NumParams]->getBeginLoc(),
Args.back()->getEndLoc());
@@ -7661,9 +7680,9 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
}
if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(FDecl))
- if (!Method->isStatic())
+ if (Method->isImplicitObjectMemberFunction())
return ExprError(Diag(LParenLoc, diag::err_member_call_without_object)
- << Fn->getSourceRange());
+ << Fn->getSourceRange() << 0);
// Check for sentinels
if (NDecl)
@@ -14955,6 +14974,34 @@ static void diagnoseAddressOfInvalidType(Sema &S, SourceLocation Loc,
S.Diag(Loc, diag::err_typecheck_address_of) << Type << E->getSourceRange();
}
+bool Sema::CheckUseOfCXXMethodAsAddressOfOperand(SourceLocation OpLoc,
+ const Expr *Op,
+ const CXXMethodDecl *MD) {
+ const auto *DRE = cast<DeclRefExpr>(Op->IgnoreParens());
+
+ if (Op != DRE)
+ return Diag(OpLoc, diag::err_parens_pointer_member_function)
+ << Op->getSourceRange();
+
+ // Taking the address of a dtor is illegal per C++ [class.dtor]p2.
+ if (isa<CXXDestructorDecl>(MD))
+ return Diag(OpLoc, diag::err_typecheck_addrof_dtor)
+ << DRE->getSourceRange();
+
+ if (DRE->getQualifier())
+ return false;
+
+ if (MD->getParent()->getName().empty())
+ return Diag(OpLoc, diag::err_unqualified_pointer_member_function)
+ << DRE->getSourceRange();
+
+ SmallString<32> Str;
+ StringRef Qual = (MD->getParent()->getName() + "::").toStringRef(Str);
+ return Diag(OpLoc, diag::err_unqualified_pointer_member_function)
+ << DRE->getSourceRange()
+ << FixItHint::CreateInsertion(DRE->getSourceRange().getBegin(), Qual);
+}
+
/// CheckAddressOfOperand - The operand of & must be either a function
/// designator or an lvalue designating an object. If it is an lvalue, the
/// object cannot be declared with storage class register or be a bit field.
@@ -15064,28 +15111,7 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
DeclRefExpr *DRE = cast<DeclRefExpr>(op);
CXXMethodDecl *MD = cast<CXXMethodDecl>(DRE->getDecl());
- // The id-expression was parenthesized.
- if (OrigOp.get() != DRE) {
- Diag(OpLoc, diag::err_parens_pointer_member_function)
- << OrigOp.get()->getSourceRange();
-
- // The method was named without a qualifier.
- } else if (!DRE->getQualifier()) {
- if (MD->getParent()->getName().empty())
- Diag(OpLoc, diag::err_unqualified_pointer_member_function)
- << op->getSourceRange();
- else {
- SmallString<32> Str;
- StringRef Qual = (MD->getParent()->getName() + "::").toStringRef(Str);
- Diag(OpLoc, diag::err_unqualified_pointer_member_function)
- << op->getSourceRange()
- << FixItHint::CreateInsertion(op->getSourceRange().getBegin(), Qual);
- }
- }
-
- // Taking the address of a dtor is illegal per C++ [class.dtor]p2.
- if (isa<CXXDestructorDecl>(MD))
- Diag(OpLoc, diag::err_typecheck_addrof_dtor) << op->getSourceRange();
+ CheckUseOfCXXMethodAsAddressOfOperand(OpLoc, OrigOp.get(), MD);
QualType MPTy = Context.getMemberPointerType(
op->getType(), Context.getTypeDeclType(MD->getParent()).getTypePtr());
@@ -15105,7 +15131,11 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
<< op->getType() << op->getSourceRange();
return QualType();
}
+ } else if (const auto *DRE = dyn_cast<DeclRefExpr>(op)) {
+ if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(DRE->getDecl()))
+ CheckUseOfCXXMethodAsAddressOfOperand(OpLoc, OrigOp.get(), MD);
}
+
} else if (op->getObjectKind() == OK_BitField) { // C99 6.5.3.2p1
// The operand cannot be a bit-field
AddressOfError = AO_Bit_Field;
@@ -16496,7 +16526,7 @@ bool Sema::isQualifiedMemberAccess(Expr *E) {
if (isa<FieldDecl>(VD) || isa<IndirectFieldDecl>(VD))
return true;
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(VD))
- return Method->isInstance();
+ return Method->isImplicitObjectMemberFunction();
return false;
}
@@ -16507,7 +16537,7 @@ bool Sema::isQualifiedMemberAccess(Expr *E) {
for (NamedDecl *D : ULE->decls()) {
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
- if (Method->isInstance())
+ if (Method->isImplicitObjectMemberFunction())
return true;
} else {
// Overload set does not contain methods.
@@ -19227,7 +19257,8 @@ static bool isVariableAlreadyCapturedInScopeInfo(CapturingScopeInfo *CSI,
// private instances of the captured declarations.
const Capture &Cap = CSI->getCapture(Var);
if (Cap.isCopyCapture() &&
- !(isa<LambdaScopeInfo>(CSI) && cast<LambdaScopeInfo>(CSI)->Mutable) &&
+ !(isa<LambdaScopeInfo>(CSI) &&
+ !cast<LambdaScopeInfo>(CSI)->lambdaCaptureShouldBeConst()) &&
!(isa<CapturedRegionScopeInfo>(CSI) &&
cast<CapturedRegionScopeInfo>(CSI)->CapRegionKind == CR_OpenMP))
DeclRefType.addConst();
@@ -19544,7 +19575,8 @@ static bool captureInLambda(LambdaScopeInfo *LSI, ValueDecl *Var,
// declared const (9.3.1) if and only if the lambda-expression's
// parameter-declaration-clause is not followed by mutable.
DeclRefType = CaptureType.getNonReferenceType();
- if (!LSI->Mutable && !CaptureType->isReferenceType())
+ bool Const = LSI->lambdaCaptureShouldBeConst();
+ if (Const && !CaptureType->isReferenceType())
DeclRefType.addConst();
}
@@ -20612,6 +20644,34 @@ void Sema::MarkVariableReferenced(SourceLocation Loc, VarDecl *Var) {
DoMarkVarDeclReferenced(*this, Loc, Var, nullptr, RefsMinusAssignments);
}
+// C++ [temp.dep.expr]p3:
+// An id-expression is type-dependent if it contains:
+// - an identifier associated by name lookup with an entity captured by copy
+// in a lambda-expression that has an explicit object parameter whose type
+// is dependent ([dcl.fct]),
+static void FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(
+ Sema &SemaRef, ValueDecl *D, Expr *E) {
+ auto *ID = dyn_cast<DeclRefExpr>(E);
+ if (!ID || ID->isTypeDependent())
+ return;
+
+ auto IsDependent = [&]() {
+ const LambdaScopeInfo *LSI = SemaRef.getCurLambda();
+ if (!LSI)
+ return false;
+ if (!LSI->ExplicitObjectParameter ||
+ !LSI->ExplicitObjectParameter->getType()->isDependentType())
+ return false;
+ if (!LSI->CaptureMap.count(D))
+ return false;
+ const Capture &Cap = LSI->getCapture(D);
+ return !Cap.isCopyCapture();
+ }();
+
+ ID->setCapturedByCopyInLambdaWithExplicitObjectParameter(
+ IsDependent, SemaRef.getASTContext());
+}
+
static void
MarkExprReferenced(Sema &SemaRef, SourceLocation Loc, Decl *D, Expr *E,
bool MightBeOdrUse,
@@ -20621,14 +20681,19 @@ MarkExprReferenced(Sema &SemaRef, SourceLocation Loc, Decl *D, Expr *E,
if (VarDecl *Var = dyn_cast<VarDecl>(D)) {
DoMarkVarDeclReferenced(SemaRef, Loc, Var, E, RefsMinusAssignments);
+ if (SemaRef.getLangOpts().CPlusPlus)
+ FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(SemaRef,
+ Var, E);
return;
}
if (BindingDecl *Decl = dyn_cast<BindingDecl>(D)) {
DoMarkBindingDeclReferenced(SemaRef, Loc, Decl, E);
+ if (SemaRef.getLangOpts().CPlusPlus)
+ FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(SemaRef,
+ Decl, E);
return;
}
-
SemaRef.MarkAnyDeclReferenced(Loc, D, MightBeOdrUse);
// If this is a call to a method via a cast, also mark the method in the
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index c147cc5..1153049 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1160,7 +1160,7 @@ static QualType adjustCVQualifiersForCXXThisWithinLambda(
auto C = CurLSI->getCXXThisCapture();
if (C.isCopyCapture()) {
- if (!CurLSI->Mutable)
+ if (CurLSI->lambdaCaptureShouldBeConst())
ClassType.addConst();
return ASTCtx.getPointerType(ClassType);
}
@@ -1216,11 +1216,11 @@ QualType Sema::getCurrentThisType() {
QualType ThisTy = CXXThisTypeOverride;
if (CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(DC)) {
- if (method && method->isInstance())
+ if (method && method->isImplicitObjectMemberFunction())
ThisTy = method->getThisType().getNonReferenceType();
}
- if (ThisTy.isNull() && isLambdaCallOperator(CurContext) &&
+ if (ThisTy.isNull() && isLambdaCallWithImplicitObjectParameter(CurContext) &&
inTemplateInstantiation() && isa<CXXRecordDecl>(DC)) {
// This is a lambda call operator that is being instantiated as a default
@@ -1398,10 +1398,22 @@ ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
/// C++ 9.3.2: In the body of a non-static member function, the keyword this
/// is a non-lvalue expression whose value is the address of the object for
/// which the function is called.
-
QualType ThisTy = getCurrentThisType();
- if (ThisTy.isNull())
- return Diag(Loc, diag::err_invalid_this_use);
+
+ if (ThisTy.isNull()) {
+ DeclContext *DC = getFunctionLevelDeclContext();
+
+ if (const auto *Method = dyn_cast<CXXMethodDecl>(DC);
+ Method && Method->isExplicitObjectMemberFunction()) {
+ return Diag(Loc, diag::err_invalid_this_use) << 1;
+ }
+
+ if (isLambdaCallWithExplicitObjectParameter(CurContext))
+ return Diag(Loc, diag::err_invalid_this_use) << 1;
+
+ return Diag(Loc, diag::err_invalid_this_use) << 0;
+ }
+
return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false);
}
@@ -3960,7 +3972,7 @@ void Sema::CheckVirtualDtorCall(CXXDestructorDecl *dtor, SourceLocation Loc,
if (getSourceManager().isInSystemHeader(PointeeRD->getLocation()))
return;
- QualType ClassType = dtor->getThisObjectType();
+ QualType ClassType = dtor->getFunctionObjectParameterType();
if (PointeeRD->isAbstract()) {
// If the class is abstract, we warn by default, because we're
// sure the code has undefined behavior.
@@ -4319,15 +4331,17 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
if (DiagnoseUseOfDecl(Fn, From->getBeginLoc()))
return ExprError();
- From = FixOverloadedFunctionReference(From, Found, Fn);
+ ExprResult Res = FixOverloadedFunctionReference(From, Found, Fn);
+ if (Res.isInvalid())
+ return ExprError();
// We might get back another placeholder expression if we resolved to a
// builtin.
- ExprResult Checked = CheckPlaceholderExpr(From);
- if (Checked.isInvalid())
+ Res = CheckPlaceholderExpr(Res.get());
+ if (Res.isInvalid())
return ExprError();
- From = Checked.get();
+ From = Res.get();
FromType = From->getType();
}
@@ -8052,68 +8066,6 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base,
Destructed);
}
-ExprResult Sema::BuildCXXMemberCallExpr(Expr *E, NamedDecl *FoundDecl,
- CXXConversionDecl *Method,
- bool HadMultipleCandidates) {
- // Convert the expression to match the conversion function's implicit object
- // parameter.
- ExprResult Exp = PerformObjectArgumentInitialization(E, /*Qualifier=*/nullptr,
- FoundDecl, Method);
- if (Exp.isInvalid())
- return true;
-
- if (Method->getParent()->isLambda() &&
- Method->getConversionType()->isBlockPointerType()) {
- // This is a lambda conversion to block pointer; check if the argument
- // was a LambdaExpr.
- Expr *SubE = E;
- CastExpr *CE = dyn_cast<CastExpr>(SubE);
- if (CE && CE->getCastKind() == CK_NoOp)
- SubE = CE->getSubExpr();
- SubE = SubE->IgnoreParens();
- if (CXXBindTemporaryExpr *BE = dyn_cast<CXXBindTemporaryExpr>(SubE))
- SubE = BE->getSubExpr();
- if (isa<LambdaExpr>(SubE)) {
- // For the conversion to block pointer on a lambda expression, we
- // construct a special BlockLiteral instead; this doesn't really make
- // a difference in ARC, but outside of ARC the resulting block literal
- // follows the normal lifetime rules for block literals instead of being
- // autoreleased.
- PushExpressionEvaluationContext(
- ExpressionEvaluationContext::PotentiallyEvaluated);
- ExprResult BlockExp = BuildBlockForLambdaConversion(
- Exp.get()->getExprLoc(), Exp.get()->getExprLoc(), Method, Exp.get());
- PopExpressionEvaluationContext();
-
- // FIXME: This note should be produced by a CodeSynthesisContext.
- if (BlockExp.isInvalid())
- Diag(Exp.get()->getExprLoc(), diag::note_lambda_to_block_conv);
- return BlockExp;
- }
- }
-
- MemberExpr *ME =
- BuildMemberExpr(Exp.get(), /*IsArrow=*/false, SourceLocation(),
- NestedNameSpecifierLoc(), SourceLocation(), Method,
- DeclAccessPair::make(FoundDecl, FoundDecl->getAccess()),
- HadMultipleCandidates, DeclarationNameInfo(),
- Context.BoundMemberTy, VK_PRValue, OK_Ordinary);
-
- QualType ResultType = Method->getReturnType();
- ExprValueKind VK = Expr::getValueKindForType(ResultType);
- ResultType = ResultType.getNonLValueExprType(Context);
-
- CXXMemberCallExpr *CE = CXXMemberCallExpr::Create(
- Context, ME, /*Args=*/{}, ResultType, VK, Exp.get()->getEndLoc(),
- CurFPFeatureOverrides());
-
- if (CheckFunctionCall(Method, CE,
- Method->getType()->castAs<FunctionProtoType>()))
- return ExprError();
-
- return CheckForImmediateInvocation(CE, CE->getMethodDecl());
-}
-
ExprResult Sema::BuildCXXNoexceptExpr(SourceLocation KeyLoc, Expr *Operand,
SourceLocation RParen) {
// If the operand is an unresolved lookup expression, the expression is ill-
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index fe92215..bd85b54 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -46,7 +46,7 @@ enum IMAKind {
/// The reference may be to an instance member, but it might be invalid if
/// so, because the context is not an instance method.
- IMA_Mixed_StaticContext,
+ IMA_Mixed_StaticOrExplicitContext,
/// The reference may be to an instance member, but it is invalid if
/// so, because the context is from an unrelated class.
@@ -63,7 +63,7 @@ enum IMAKind {
/// The reference may be to an unresolved using declaration and the
/// context is not an instance method.
- IMA_Unresolved_StaticContext,
+ IMA_Unresolved_StaticOrExplicitContext,
// The reference refers to a field which is not a member of the containing
// class, which is allowed because we're in C++11 mode and the context is
@@ -72,7 +72,7 @@ enum IMAKind {
/// All possible referrents are instance members and the current
/// context is not an instance method.
- IMA_Error_StaticContext,
+ IMA_Error_StaticOrExplicitContext,
/// All possible referrents are instance members of an unrelated
/// class.
@@ -91,11 +91,14 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
DeclContext *DC = SemaRef.getFunctionLevelDeclContext();
- bool isStaticContext = SemaRef.CXXThisTypeOverride.isNull() &&
- (!isa<CXXMethodDecl>(DC) || cast<CXXMethodDecl>(DC)->isStatic());
+ bool isStaticOrExplicitContext =
+ SemaRef.CXXThisTypeOverride.isNull() &&
+ (!isa<CXXMethodDecl>(DC) || cast<CXXMethodDecl>(DC)->isStatic() ||
+ cast<CXXMethodDecl>(DC)->isExplicitObjectMemberFunction());
if (R.isUnresolvableResult())
- return isStaticContext ? IMA_Unresolved_StaticContext : IMA_Unresolved;
+ return isStaticOrExplicitContext ? IMA_Unresolved_StaticOrExplicitContext
+ : IMA_Unresolved;
// Collect all the declaring classes of instance members we find.
bool hasNonInstance = false;
@@ -152,12 +155,12 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
// If the current context is not an instance method, it can't be
// an implicit member reference.
- if (isStaticContext) {
+ if (isStaticOrExplicitContext) {
if (hasNonInstance)
- return IMA_Mixed_StaticContext;
+ return IMA_Mixed_StaticOrExplicitContext;
return AbstractInstanceResult ? AbstractInstanceResult
- : IMA_Error_StaticContext;
+ : IMA_Error_StaticOrExplicitContext;
}
CXXRecordDecl *contextClass;
@@ -167,7 +170,7 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
contextClass = RD;
else
return AbstractInstanceResult ? AbstractInstanceResult
- : IMA_Error_StaticContext;
+ : IMA_Error_StaticOrExplicitContext;
// [class.mfct.non-static]p3:
// ...is used in the body of a non-static member function of class X,
@@ -214,14 +217,31 @@ static void diagnoseInstanceReference(Sema &SemaRef,
CXXRecordDecl *RepClass = dyn_cast<CXXRecordDecl>(Rep->getDeclContext());
bool InStaticMethod = Method && Method->isStatic();
+ bool InExplicitObjectMethod =
+ Method && Method->isExplicitObjectMemberFunction();
bool IsField = isa<FieldDecl>(Rep) || isa<IndirectFieldDecl>(Rep);
+ std::string Replacement;
+ if (InExplicitObjectMethod) {
+ DeclarationName N = Method->getParamDecl(0)->getDeclName();
+ if (!N.isEmpty()) {
+ Replacement.append(N.getAsString());
+ Replacement.append(".");
+ }
+ }
if (IsField && InStaticMethod)
// "invalid use of member 'x' in static member function"
- SemaRef.Diag(Loc, diag::err_invalid_member_use_in_static_method)
- << Range << nameInfo.getName();
- else if (ContextClass && RepClass && SS.isEmpty() && !InStaticMethod &&
- !RepClass->Equals(ContextClass) && RepClass->Encloses(ContextClass))
+ SemaRef.Diag(Loc, diag::err_invalid_member_use_in_method)
+ << Range << nameInfo.getName() << /*static*/ 0;
+ else if (IsField && InExplicitObjectMethod) {
+ auto Diag = SemaRef.Diag(Loc, diag::err_invalid_member_use_in_method)
+ << Range << nameInfo.getName() << /*explicit*/ 1;
+ if (!Replacement.empty())
+ Diag << FixItHint::CreateInsertion(Loc, Replacement);
+ } else if (ContextClass && RepClass && SS.isEmpty() &&
+ !InExplicitObjectMethod && !InStaticMethod &&
+ !RepClass->Equals(ContextClass) &&
+ RepClass->Encloses(ContextClass))
// Unqualified lookup in a non-static member function found a member of an
// enclosing class.
SemaRef.Diag(Loc, diag::err_nested_non_static_member_use)
@@ -229,9 +249,16 @@ static void diagnoseInstanceReference(Sema &SemaRef,
else if (IsField)
SemaRef.Diag(Loc, diag::err_invalid_non_static_member_use)
<< nameInfo.getName() << Range;
- else
+ else if (!InExplicitObjectMethod)
SemaRef.Diag(Loc, diag::err_member_call_without_object)
- << Range;
+ << Range << /*static*/ 0;
+ else {
+ const auto *Callee = dyn_cast<CXXMethodDecl>(Rep);
+ auto Diag = SemaRef.Diag(Loc, diag::err_member_call_without_object)
+ << Range << Callee->isExplicitObjectMemberFunction();
+ if (!Replacement.empty())
+ Diag << FixItHint::CreateInsertion(Loc, Replacement);
+ }
}
/// Builds an expression which might be an implicit member expression.
@@ -255,13 +282,13 @@ ExprResult Sema::BuildPossibleImplicitMemberExpr(
[[fallthrough]];
case IMA_Static:
case IMA_Abstract:
- case IMA_Mixed_StaticContext:
- case IMA_Unresolved_StaticContext:
+ case IMA_Mixed_StaticOrExplicitContext:
+ case IMA_Unresolved_StaticOrExplicitContext:
if (TemplateArgs || TemplateKWLoc.isValid())
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, false, TemplateArgs);
return AsULE ? AsULE : BuildDeclarationNameExpr(SS, R, false);
- case IMA_Error_StaticContext:
+ case IMA_Error_StaticOrExplicitContext:
case IMA_Error_Unrelated:
diagnoseInstanceReference(*this, SS, R.getRepresentativeDecl(),
R.getLookupNameInfo());
diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp
index 5df830e..33e98bc 100644
--- a/clang/lib/Sema/SemaExprObjC.cpp
+++ b/clang/lib/Sema/SemaExprObjC.cpp
@@ -1800,7 +1800,8 @@ bool Sema::CheckMessageArgumentTypes(
// FIXME. This need be cleaned up.
if (Args.size() < NumNamedArgs) {
Diag(SelLoc, diag::err_typecheck_call_too_few_args)
- << 2 << NumNamedArgs << static_cast<unsigned>(Args.size());
+ << 2 << NumNamedArgs << static_cast<unsigned>(Args.size())
+ << /*is non object*/ 0;
return false;
}
@@ -1898,7 +1899,7 @@ bool Sema::CheckMessageArgumentTypes(
Diag(Args[NumNamedArgs]->getBeginLoc(),
diag::err_typecheck_call_too_many_args)
<< 2 /*method*/ << NumNamedArgs << static_cast<unsigned>(Args.size())
- << Method->getSourceRange()
+ << Method->getSourceRange() << /*is non object*/ 0
<< SourceRange(Args[NumNamedArgs]->getBeginLoc(),
Args.back()->getEndLoc());
}
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index de576cc..fd95b16 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -7421,8 +7421,9 @@ static bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) {
return true;
if (!isInStlNamespace(Callee->getParent()))
return false;
- if (!isRecordWithAttr<PointerAttr>(Callee->getThisObjectType()) &&
- !isRecordWithAttr<OwnerAttr>(Callee->getThisObjectType()))
+ if (!isRecordWithAttr<PointerAttr>(
+ Callee->getFunctionObjectParameterType()) &&
+ !isRecordWithAttr<OwnerAttr>(Callee->getFunctionObjectParameterType()))
return false;
if (Callee->getReturnType()->isPointerType() ||
isRecordWithAttr<PointerAttr>(Callee->getReturnType())) {
@@ -7557,7 +7558,7 @@ static bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
QualType LHST;
auto *MD = dyn_cast<CXXMethodDecl>(FD);
if (MD && MD->isCXXInstanceMember())
- LHST = Ctx.getLValueReferenceType(MD->getThisObjectType());
+ LHST = Ctx.getLValueReferenceType(MD->getFunctionObjectParameterType());
else
LHST = MD->getParamDecl(0)->getType();
if (Ctx.hasSameType(RetT, LHST))
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 1702ddb..6fd91bd 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -379,6 +379,38 @@ buildTypeForLambdaCallOperator(Sema &S, clang::CXXRecordDecl *Class,
return MethodType;
}
+// [C++2b] [expr.prim.lambda.closure] p4
+// Given a lambda with a lambda-capture, the type of the explicit object
+// parameter, if any, of the lambda's function call operator (possibly
+// instantiated from a function call operator template) shall be either:
+// - the closure type,
+// - class type derived from the closure type, or
+// - a reference to a possibly cv-qualified such type.
+void Sema::DiagnoseInvalidExplicitObjectParameterInLambda(
+ CXXMethodDecl *Method) {
+ if (!isLambdaCallWithExplicitObjectParameter(Method))
+ return;
+ CXXRecordDecl *RD = Method->getParent();
+ if (Method->getType()->isDependentType())
+ return;
+ if (RD->getLambdaCaptureDefault() == LambdaCaptureDefault::LCD_None &&
+ RD->capture_size() == 0)
+ return;
+ QualType ExplicitObjectParameterType = Method->getParamDecl(0)
+ ->getType()
+ .getNonReferenceType()
+ .getUnqualifiedType()
+ .getDesugaredType(getASTContext());
+ QualType LambdaType = getASTContext().getRecordType(RD);
+ if (LambdaType == ExplicitObjectParameterType)
+ return;
+ if (IsDerivedFrom(RD->getLocation(), ExplicitObjectParameterType, LambdaType))
+ return;
+ Diag(Method->getParamDecl(0)->getLocation(),
+ diag::err_invalid_explicit_object_type_in_lambda)
+ << ExplicitObjectParameterType;
+}
+
void Sema::handleLambdaNumbering(
CXXRecordDecl *Class, CXXMethodDecl *Method,
std::optional<CXXRecordDecl::LambdaNumbering> NumberingOverride) {
@@ -860,9 +892,17 @@ static TypeSourceInfo *getLambdaType(Sema &S, LambdaIntroducer &Intro,
if (ParamInfo.getNumTypeObjects() == 0) {
MethodTyInfo = getDummyLambdaType(S, Loc);
} else {
+ // Check explicit parameters
+ S.CheckExplicitObjectLambda(ParamInfo);
+
DeclaratorChunk::FunctionTypeInfo &FTI = ParamInfo.getFunctionTypeInfo();
+
+ bool HasExplicitObjectParameter =
+ ParamInfo.isExplicitObjectMemberFunction();
+
ExplicitResultType = FTI.hasTrailingReturnType();
- if (!FTI.hasMutableQualifier() && !IsLambdaStatic)
+ if (!FTI.hasMutableQualifier() && !IsLambdaStatic &&
+ !HasExplicitObjectParameter)
FTI.getOrCreateMethodQualifiers().SetTypeQual(DeclSpec::TQ_const, Loc);
if (ExplicitResultType && S.getLangOpts().HLSL) {
@@ -1686,7 +1726,7 @@ static void addFunctionPointerConversion(Sema &S, SourceRange IntroducerRange,
// function that will be the result of the conversion with a
// certain unique ID.
// When it is static we just return the static call operator instead.
- if (CallOperator->isInstance()) {
+ if (CallOperator->isImplicitObjectMemberFunction()) {
DeclarationName InvokerName =
&S.Context.Idents.get(getLambdaStaticInvokerName());
// FIXME: Instead of passing in the CallOperator->getTypeSourceInfo()
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 0ac2ac2..5ddae2d 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -910,6 +910,9 @@ static bool FunctionsCorrespond(ASTContext &Ctx, const FunctionDecl *X,
return false;
if (X->getNumParams() != Y->getNumParams())
return false;
+ // FIXME: when do rewritten comparison operators
+ // with explicit object parameters correspond?
+ // https://cplusplus.github.io/CWG/issues/2797.html
for (unsigned I = 0; I < X->getNumParams(); ++I)
if (!Ctx.hasSameUnqualifiedType(X->getParamDecl(I)->getType(),
Y->getParamDecl(I)->getType()))
@@ -994,7 +997,7 @@ bool OverloadCandidateSet::OperatorRewriteInfo::shouldAddReversed(
}
// Don't bother adding a reversed candidate that can never be a better
// match than the non-reversed version.
- return FD->getNumParams() != 2 ||
+ return FD->getNumNonObjectParams() != 2 ||
!S.Context.hasSameUnqualifiedType(FD->getParamDecl(0)->getType(),
FD->getParamDecl(1)->getType()) ||
FD->hasAttr<EnableIfAttr>();
@@ -1235,9 +1238,11 @@ Sema::CheckOverload(Scope *S, FunctionDecl *New, const LookupResult &Old,
return Ovl_Overload;
}
-bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
- bool UseMemberUsingDeclRules, bool ConsiderCudaAttrs,
- bool ConsiderRequiresClauses) {
+static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
+ FunctionDecl *Old,
+ bool UseMemberUsingDeclRules,
+ bool ConsiderCudaAttrs,
+ bool UseOverrideRules = false) {
// C++ [basic.start.main]p2: This function shall not be overloaded.
if (New->isMain())
return false;
@@ -1256,8 +1261,8 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
return true;
// Is the function New an overload of the function Old?
- QualType OldQType = Context.getCanonicalType(Old->getType());
- QualType NewQType = Context.getCanonicalType(New->getType());
+ QualType OldQType = SemaRef.Context.getCanonicalType(Old->getType());
+ QualType NewQType = SemaRef.Context.getCanonicalType(New->getType());
// Compare the signatures (C++ 1.3.10) of the two functions to
// determine whether they are overloads. If we find any mismatch
@@ -1275,10 +1280,7 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
// The signature of a function includes the types of its
// parameters (C++ 1.3.10), which includes the presence or absence
// of the ellipsis; see C++ DR 357).
- if (OldQType != NewQType &&
- (OldType->getNumParams() != NewType->getNumParams() ||
- OldType->isVariadic() != NewType->isVariadic() ||
- !FunctionParamTypesAreEqual(OldType, NewType)))
+ if (OldQType != NewQType && OldType->isVariadic() != NewType->isVariadic())
return true;
// For member-like friends, the enclosing class is part of the signature.
@@ -1286,6 +1288,128 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
Old->isMemberLikeConstrainedFriend()) &&
!New->getLexicalDeclContext()->Equals(Old->getLexicalDeclContext()))
return true;
+ const auto *OldMethod = dyn_cast<CXXMethodDecl>(Old);
+ const auto *NewMethod = dyn_cast<CXXMethodDecl>(New);
+
+ int OldParamsOffset = 0;
+ int NewParamsOffset = 0;
+
+ // When determining if a method is an overload from a base class, act as if
+ // the implicit object parameter are of the same type.
+
+ auto NormalizeQualifiers = [&](const CXXMethodDecl *M, Qualifiers Q) {
+ if (M->isExplicitObjectMemberFunction())
+ return Q;
+
+ // We do not allow overloading based off of '__restrict'.
+ Q.removeRestrict();
+
+ // We may not have applied the implicit const for a constexpr member
+ // function yet (because we haven't yet resolved whether this is a static
+ // or non-static member function). Add it now, on the assumption that this
+ // is a redeclaration of OldMethod.
+ if (!SemaRef.getLangOpts().CPlusPlus14 &&
+ (M->isConstexpr() || M->isConsteval()) &&
+ !isa<CXXConstructorDecl>(NewMethod))
+ Q.addConst();
+ return Q;
+ };
+
+ auto CompareType = [&](QualType Base, QualType D) {
+ auto BS = Base.getNonReferenceType().getCanonicalType().split();
+ BS.Quals = NormalizeQualifiers(OldMethod, BS.Quals);
+
+ auto DS = D.getNonReferenceType().getCanonicalType().split();
+ DS.Quals = NormalizeQualifiers(NewMethod, DS.Quals);
+
+ if (BS.Quals != DS.Quals)
+ return false;
+
+ if (OldMethod->isImplicitObjectMemberFunction() &&
+ OldMethod->getParent() != NewMethod->getParent()) {
+ QualType ParentType =
+ SemaRef.Context.getTypeDeclType(OldMethod->getParent())
+ .getCanonicalType();
+ if (ParentType.getTypePtr() != BS.Ty)
+ return false;
+ BS.Ty = DS.Ty;
+ }
+
+ // FIXME: should we ignore some type attributes here?
+ if (BS.Ty != DS.Ty)
+ return false;
+
+ if (Base->isLValueReferenceType())
+ return D->isLValueReferenceType();
+ return Base->isRValueReferenceType() == D->isRValueReferenceType();
+ };
+
+ // If the function is a class member, its signature includes the
+ // cv-qualifiers (if any) and ref-qualifier (if any) on the function itself.
+ auto DiagnoseInconsistentRefQualifiers = [&]() {
+ if (SemaRef.LangOpts.CPlusPlus23)
+ return false;
+ if (OldMethod->getRefQualifier() == NewMethod->getRefQualifier())
+ return false;
+ if (OldMethod->isExplicitObjectMemberFunction() ||
+ NewMethod->isExplicitObjectMemberFunction())
+ return false;
+ if (!UseMemberUsingDeclRules && (OldMethod->getRefQualifier() == RQ_None ||
+ NewMethod->getRefQualifier() == RQ_None)) {
+ SemaRef.Diag(NewMethod->getLocation(), diag::err_ref_qualifier_overload)
+ << NewMethod->getRefQualifier() << OldMethod->getRefQualifier();
+ SemaRef.Diag(OldMethod->getLocation(), diag::note_previous_declaration);
+ return true;
+ }
+ return false;
+ };
+
+ if (OldMethod && OldMethod->isExplicitObjectMemberFunction())
+ OldParamsOffset++;
+ if (NewMethod && NewMethod->isExplicitObjectMemberFunction())
+ NewParamsOffset++;
+
+ if (OldType->getNumParams() - OldParamsOffset !=
+ NewType->getNumParams() - NewParamsOffset ||
+ !SemaRef.FunctionParamTypesAreEqual(
+ {OldType->param_type_begin() + OldParamsOffset,
+ OldType->param_type_end()},
+ {NewType->param_type_begin() + NewParamsOffset,
+ NewType->param_type_end()},
+ nullptr)) {
+ return true;
+ }
+
+ if (OldMethod && NewMethod && !OldMethod->isStatic() &&
+ !OldMethod->isStatic()) {
+ bool HaveCorrespondingObjectParameters = [&](const CXXMethodDecl *Old,
+ const CXXMethodDecl *New) {
+ auto NewObjectType = New->getFunctionObjectParameterReferenceType();
+ auto OldObjectType = Old->getFunctionObjectParameterReferenceType();
+
+ auto IsImplicitWithNoRefQual = [](const CXXMethodDecl *F) {
+ return F->getRefQualifier() == RQ_None &&
+ !F->isExplicitObjectMemberFunction();
+ };
+
+ if (IsImplicitWithNoRefQual(Old) != IsImplicitWithNoRefQual(New) &&
+ CompareType(OldObjectType.getNonReferenceType(),
+ NewObjectType.getNonReferenceType()))
+ return true;
+ return CompareType(OldObjectType, NewObjectType);
+ }(OldMethod, NewMethod);
+
+ if (!HaveCorrespondingObjectParameters) {
+ if (DiagnoseInconsistentRefQualifiers())
+ return true;
+ // CWG2554
+ // and, if at least one is an explicit object member function, ignoring
+ // object parameters
+ if (!UseOverrideRules || (!NewMethod->isExplicitObjectMemberFunction() &&
+ !OldMethod->isExplicitObjectMemberFunction()))
+ return true;
+ }
+ }
if (NewTemplate) {
// C++ [temp.over.link]p4:
@@ -1297,11 +1421,11 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
//
// We check the return type and template parameter lists for function
// templates first; the remaining checks follow.
- bool SameTemplateParameterList = TemplateParameterListsAreEqual(
+ bool SameTemplateParameterList = SemaRef.TemplateParameterListsAreEqual(
NewTemplate, NewTemplate->getTemplateParameters(), OldTemplate,
- OldTemplate->getTemplateParameters(), false, TPL_TemplateMatch);
- bool SameReturnType = Context.hasSameType(Old->getDeclaredReturnType(),
- New->getDeclaredReturnType());
+ OldTemplate->getTemplateParameters(), false, Sema::TPL_TemplateMatch);
+ bool SameReturnType = SemaRef.Context.hasSameType(
+ Old->getDeclaredReturnType(), New->getDeclaredReturnType());
// FIXME(GH58571): Match template parameter list even for non-constrained
// template heads. This currently ensures that the code prior to C++20 is
// not newly broken.
@@ -1324,59 +1448,19 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
return true;
}
- if (ConsiderRequiresClauses) {
+ if (!UseOverrideRules) {
Expr *NewRC = New->getTrailingRequiresClause(),
*OldRC = Old->getTrailingRequiresClause();
if ((NewRC != nullptr) != (OldRC != nullptr))
return true;
- if (NewRC && !AreConstraintExpressionsEqual(Old, OldRC, New, NewRC))
- return true;
- }
-
- // If the function is a class member, its signature includes the
- // cv-qualifiers (if any) and ref-qualifier (if any) on the function itself.
- //
- // As part of this, also check whether one of the member functions
- // is static, in which case they are not overloads (C++
- // 13.1p2). While not part of the definition of the signature,
- // this check is important to determine whether these functions
- // can be overloaded.
- CXXMethodDecl *OldMethod = dyn_cast<CXXMethodDecl>(Old);
- CXXMethodDecl *NewMethod = dyn_cast<CXXMethodDecl>(New);
- if (OldMethod && NewMethod &&
- !OldMethod->isStatic() && !NewMethod->isStatic()) {
- if (OldMethod->getRefQualifier() != NewMethod->getRefQualifier()) {
- if (!UseMemberUsingDeclRules &&
- (OldMethod->getRefQualifier() == RQ_None ||
- NewMethod->getRefQualifier() == RQ_None)) {
- // C++20 [over.load]p2:
- // - Member function declarations with the same name, the same
- // parameter-type-list, and the same trailing requires-clause (if
- // any), as well as member function template declarations with the
- // same name, the same parameter-type-list, the same trailing
- // requires-clause (if any), and the same template-head, cannot be
- // overloaded if any of them, but not all, have a ref-qualifier.
- Diag(NewMethod->getLocation(), diag::err_ref_qualifier_overload)
- << NewMethod->getRefQualifier() << OldMethod->getRefQualifier();
- Diag(OldMethod->getLocation(), diag::note_previous_declaration);
- }
+ if (NewRC && !SemaRef.AreConstraintExpressionsEqual(Old, OldRC, New, NewRC))
return true;
- }
+ }
- // We may not have applied the implicit const for a constexpr member
- // function yet (because we haven't yet resolved whether this is a static
- // or non-static member function). Add it now, on the assumption that this
- // is a redeclaration of OldMethod.
- auto OldQuals = OldMethod->getMethodQualifiers();
- auto NewQuals = NewMethod->getMethodQualifiers();
- if (!getLangOpts().CPlusPlus14 && NewMethod->isConstexpr() &&
- !isa<CXXConstructorDecl>(NewMethod))
- NewQuals.addConst();
- // We do not allow overloading based off of '__restrict'.
- OldQuals.removeRestrict();
- NewQuals.removeRestrict();
- if (OldQuals != NewQuals)
+ if (NewMethod && OldMethod && OldMethod->isImplicitObjectMemberFunction() &&
+ NewMethod->isImplicitObjectMemberFunction()) {
+ if (DiagnoseInconsistentRefQualifiers())
return true;
}
@@ -1398,20 +1482,20 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
if (NewI == NewE || OldI == OldE)
return true;
llvm::FoldingSetNodeID NewID, OldID;
- NewI->getCond()->Profile(NewID, Context, true);
- OldI->getCond()->Profile(OldID, Context, true);
+ NewI->getCond()->Profile(NewID, SemaRef.Context, true);
+ OldI->getCond()->Profile(OldID, SemaRef.Context, true);
if (NewID != OldID)
return true;
}
- if (getLangOpts().CUDA && ConsiderCudaAttrs) {
+ if (SemaRef.getLangOpts().CUDA && ConsiderCudaAttrs) {
// Don't allow overloading of destructors. (In theory we could, but it
// would be a giant change to clang.)
if (!isa<CXXDestructorDecl>(New)) {
- CUDAFunctionTarget NewTarget = IdentifyCUDATarget(New),
- OldTarget = IdentifyCUDATarget(Old);
- if (NewTarget != CFT_InvalidTarget) {
- assert((OldTarget != CFT_InvalidTarget) &&
+ Sema::CUDAFunctionTarget NewTarget = SemaRef.IdentifyCUDATarget(New),
+ OldTarget = SemaRef.IdentifyCUDATarget(Old);
+ if (NewTarget != Sema::CFT_InvalidTarget) {
+ assert((OldTarget != Sema::CFT_InvalidTarget) &&
"Unexpected invalid target.");
// Allow overloading of functions with same signature and different CUDA
@@ -1426,6 +1510,20 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
return false;
}
+bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
+ bool UseMemberUsingDeclRules, bool ConsiderCudaAttrs) {
+ return IsOverloadOrOverrideImpl(*this, New, Old, UseMemberUsingDeclRules,
+ ConsiderCudaAttrs);
+}
+
+bool Sema::IsOverride(FunctionDecl *MD, FunctionDecl *BaseMD,
+ bool UseMemberUsingDeclRules, bool ConsiderCudaAttrs) {
+ return IsOverloadOrOverrideImpl(*this, MD, BaseMD,
+ /*UseMemberUsingDeclRules=*/false,
+ /*ConsiderCudaAttrs=*/true,
+ /*UseOverrideRules=*/true);
+}
+
/// Tries a user-defined conversion from From to ToType.
///
/// Produces an implicit conversion sequence for when a standard conversion
@@ -1884,7 +1982,8 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
// fact that non-static member functions *must* have such an address-of
// expression.
CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Fn);
- if (Method && !Method->isStatic()) {
+ if (Method && !Method->isStatic() &&
+ !Method->isExplicitObjectMemberFunction()) {
assert(isa<UnaryOperator>(From->IgnoreParens()) &&
"Non-unary operator on non-static member address");
assert(cast<UnaryOperator>(From->IgnoreParens())->getOpcode()
@@ -3104,30 +3203,40 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
/// If `Reversed` is true, the parameters of `NewType` will be compared in
/// reverse order. That's useful if one of the functions is being used as a C++20
/// synthesized operator overload with a reversed parameter order.
-bool Sema::FunctionParamTypesAreEqual(const FunctionProtoType *OldType,
- const FunctionProtoType *NewType,
- unsigned *ArgPos, bool Reversed) {
- assert(OldType->getNumParams() == NewType->getNumParams() &&
+bool Sema::FunctionParamTypesAreEqual(ArrayRef<QualType> Old,
+ ArrayRef<QualType> New, unsigned *ArgPos,
+ bool Reversed) {
+ assert(llvm::size(Old) == llvm::size(New) &&
"Can't compare parameters of functions with different number of "
"parameters!");
- for (size_t I = 0; I < OldType->getNumParams(); I++) {
+
+ for (auto &&[Idx, Type] : llvm::enumerate(Old)) {
// Reverse iterate over the parameters of `OldType` if `Reversed` is true.
- size_t J = Reversed ? (OldType->getNumParams() - I - 1) : I;
+ size_t J = Reversed ? (llvm::size(New) - Idx - 1) : Idx;
// Ignore address spaces in pointee type. This is to disallow overloading
// on __ptr32/__ptr64 address spaces.
- QualType Old = Context.removePtrSizeAddrSpace(OldType->getParamType(I).getUnqualifiedType());
- QualType New = Context.removePtrSizeAddrSpace(NewType->getParamType(J).getUnqualifiedType());
+ QualType OldType =
+ Context.removePtrSizeAddrSpace(Type.getUnqualifiedType());
+ QualType NewType =
+ Context.removePtrSizeAddrSpace((New.begin() + J)->getUnqualifiedType());
- if (!Context.hasSameType(Old, New)) {
+ if (!Context.hasSameType(OldType, NewType)) {
if (ArgPos)
- *ArgPos = I;
+ *ArgPos = Idx;
return false;
}
}
return true;
}
+bool Sema::FunctionParamTypesAreEqual(const FunctionProtoType *OldType,
+ const FunctionProtoType *NewType,
+ unsigned *ArgPos, bool Reversed) {
+ return FunctionParamTypesAreEqual(OldType->param_types(),
+ NewType->param_types(), ArgPos, Reversed);
+}
+
/// CheckPointerConversion - Check the pointer conversion from the
/// expression From to the type ToType. This routine checks for
/// ambiguous or inaccessible derived-to-base pointer
@@ -3532,7 +3641,7 @@ IsInitializerListConstructorConversion(Sema &S, Expr *From, QualType ToType,
case OR_Success: {
// Record the standard conversion we used and the conversion function.
CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(Best->Function);
- QualType ThisType = Constructor->getThisObjectType();
+ QualType ThisType = Constructor->getFunctionObjectParameterType();
// Initializer lists don't have conversions as such.
User.Before.setAsIdentityConversion();
User.HadMultipleCandidates = HadMultipleCandidates;
@@ -3734,7 +3843,7 @@ IsUserDefinedConversion(Sema &S, Expr *From, QualType ToType,
User.ConversionFunction = Constructor;
User.FoundConversionFunction = Best->FoundDecl;
User.After.setAsIdentityConversion();
- User.After.setFromType(Constructor->getThisObjectType());
+ User.After.setFromType(Constructor->getFunctionObjectParameterType());
User.After.setAllToTypes(ToType);
return Result;
}
@@ -5505,11 +5614,45 @@ static bool TryCopyInitialization(const CanQualType FromQTy,
/// TryObjectArgumentInitialization - Try to initialize the object
/// parameter of the given member function (@c Method) from the
/// expression @p From.
-static ImplicitConversionSequence
-TryObjectArgumentInitialization(Sema &S, SourceLocation Loc, QualType FromType,
- Expr::Classification FromClassification,
- CXXMethodDecl *Method,
- CXXRecordDecl *ActingContext) {
+static ImplicitConversionSequence TryObjectArgumentInitialization(
+ Sema &S, SourceLocation Loc, QualType FromType,
+ Expr::Classification FromClassification, CXXMethodDecl *Method,
+ const CXXRecordDecl *ActingContext, bool InOverloadResolution = false,
+ QualType ExplicitParameterType = QualType(),
+ bool SuppressUserConversion = false) {
+
+ // We need to have an object of class type.
+ if (const auto *PT = FromType->getAs<PointerType>()) {
+ FromType = PT->getPointeeType();
+
+ // When we had a pointer, it's implicitly dereferenced, so we
+ // better have an lvalue.
+ assert(FromClassification.isLValue());
+ }
+
+ auto ValueKindFromClassification = [](Expr::Classification C) {
+ if (C.isPRValue())
+ return clang::VK_PRValue;
+ if (C.isXValue())
+ return VK_XValue;
+ return clang::VK_LValue;
+ };
+
+ if (Method->isExplicitObjectMemberFunction()) {
+ if (ExplicitParameterType.isNull())
+ ExplicitParameterType = Method->getFunctionObjectParameterReferenceType();
+ OpaqueValueExpr TmpExpr(Loc, FromType.getNonReferenceType(),
+ ValueKindFromClassification(FromClassification));
+ ImplicitConversionSequence ICS = TryCopyInitialization(
+ S, &TmpExpr, ExplicitParameterType, SuppressUserConversion,
+ /*InOverloadResolution=*/true, false);
+ if (ICS.isBad())
+ ICS.Bad.FromExpr = nullptr;
+ return ICS;
+ }
+
+ assert(FromType->isRecordType());
+
QualType ClassType = S.Context.getTypeDeclType(ActingContext);
// [class.dtor]p2: A destructor can be invoked for a const, volatile or
// const volatile object.
@@ -5525,17 +5668,6 @@ TryObjectArgumentInitialization(Sema &S, SourceLocation Loc, QualType FromType,
// to exit early.
ImplicitConversionSequence ICS;
- // We need to have an object of class type.
- if (const PointerType *PT = FromType->getAs<PointerType>()) {
- FromType = PT->getPointeeType();
-
- // When we had a pointer, it's implicitly dereferenced, so we
- // better have an lvalue.
- assert(FromClassification.isLValue());
- }
-
- assert(FromType->isRecordType());
-
// C++0x [over.match.funcs]p4:
// For non-static member functions, the type of the implicit object
// parameter is
@@ -5582,9 +5714,9 @@ TryObjectArgumentInitialization(Sema &S, SourceLocation Loc, QualType FromType,
ImplicitConversionKind SecondKind;
if (ClassTypeCanon == FromTypeCanon.getLocalUnqualifiedType()) {
SecondKind = ICK_Identity;
- } else if (S.IsDerivedFrom(Loc, FromType, ClassType))
+ } else if (S.IsDerivedFrom(Loc, FromType, ClassType)) {
SecondKind = ICK_Derived_To_Base;
- else {
+ } else if (!Method->isExplicitObjectMemberFunction()) {
ICS.setBad(BadConversionSequence::unrelated_class,
FromType, ImplicitParamType);
return ICS;
@@ -5634,13 +5766,11 @@ TryObjectArgumentInitialization(Sema &S, SourceLocation Loc, QualType FromType,
/// PerformObjectArgumentInitialization - Perform initialization of
/// the implicit object parameter for the given Method with the given
/// expression.
-ExprResult
-Sema::PerformObjectArgumentInitialization(Expr *From,
- NestedNameSpecifier *Qualifier,
- NamedDecl *FoundDecl,
- CXXMethodDecl *Method) {
+ExprResult Sema::PerformImplicitObjectArgumentInitialization(
+ Expr *From, NestedNameSpecifier *Qualifier, NamedDecl *FoundDecl,
+ CXXMethodDecl *Method) {
QualType FromRecordType, DestType;
- QualType ImplicitParamRecordType = Method->getThisObjectType();
+ QualType ImplicitParamRecordType = Method->getFunctionObjectParameterType();
Expr::Classification FromClassification;
if (const PointerType *PT = From->getType()->getAs<PointerType>()) {
@@ -6134,6 +6264,64 @@ ExprResult Sema::PerformContextuallyConvertToObjCPointer(Expr *From) {
return ExprResult();
}
+static QualType GetExplicitObjectType(Sema &S, const Expr *MemExprE) {
+ const Expr *Base = nullptr;
+ assert((isa<UnresolvedMemberExpr, MemberExpr>(MemExprE)) &&
+ "expected a member expression");
+
+ if (const auto M = dyn_cast<UnresolvedMemberExpr>(MemExprE);
+ M && !M->isImplicitAccess())
+ Base = M->getBase();
+ else if (const auto M = dyn_cast<MemberExpr>(MemExprE);
+ M && !M->isImplicitAccess())
+ Base = M->getBase();
+
+ QualType T = Base ? Base->getType() : S.getCurrentThisType();
+
+ if (T->isPointerType())
+ T = T->getPointeeType();
+
+ return T;
+}
+
+static Expr *GetExplicitObjectExpr(Sema &S, Expr *Obj,
+ const FunctionDecl *Fun) {
+ QualType ObjType = Obj->getType();
+ if (ObjType->isPointerType()) {
+ ObjType = ObjType->getPointeeType();
+ Obj = UnaryOperator::Create(S.getASTContext(), Obj, UO_Deref, ObjType,
+ VK_LValue, OK_Ordinary, SourceLocation(),
+ /*CanOverflow=*/false, FPOptionsOverride());
+ }
+ if (Obj->Classify(S.getASTContext()).isPRValue()) {
+ Obj = S.CreateMaterializeTemporaryExpr(
+ ObjType, Obj,
+ !Fun->getParamDecl(0)->getType()->isRValueReferenceType());
+ }
+ return Obj;
+}
+
+ExprResult Sema::InitializeExplicitObjectArgument(Sema &S, Expr *Obj,
+ FunctionDecl *Fun) {
+ Obj = GetExplicitObjectExpr(S, Obj, Fun);
+ return S.PerformCopyInitialization(
+ InitializedEntity::InitializeParameter(S.Context, Fun->getParamDecl(0)),
+ Obj->getExprLoc(), Obj);
+}
+
+static void PrepareExplicitObjectArgument(Sema &S, CXXMethodDecl *Method,
+ Expr *Object, MultiExprArg &Args,
+ SmallVectorImpl<Expr *> &NewArgs) {
+ assert(Method->isExplicitObjectMemberFunction() &&
+ "Method is not an explicit member function");
+ assert(NewArgs.empty() && "NewArgs should be empty");
+ NewArgs.reserve(Args.size() + 1);
+ Expr *This = GetExplicitObjectExpr(S, Object, Method);
+ NewArgs.push_back(This);
+ NewArgs.append(Args.begin(), Args.end());
+ Args = NewArgs;
+}
+
/// Determine whether the provided type is an integral type, or an enumeration
/// type of a permitted flavor.
bool Sema::ICEConvertDiagnoser::match(QualType T) {
@@ -6868,7 +7056,7 @@ static bool convertArgsForAvailabilityChecks(
assert(!isa<CXXConstructorDecl>(Method) &&
"Shouldn't have `this` for ctors!");
assert(!Method->isStatic() && "Shouldn't have `this` for static methods!");
- ExprResult R = S.PerformObjectArgumentInitialization(
+ ExprResult R = S.PerformImplicitObjectArgumentInitialization(
ThisArg, /*Qualifier=*/nullptr, Method, Method);
if (R.isInvalid())
return false;
@@ -7168,7 +7356,8 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
Candidate.IgnoreObjectArgument = false;
Candidate.ExplicitCallArguments = Args.size();
- unsigned NumParams = Proto->getNumParams();
+ unsigned NumParams = Method->getNumExplicitParams();
+ unsigned ExplicitOffset = Method->isExplicitObjectMemberFunction() ? 1 : 0;
// (C++ 13.3.2p2): A candidate function having fewer than m
// parameters is viable only if it has an ellipsis in its parameter
@@ -7186,7 +7375,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
// (8.3.6). For the purposes of overload resolution, the
// parameter list is truncated on the right, so that there are
// exactly m parameters.
- unsigned MinRequiredArgs = Method->getMinRequiredArguments();
+ unsigned MinRequiredArgs = Method->getMinRequiredExplicitArguments();
if (Args.size() < MinRequiredArgs && !PartialOverloading) {
// Not enough arguments.
Candidate.Viable = false;
@@ -7215,7 +7404,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
// parameter.
Candidate.Conversions[FirstConvIdx] = TryObjectArgumentInitialization(
*this, CandidateSet.getLocation(), ObjectType, ObjectClassification,
- Method, ActingContext);
+ Method, ActingContext, /*InOverloadResolution=*/true);
if (Candidate.Conversions[FirstConvIdx].isBad()) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_conversion;
@@ -7255,7 +7444,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
// exist for each argument an implicit conversion sequence
// (13.3.3.1) that converts that argument to the corresponding
// parameter of F.
- QualType ParamType = Proto->getParamType(ArgIdx);
+ QualType ParamType = Proto->getParamType(ArgIdx + ExplicitOffset);
Candidate.Conversions[ConvIdx]
= TryCopyInitialization(*this, Args[ArgIdx], ParamType,
SuppressUserConversions,
@@ -7320,8 +7509,8 @@ void Sema::AddMethodTemplateCandidate(
ConversionSequenceList Conversions;
if (TemplateDeductionResult Result = DeduceTemplateArguments(
MethodTmpl, ExplicitTemplateArgs, Args, Specialization, Info,
- PartialOverloading, /*AggregateDeductionCandidate=*/false,
- [&](ArrayRef<QualType> ParamTypes) {
+ PartialOverloading, /*AggregateDeductionCandidate=*/false, ObjectType,
+ ObjectClassification, [&](ArrayRef<QualType> ParamTypes) {
return CheckNonDependentConversions(
MethodTmpl, ParamTypes, Args, CandidateSet, Conversions,
SuppressUserConversions, ActingContext, ObjectType,
@@ -7405,6 +7594,8 @@ void Sema::AddTemplateOverloadCandidate(
if (TemplateDeductionResult Result = DeduceTemplateArguments(
FunctionTemplate, ExplicitTemplateArgs, Args, Specialization, Info,
PartialOverloading, AggregateCandidateDeduction,
+ /*ObjectType=*/QualType(),
+ /*ObjectClassification=*/Expr::Classification(),
[&](ArrayRef<QualType> ParamTypes) {
return CheckNonDependentConversions(
FunctionTemplate, ParamTypes, Args, CandidateSet, Conversions,
@@ -7477,16 +7668,24 @@ bool Sema::CheckNonDependentConversions(
if (HasThisConversion && !cast<CXXMethodDecl>(FD)->isStatic() &&
!ObjectType.isNull()) {
unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed ? 1 : 0;
- Conversions[ConvIdx] = TryObjectArgumentInitialization(
- *this, CandidateSet.getLocation(), ObjectType, ObjectClassification,
- Method, ActingContext);
- if (Conversions[ConvIdx].isBad())
- return true;
+ if (!FD->hasCXXExplicitFunctionObjectParameter() ||
+ !ParamTypes[0]->isDependentType()) {
+ Conversions[ConvIdx] = TryObjectArgumentInitialization(
+ *this, CandidateSet.getLocation(), ObjectType, ObjectClassification,
+ Method, ActingContext, /*InOverloadResolution=*/true,
+ FD->hasCXXExplicitFunctionObjectParameter() ? ParamTypes[0]
+ : QualType());
+ if (Conversions[ConvIdx].isBad())
+ return true;
+ }
}
+ unsigned Offset =
+ Method && Method->hasCXXExplicitFunctionObjectParameter() ? 1 : 0;
+
for (unsigned I = 0, N = std::min(ParamTypes.size(), Args.size()); I != N;
++I) {
- QualType ParamType = ParamTypes[I];
+ QualType ParamType = ParamTypes[I + Offset];
if (!ParamType->isDependentType()) {
unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed
? 0
@@ -7619,15 +7818,21 @@ void Sema::AddConversionCandidate(
//
// Determine the implicit conversion sequence for the implicit
// object parameter.
- QualType ImplicitParamType = From->getType();
- if (const PointerType *FromPtrType = ImplicitParamType->getAs<PointerType>())
- ImplicitParamType = FromPtrType->getPointeeType();
- CXXRecordDecl *ConversionContext
- = cast<CXXRecordDecl>(ImplicitParamType->castAs<RecordType>()->getDecl());
-
+ QualType ObjectType = From->getType();
+ if (const auto *FromPtrType = ObjectType->getAs<PointerType>())
+ ObjectType = FromPtrType->getPointeeType();
+ const auto *ConversionContext =
+ cast<CXXRecordDecl>(ObjectType->castAs<RecordType>()->getDecl());
+
+ // C++23 [over.best.ics.general]
+ // However, if the target is [...]
+ // - the object parameter of a user-defined conversion function
+ // [...] user-defined conversion sequences are not considered.
Candidate.Conversions[0] = TryObjectArgumentInitialization(
*this, CandidateSet.getLocation(), From->getType(),
- From->Classify(Context), Conversion, ConversionContext);
+ From->Classify(Context), Conversion, ConversionContext,
+ /*InOverloadResolution*/ false, /*ExplicitParameterType=*/QualType(),
+ /*SuppressUserConversion*/ true);
if (Candidate.Conversions[0].isBad()) {
Candidate.Viable = false;
@@ -7781,11 +7986,14 @@ void Sema::AddTemplateConversionCandidate(
return;
}
+ QualType ObjectType = From->getType();
+ Expr::Classification ObjectClassification = From->Classify(getASTContext());
+
TemplateDeductionInfo Info(CandidateSet.getLocation());
CXXConversionDecl *Specialization = nullptr;
- if (TemplateDeductionResult Result
- = DeduceTemplateArguments(FunctionTemplate, ToType,
- Specialization, Info)) {
+ if (TemplateDeductionResult Result = DeduceTemplateArguments(
+ FunctionTemplate, ObjectType, ObjectClassification, ToType,
+ Specialization, Info)) {
OverloadCandidate &Candidate = CandidateSet.addCandidate();
Candidate.FoundDecl = FoundDecl;
Candidate.Function = FunctionTemplate->getTemplatedDecl();
@@ -7837,9 +8045,18 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion,
// Determine the implicit conversion sequence for the implicit
// object parameter.
- ImplicitConversionSequence ObjectInit = TryObjectArgumentInitialization(
- *this, CandidateSet.getLocation(), Object->getType(),
- Object->Classify(Context), Conversion, ActingContext);
+ ImplicitConversionSequence ObjectInit;
+ if (Conversion->hasCXXExplicitFunctionObjectParameter()) {
+ ObjectInit = TryCopyInitialization(*this, Object,
+ Conversion->getParamDecl(0)->getType(),
+ /*SuppressUserConversions=*/false,
+ /*InOverloadResolution=*/true, false);
+ } else {
+ ObjectInit = TryObjectArgumentInitialization(
+ *this, CandidateSet.getLocation(), Object->getType(),
+ Object->Classify(Context), Conversion, ActingContext);
+ }
+
if (ObjectInit.isBad()) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_bad_conversion;
@@ -9865,11 +10082,7 @@ getImplicitObjectParamType(ASTContext &Context, const FunctionDecl *F) {
// Static member functions' object parameters match all types.
if (M->isStatic())
return QualType();
-
- QualType T = M->getThisObjectType();
- if (M->getRefQualifier() == RQ_RValue)
- return Context.getRValueReferenceType(T);
- return Context.getLValueReferenceType(T);
+ return M->getFunctionObjectParameterReferenceType();
}
static bool haveSameParameterTypes(ASTContext &Context, const FunctionDecl *F1,
@@ -11072,39 +11285,43 @@ static void DiagnoseArityMismatch(Sema &S, NamedDecl *Found, Decl *D,
// TODO: treat calls to a missing default constructor as a special case
const auto *FnTy = Fn->getType()->castAs<FunctionProtoType>();
- unsigned MinParams = Fn->getMinRequiredArguments();
+ unsigned MinParams = Fn->getMinRequiredExplicitArguments();
// at least / at most / exactly
+ bool HasExplicitObjectParam = Fn->hasCXXExplicitFunctionObjectParameter();
+ unsigned ParamCount = FnTy->getNumParams() - (HasExplicitObjectParam ? 1 : 0);
unsigned mode, modeCount;
if (NumFormalArgs < MinParams) {
- if (MinParams != FnTy->getNumParams() || FnTy->isVariadic() ||
+ if (MinParams != ParamCount || FnTy->isVariadic() ||
FnTy->isTemplateVariadic())
mode = 0; // "at least"
else
mode = 2; // "exactly"
modeCount = MinParams;
} else {
- if (MinParams != FnTy->getNumParams())
+ if (MinParams != ParamCount)
mode = 1; // "at most"
else
mode = 2; // "exactly"
- modeCount = FnTy->getNumParams();
+ modeCount = ParamCount;
}
std::string Description;
std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
ClassifyOverloadCandidate(S, Found, Fn, CRK_None, Description);
- if (modeCount == 1 && Fn->getParamDecl(0)->getDeclName())
+ if (modeCount == 1 &&
+ Fn->getParamDecl(HasExplicitObjectParam ? 1 : 0)->getDeclName())
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity_one)
<< (unsigned)FnKindPair.first << (unsigned)FnKindPair.second
- << Description << mode << Fn->getParamDecl(0) << NumFormalArgs
- << Fn->getParametersSourceRange();
+ << Description << mode
+ << Fn->getParamDecl(HasExplicitObjectParam ? 1 : 0) << NumFormalArgs
+ << HasExplicitObjectParam << Fn->getParametersSourceRange();
else
S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity)
<< (unsigned)FnKindPair.first << (unsigned)FnKindPair.second
<< Description << mode << modeCount << NumFormalArgs
- << Fn->getParametersSourceRange();
+ << HasExplicitObjectParam << Fn->getParametersSourceRange();
MaybeEmitInheritedConstructorNote(S, Found);
}
@@ -12448,7 +12665,9 @@ private:
= dyn_cast<CXXMethodDecl>(FunctionTemplate->getTemplatedDecl())) {
// Skip non-static function templates when converting to pointer, and
// static when converting to member pointer.
- if (Method->isStatic() == TargetTypeIsNonStaticMemberFunction)
+ bool CanConvertToFunctionPointer =
+ Method->isStatic() || Method->isExplicitObjectMemberFunction();
+ if (CanConvertToFunctionPointer == TargetTypeIsNonStaticMemberFunction)
return false;
}
else if (TargetTypeIsNonStaticMemberFunction)
@@ -12493,7 +12712,9 @@ private:
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Fn)) {
// Skip non-static functions when converting to pointer, and static
// when converting to member pointer.
- if (Method->isStatic() == TargetTypeIsNonStaticMemberFunction)
+ bool CanConvertToFunctionPointer =
+ Method->isStatic() || Method->isExplicitObjectMemberFunction();
+ if (CanConvertToFunctionPointer == TargetTypeIsNonStaticMemberFunction)
return false;
}
else if (TargetTypeIsNonStaticMemberFunction)
@@ -12895,7 +13116,10 @@ bool Sema::resolveAndFixAddressOfSingleOverloadCandidate(
// for both.
DiagnoseUseOfDecl(Found, E->getExprLoc());
CheckAddressOfMemberAccess(E, DAP);
- Expr *Fixed = FixOverloadedFunctionReference(E, DAP, Found);
+ ExprResult Res = FixOverloadedFunctionReference(E, DAP, Found);
+ if (Res.isInvalid())
+ return false;
+ Expr *Fixed = Res.get();
if (DoFunctionPointerConversion && Fixed->getType()->isFunctionType())
SrcExpr = DefaultFunctionArrayConversion(Fixed, /*Diagnose=*/false);
else
@@ -13557,10 +13781,13 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
SemaRef.CheckUnresolvedLookupAccess(ULE, (*Best)->FoundDecl);
if (SemaRef.DiagnoseUseOfDecl(FDecl, ULE->getNameLoc()))
return ExprError();
- Fn = SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl);
- return SemaRef.BuildResolvedCallExpr(Fn, FDecl, LParenLoc, Args, RParenLoc,
- ExecConfig, /*IsExecConfig=*/false,
- (*Best)->IsADLCandidate);
+ ExprResult Res =
+ SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl);
+ if (Res.isInvalid())
+ return ExprError();
+ return SemaRef.BuildResolvedCallExpr(
+ Res.get(), FDecl, LParenLoc, Args, RParenLoc, ExecConfig,
+ /*IsExecConfig=*/false, (*Best)->IsADLCandidate);
}
case OR_No_Viable_Function: {
@@ -13615,10 +13842,13 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
// We emitted an error for the unavailable/deleted function call but keep
// the call in the AST.
FunctionDecl *FDecl = (*Best)->Function;
- Fn = SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl);
- return SemaRef.BuildResolvedCallExpr(Fn, FDecl, LParenLoc, Args, RParenLoc,
- ExecConfig, /*IsExecConfig=*/false,
- (*Best)->IsADLCandidate);
+ ExprResult Res =
+ SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl);
+ if (Res.isInvalid())
+ return ExprError();
+ return SemaRef.BuildResolvedCallExpr(
+ Res.get(), FDecl, LParenLoc, Args, RParenLoc, ExecConfig,
+ /*IsExecConfig=*/false, (*Best)->IsADLCandidate);
}
}
@@ -13692,6 +13922,83 @@ ExprResult Sema::CreateUnresolvedLookupExpr(CXXRecordDecl *NamingClass,
Fns.begin(), Fns.end());
}
+ExprResult Sema::BuildCXXMemberCallExpr(Expr *E, NamedDecl *FoundDecl,
+ CXXConversionDecl *Method,
+ bool HadMultipleCandidates) {
+ // Convert the expression to match the conversion function's implicit object
+ // parameter.
+ ExprResult Exp;
+ if (Method->isExplicitObjectMemberFunction())
+ Exp = InitializeExplicitObjectArgument(*this, E, Method);
+ else
+ Exp = PerformImplicitObjectArgumentInitialization(E, /*Qualifier=*/nullptr,
+ FoundDecl, Method);
+ if (Exp.isInvalid())
+ return true;
+
+ if (Method->getParent()->isLambda() &&
+ Method->getConversionType()->isBlockPointerType()) {
+ // This is a lambda conversion to block pointer; check if the argument
+ // was a LambdaExpr.
+ Expr *SubE = E;
+ auto *CE = dyn_cast<CastExpr>(SubE);
+ if (CE && CE->getCastKind() == CK_NoOp)
+ SubE = CE->getSubExpr();
+ SubE = SubE->IgnoreParens();
+ if (auto *BE = dyn_cast<CXXBindTemporaryExpr>(SubE))
+ SubE = BE->getSubExpr();
+ if (isa<LambdaExpr>(SubE)) {
+ // For the conversion to block pointer on a lambda expression, we
+ // construct a special BlockLiteral instead; this doesn't really make
+ // a difference in ARC, but outside of ARC the resulting block literal
+ // follows the normal lifetime rules for block literals instead of being
+ // autoreleased.
+ PushExpressionEvaluationContext(
+ ExpressionEvaluationContext::PotentiallyEvaluated);
+ ExprResult BlockExp = BuildBlockForLambdaConversion(
+ Exp.get()->getExprLoc(), Exp.get()->getExprLoc(), Method, Exp.get());
+ PopExpressionEvaluationContext();
+
+ // FIXME: This note should be produced by a CodeSynthesisContext.
+ if (BlockExp.isInvalid())
+ Diag(Exp.get()->getExprLoc(), diag::note_lambda_to_block_conv);
+ return BlockExp;
+ }
+ }
+ CallExpr *CE;
+ QualType ResultType = Method->getReturnType();
+ ExprValueKind VK = Expr::getValueKindForType(ResultType);
+ ResultType = ResultType.getNonLValueExprType(Context);
+ if (Method->isExplicitObjectMemberFunction()) {
+ ExprResult FnExpr =
+ CreateFunctionRefExpr(*this, Method, FoundDecl, Exp.get(),
+ HadMultipleCandidates, E->getBeginLoc());
+ if (FnExpr.isInvalid())
+ return ExprError();
+ Expr *ObjectParam = Exp.get();
+ CE = CallExpr::Create(Context, FnExpr.get(), MultiExprArg(&ObjectParam, 1),
+ ResultType, VK, Exp.get()->getEndLoc(),
+ CurFPFeatureOverrides());
+ } else {
+ MemberExpr *ME =
+ BuildMemberExpr(Exp.get(), /*IsArrow=*/false, SourceLocation(),
+ NestedNameSpecifierLoc(), SourceLocation(), Method,
+ DeclAccessPair::make(FoundDecl, FoundDecl->getAccess()),
+ HadMultipleCandidates, DeclarationNameInfo(),
+ Context.BoundMemberTy, VK_PRValue, OK_Ordinary);
+
+ CE = CXXMemberCallExpr::Create(Context, ME, /*Args=*/{}, ResultType, VK,
+ Exp.get()->getEndLoc(),
+ CurFPFeatureOverrides());
+ }
+
+ if (CheckFunctionCall(Method, CE,
+ Method->getType()->castAs<FunctionProtoType>()))
+ return ExprError();
+
+ return CheckForImmediateInvocation(CE, CE->getDirectCallee());
+}
+
/// Create a unary operation that may resolve to an overloaded
/// operator.
///
@@ -13786,14 +14093,17 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
// Convert the arguments.
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FnDecl)) {
- CheckMemberOperatorAccess(OpLoc, Args[0], nullptr, Best->FoundDecl);
+ CheckMemberOperatorAccess(OpLoc, Input, nullptr, Best->FoundDecl);
- ExprResult InputRes =
- PerformObjectArgumentInitialization(Input, /*Qualifier=*/nullptr,
- Best->FoundDecl, Method);
- if (InputRes.isInvalid())
+ ExprResult InputInit;
+ if (Method->isExplicitObjectMemberFunction())
+ InputInit = InitializeExplicitObjectArgument(*this, Input, Method);
+ else
+ InputInit = PerformImplicitObjectArgumentInitialization(
+ Input, /*Qualifier=*/nullptr, Best->FoundDecl, Method);
+ if (InputInit.isInvalid())
return ExprError();
- Base = Input = InputRes.get();
+ Base = Input = InputInit.get();
} else {
// Convert the arguments.
ExprResult InputInit
@@ -14127,13 +14437,16 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
if (auto *MD = dyn_cast<CXXMethodDecl>(FnDecl))
if (Op == OverloadedOperatorKind::OO_EqualEqual &&
!MD->isConst() &&
+ !MD->hasCXXExplicitFunctionObjectParameter() &&
Context.hasSameUnqualifiedType(
- MD->getThisObjectType(),
+ MD->getFunctionObjectParameterType(),
MD->getParamDecl(0)->getType().getNonReferenceType()) &&
- Context.hasSameUnqualifiedType(MD->getThisObjectType(),
- Args[0]->getType()) &&
- Context.hasSameUnqualifiedType(MD->getThisObjectType(),
- Args[1]->getType()))
+ Context.hasSameUnqualifiedType(
+ MD->getFunctionObjectParameterType(),
+ Args[0]->getType()) &&
+ Context.hasSameUnqualifiedType(
+ MD->getFunctionObjectParameterType(),
+ Args[1]->getType()))
Diag(FnDecl->getLocation(),
diag::note_ovl_ambiguous_eqeq_reversed_self_non_const);
} else {
@@ -14151,19 +14464,22 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
// Best->Access is only meaningful for class members.
CheckMemberOperatorAccess(OpLoc, Args[0], Args[1], Best->FoundDecl);
- ExprResult Arg1 =
- PerformCopyInitialization(
- InitializedEntity::InitializeParameter(Context,
- FnDecl->getParamDecl(0)),
+ ExprResult Arg0, Arg1;
+ unsigned ParamIdx = 0;
+ if (Method->isExplicitObjectMemberFunction()) {
+ Arg0 = InitializeExplicitObjectArgument(*this, Args[0], FnDecl);
+ ParamIdx = 1;
+ } else {
+ Arg0 = PerformImplicitObjectArgumentInitialization(
+ Args[0], /*Qualifier=*/nullptr, Best->FoundDecl, Method);
+ }
+ Arg1 = PerformCopyInitialization(
+ InitializedEntity::InitializeParameter(
+ Context, FnDecl->getParamDecl(ParamIdx)),
SourceLocation(), Args[1]);
- if (Arg1.isInvalid())
+ if (Arg0.isInvalid() || Arg1.isInvalid())
return ExprError();
- ExprResult Arg0 =
- PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/nullptr,
- Best->FoundDecl, Method);
- if (Arg0.isInvalid())
- return ExprError();
Base = Args[0] = Arg0.getAs<Expr>();
Args[1] = RHS = Arg1.getAs<Expr>();
} else {
@@ -14198,22 +14514,27 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
ExprValueKind VK = Expr::getValueKindForType(ResultTy);
ResultTy = ResultTy.getNonLValueExprType(Context);
- CXXOperatorCallExpr *TheCall = CXXOperatorCallExpr::Create(
+ CallExpr *TheCall;
+ ArrayRef<const Expr *> ArgsArray(Args, 2);
+ const Expr *ImplicitThis = nullptr;
+
+ // We always create a CXXOperatorCallExpr, even for explicit object
+ // members; CodeGen should take care not to emit the this pointer.
+ TheCall = CXXOperatorCallExpr::Create(
Context, ChosenOp, FnExpr.get(), Args, ResultTy, VK, OpLoc,
CurFPFeatureOverrides(), Best->IsADLCandidate);
- if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall,
- FnDecl))
- return ExprError();
-
- ArrayRef<const Expr *> ArgsArray(Args, 2);
- const Expr *ImplicitThis = nullptr;
- // Cut off the implicit 'this'.
- if (isa<CXXMethodDecl>(FnDecl)) {
+ if (const auto *Method = dyn_cast<CXXMethodDecl>(FnDecl);
+ Method && Method->isImplicitObjectMemberFunction()) {
+ // Cut off the implicit 'this'.
ImplicitThis = ArgsArray[0];
ArgsArray = ArgsArray.slice(1);
}
+ if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall,
+ FnDecl))
+ return ExprError();
+
// Check for a self move.
if (Op == OO_Equal)
DiagnoseSelfMove(Args[0], Args[1], OpLoc);
@@ -14221,7 +14542,7 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
if (ImplicitThis) {
QualType ThisType = Context.getPointerType(ImplicitThis->getType());
QualType ThisTypeFromDecl = Context.getPointerType(
- cast<CXXMethodDecl>(FnDecl)->getThisObjectType());
+ cast<CXXMethodDecl>(FnDecl)->getFunctionObjectParameterType());
CheckArgAlignment(OpLoc, FnDecl, "'this'", ThisType,
ThisTypeFromDecl);
@@ -14600,8 +14921,15 @@ ExprResult Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
SmallVector<Expr *, 2> MethodArgs;
// Handle 'this' parameter if the selected function is not static.
- if (Method->isInstance()) {
- ExprResult Arg0 = PerformObjectArgumentInitialization(
+ if (Method->isExplicitObjectMemberFunction()) {
+ ExprResult Res =
+ InitializeExplicitObjectArgument(*this, Args[0], Method);
+ if (Res.isInvalid())
+ return ExprError();
+ Args[0] = Res.get();
+ ArgExpr = Args;
+ } else if (Method->isInstance()) {
+ ExprResult Arg0 = PerformImplicitObjectArgumentInitialization(
Args[0], /*Qualifier=*/nullptr, Best->FoundDecl, Method);
if (Arg0.isInvalid())
return ExprError();
@@ -14803,6 +15131,7 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
MemberExpr *MemExpr;
CXXMethodDecl *Method = nullptr;
+ bool HadMultipleCandidates = false;
DeclAccessPair FoundDecl = DeclAccessPair::make(nullptr, AS_public);
NestedNameSpecifier *Qualifier = nullptr;
if (isa<MemberExpr>(NakedMemExpr)) {
@@ -14834,11 +15163,24 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
for (UnresolvedMemberExpr::decls_iterator I = UnresExpr->decls_begin(),
E = UnresExpr->decls_end(); I != E; ++I) {
+ QualType ExplicitObjectType = ObjectType;
+
NamedDecl *Func = *I;
CXXRecordDecl *ActingDC = cast<CXXRecordDecl>(Func->getDeclContext());
if (isa<UsingShadowDecl>(Func))
Func = cast<UsingShadowDecl>(Func)->getTargetDecl();
+ bool HasExplicitParameter = false;
+ if (const auto *M = dyn_cast<FunctionDecl>(Func);
+ M && M->hasCXXExplicitFunctionObjectParameter())
+ HasExplicitParameter = true;
+ else if (const auto *M = dyn_cast<FunctionTemplateDecl>(Func);
+ M &&
+ M->getTemplatedDecl()->hasCXXExplicitFunctionObjectParameter())
+ HasExplicitParameter = true;
+
+ if (HasExplicitParameter)
+ ExplicitObjectType = GetExplicitObjectType(*this, UnresExpr);
// Microsoft supports direct constructor calls.
if (getLangOpts().MicrosoftExt && isa<CXXConstructorDecl>(Func)) {
@@ -14851,17 +15193,20 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
if (TemplateArgs)
continue;
- AddMethodCandidate(Method, I.getPair(), ActingDC, ObjectType,
+ AddMethodCandidate(Method, I.getPair(), ActingDC, ExplicitObjectType,
ObjectClassification, Args, CandidateSet,
/*SuppressUserConversions=*/false);
} else {
- AddMethodTemplateCandidate(
- cast<FunctionTemplateDecl>(Func), I.getPair(), ActingDC,
- TemplateArgs, ObjectType, ObjectClassification, Args, CandidateSet,
- /*SuppressUserConversions=*/false);
+ AddMethodTemplateCandidate(cast<FunctionTemplateDecl>(Func),
+ I.getPair(), ActingDC, TemplateArgs,
+ ExplicitObjectType, ObjectClassification,
+ Args, CandidateSet,
+ /*SuppressUserConversions=*/false);
}
}
+ HadMultipleCandidates = (CandidateSet.size() > 1);
+
DeclarationName DeclName = UnresExpr->getMemberName();
UnbridgedCasts.restore();
@@ -14915,10 +15260,14 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
if (!Succeeded)
return BuildRecoveryExpr(chooseRecoveryType(CandidateSet, &Best));
- MemExprE = FixOverloadedFunctionReference(MemExprE, FoundDecl, Method);
+ ExprResult Res =
+ FixOverloadedFunctionReference(MemExprE, FoundDecl, Method);
+ if (Res.isInvalid())
+ return ExprError();
+ MemExprE = Res.get();
- // If overload resolution picked a static member, build a
- // non-member call based on that function.
+ // If overload resolution picked a static member
+ // build a non-member call based on that function.
if (Method->isStatic()) {
return BuildResolvedCallExpr(MemExprE, Method, LParenLoc, Args, RParenLoc,
ExecConfig, IsExecConfig);
@@ -14933,27 +15282,41 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
assert(Method && "Member call to something that isn't a method?");
const auto *Proto = Method->getType()->castAs<FunctionProtoType>();
- CXXMemberCallExpr *TheCall = CXXMemberCallExpr::Create(
- Context, MemExprE, Args, ResultType, VK, RParenLoc,
- CurFPFeatureOverrides(), Proto->getNumParams());
- // Check for a valid return type.
- if (CheckCallReturnType(Method->getReturnType(), MemExpr->getMemberLoc(),
- TheCall, Method))
- return BuildRecoveryExpr(ResultType);
+ CallExpr *TheCall = nullptr;
+ llvm::SmallVector<Expr *, 8> NewArgs;
+ if (Method->isExplicitObjectMemberFunction()) {
+ PrepareExplicitObjectArgument(*this, Method, MemExpr->getBase(), Args,
+ NewArgs);
+ // Build the actual expression node.
+ ExprResult FnExpr =
+ CreateFunctionRefExpr(*this, Method, FoundDecl, MemExpr,
+ HadMultipleCandidates, MemExpr->getExprLoc());
+ if (FnExpr.isInvalid())
+ return ExprError();
- // Convert the object argument (for a non-static member function call).
- // We only need to do this if there was actually an overload; otherwise
- // it was done at lookup.
- if (!Method->isStatic()) {
- ExprResult ObjectArg =
- PerformObjectArgumentInitialization(MemExpr->getBase(), Qualifier,
- FoundDecl, Method);
+ TheCall =
+ CallExpr::Create(Context, FnExpr.get(), Args, ResultType, VK, RParenLoc,
+ CurFPFeatureOverrides(), Proto->getNumParams());
+ } else {
+ // Convert the object argument (for a non-static member function call).
+ // We only need to do this if there was actually an overload; otherwise
+ // it was done at lookup.
+ ExprResult ObjectArg = PerformImplicitObjectArgumentInitialization(
+ MemExpr->getBase(), Qualifier, FoundDecl, Method);
if (ObjectArg.isInvalid())
return ExprError();
MemExpr->setBase(ObjectArg.get());
+ TheCall = CXXMemberCallExpr::Create(Context, MemExprE, Args, ResultType, VK,
+ RParenLoc, CurFPFeatureOverrides(),
+ Proto->getNumParams());
}
+ // Check for a valid return type.
+ if (CheckCallReturnType(Method->getReturnType(), MemExpr->getMemberLoc(),
+ TheCall, Method))
+ return BuildRecoveryExpr(ResultType);
+
// Convert the rest of the arguments
if (ConvertArgumentsForCall(TheCall, MemExpr, Method, Proto, Args,
RParenLoc))
@@ -14980,10 +15343,9 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
}
}
- if ((isa<CXXConstructorDecl>(CurContext) ||
- isa<CXXDestructorDecl>(CurContext)) &&
- TheCall->getMethodDecl()->isPure()) {
- const CXXMethodDecl *MD = TheCall->getMethodDecl();
+ if (isa<CXXConstructorDecl, CXXDestructorDecl>(CurContext) &&
+ TheCall->getDirectCallee()->isPure()) {
+ const FunctionDecl *MD = TheCall->getDirectCallee();
if (isa<CXXThisExpr>(MemExpr->getBase()->IgnoreParenCasts()) &&
MemExpr->performsVirtualDispatch(getLangOpts())) {
@@ -14999,8 +15361,7 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
}
}
- if (CXXDestructorDecl *DD =
- dyn_cast<CXXDestructorDecl>(TheCall->getMethodDecl())) {
+ if (auto *DD = dyn_cast<CXXDestructorDecl>(TheCall->getDirectCallee())) {
// a->A::f() doesn't go through the vtable, except in AppleKext mode.
bool CallCanBeVirtual = !MemExpr->hasQualifier() || getLangOpts().AppleKext;
CheckVirtualDtorCall(DD, MemExpr->getBeginLoc(), /*IsDelete=*/false,
@@ -15009,7 +15370,7 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
}
return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall),
- TheCall->getMethodDecl());
+ TheCall->getDirectCallee());
}
/// BuildCallToObjectOfClassType - Build a call to an object of class
@@ -15234,8 +15595,13 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
// Initialize the implicit object parameter if needed.
// Since C++23, this could also be a call to a static call operator
// which we emit as a regular CallExpr.
- if (Method->isInstance()) {
- ExprResult ObjRes = PerformObjectArgumentInitialization(
+ llvm::SmallVector<Expr *, 8> NewArgs;
+ if (Method->isExplicitObjectMemberFunction()) {
+ // FIXME: we should do that during the definition of the lambda when we can.
+ DiagnoseInvalidExplicitObjectParameterInLambda(Method);
+ PrepareExplicitObjectArgument(*this, Method, Obj, Args, NewArgs);
+ } else if (Method->isInstance()) {
+ ExprResult ObjRes = PerformImplicitObjectArgumentInitialization(
Object.get(), /*Qualifier=*/nullptr, Best->FoundDecl, Method);
if (ObjRes.isInvalid())
IsError = true;
@@ -15377,12 +15743,19 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
// Convert the object parameter.
CXXMethodDecl *Method = cast<CXXMethodDecl>(Best->Function);
- ExprResult BaseResult =
- PerformObjectArgumentInitialization(Base, /*Qualifier=*/nullptr,
- Best->FoundDecl, Method);
- if (BaseResult.isInvalid())
- return ExprError();
- Base = BaseResult.get();
+
+ if (Method->isExplicitObjectMemberFunction()) {
+ ExprResult R = InitializeExplicitObjectArgument(*this, Base, Method);
+ if (R.isInvalid())
+ return ExprError();
+ Base = R.get();
+ } else {
+ ExprResult BaseResult = PerformImplicitObjectArgumentInitialization(
+ Base, /*Qualifier=*/nullptr, Best->FoundDecl, Method);
+ if (BaseResult.isInvalid())
+ return ExprError();
+ Base = BaseResult.get();
+ }
// Build the operator call.
ExprResult FnExpr = CreateFunctionRefExpr(*this, Method, Best->FoundDecl,
@@ -15393,7 +15766,8 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
QualType ResultTy = Method->getReturnType();
ExprValueKind VK = Expr::getValueKindForType(ResultTy);
ResultTy = ResultTy.getNonLValueExprType(Context);
- CXXOperatorCallExpr *TheCall =
+
+ CallExpr *TheCall =
CXXOperatorCallExpr::Create(Context, OO_Arrow, FnExpr.get(), Base,
ResultTy, VK, OpLoc, CurFPFeatureOverrides());
@@ -15559,37 +15933,44 @@ Sema::BuildForRangeBeginEndCall(SourceLocation Loc,
/// perhaps a '&' around it). We have resolved the overloaded function
/// to the function declaration Fn, so patch up the expression E to
/// refer (possibly indirectly) to Fn. Returns the new expr.
-Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
- FunctionDecl *Fn) {
+ExprResult Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
+ FunctionDecl *Fn) {
if (ParenExpr *PE = dyn_cast<ParenExpr>(E)) {
- Expr *SubExpr = FixOverloadedFunctionReference(PE->getSubExpr(),
- Found, Fn);
- if (SubExpr == PE->getSubExpr())
+ ExprResult SubExpr =
+ FixOverloadedFunctionReference(PE->getSubExpr(), Found, Fn);
+ if (SubExpr.isInvalid())
+ return ExprError();
+ if (SubExpr.get() == PE->getSubExpr())
return PE;
- return new (Context) ParenExpr(PE->getLParen(), PE->getRParen(), SubExpr);
+ return new (Context)
+ ParenExpr(PE->getLParen(), PE->getRParen(), SubExpr.get());
}
if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
- Expr *SubExpr = FixOverloadedFunctionReference(ICE->getSubExpr(),
- Found, Fn);
+ ExprResult SubExpr =
+ FixOverloadedFunctionReference(ICE->getSubExpr(), Found, Fn);
+ if (SubExpr.isInvalid())
+ return ExprError();
assert(Context.hasSameType(ICE->getSubExpr()->getType(),
- SubExpr->getType()) &&
+ SubExpr.get()->getType()) &&
"Implicit cast type cannot be determined from overload");
assert(ICE->path_empty() && "fixing up hierarchy conversion?");
- if (SubExpr == ICE->getSubExpr())
+ if (SubExpr.get() == ICE->getSubExpr())
return ICE;
return ImplicitCastExpr::Create(Context, ICE->getType(), ICE->getCastKind(),
- SubExpr, nullptr, ICE->getValueKind(),
+ SubExpr.get(), nullptr, ICE->getValueKind(),
CurFPFeatureOverrides());
}
if (auto *GSE = dyn_cast<GenericSelectionExpr>(E)) {
if (!GSE->isResultDependent()) {
- Expr *SubExpr =
+ ExprResult SubExpr =
FixOverloadedFunctionReference(GSE->getResultExpr(), Found, Fn);
- if (SubExpr == GSE->getResultExpr())
+ if (SubExpr.isInvalid())
+ return ExprError();
+ if (SubExpr.get() == GSE->getResultExpr())
return GSE;
// Replace the resulting type information before rebuilding the generic
@@ -15597,7 +15978,7 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
ArrayRef<Expr *> A = GSE->getAssocExprs();
SmallVector<Expr *, 4> AssocExprs(A.begin(), A.end());
unsigned ResultIdx = GSE->getResultIndex();
- AssocExprs[ResultIdx] = SubExpr;
+ AssocExprs[ResultIdx] = SubExpr.get();
if (GSE->isExprPredicate())
return GenericSelectionExpr::Create(
@@ -15627,15 +16008,21 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
// Fix the subexpression, which really has to be an
// UnresolvedLookupExpr holding an overloaded member function
// or template.
- Expr *SubExpr = FixOverloadedFunctionReference(UnOp->getSubExpr(),
- Found, Fn);
- if (SubExpr == UnOp->getSubExpr())
+ ExprResult SubExpr =
+ FixOverloadedFunctionReference(UnOp->getSubExpr(), Found, Fn);
+ if (SubExpr.isInvalid())
+ return ExprError();
+ if (SubExpr.get() == UnOp->getSubExpr())
return UnOp;
- assert(isa<DeclRefExpr>(SubExpr)
- && "fixed to something other than a decl ref");
- assert(cast<DeclRefExpr>(SubExpr)->getQualifier()
- && "fixed to a member ref with no nested name qualifier");
+ if (CheckUseOfCXXMethodAsAddressOfOperand(UnOp->getBeginLoc(),
+ SubExpr.get(), Method))
+ return ExprError();
+
+ assert(isa<DeclRefExpr>(SubExpr.get()) &&
+ "fixed to something other than a decl ref");
+ assert(cast<DeclRefExpr>(SubExpr.get())->getQualifier() &&
+ "fixed to a member ref with no nested name qualifier");
// We have taken the address of a pointer to member
// function. Perform the computation here so that we get the
@@ -15648,19 +16035,21 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, DeclAccessPair Found,
if (Context.getTargetInfo().getCXXABI().isMicrosoft())
(void)isCompleteType(UnOp->getOperatorLoc(), MemPtrType);
- return UnaryOperator::Create(
- Context, SubExpr, UO_AddrOf, MemPtrType, VK_PRValue, OK_Ordinary,
- UnOp->getOperatorLoc(), false, CurFPFeatureOverrides());
+ return UnaryOperator::Create(Context, SubExpr.get(), UO_AddrOf,
+ MemPtrType, VK_PRValue, OK_Ordinary,
+ UnOp->getOperatorLoc(), false,
+ CurFPFeatureOverrides());
}
}
- Expr *SubExpr = FixOverloadedFunctionReference(UnOp->getSubExpr(),
- Found, Fn);
- if (SubExpr == UnOp->getSubExpr())
+ ExprResult SubExpr =
+ FixOverloadedFunctionReference(UnOp->getSubExpr(), Found, Fn);
+ if (SubExpr.isInvalid())
+ return ExprError();
+ if (SubExpr.get() == UnOp->getSubExpr())
return UnOp;
- // FIXME: This can't currently fail, but in principle it could.
- return CreateBuiltinUnaryOp(UnOp->getOperatorLoc(), UO_AddrOf, SubExpr)
- .get();
+ return CreateBuiltinUnaryOp(UnOp->getOperatorLoc(), UO_AddrOf,
+ SubExpr.get());
}
if (UnresolvedLookupExpr *ULE = dyn_cast<UnresolvedLookupExpr>(E)) {
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 10adfbc..3332269 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -689,7 +689,7 @@ bool Sema::checkMustTailAttr(const Stmt *St, const Attr &MTA) {
if (CMD->isStatic())
Type.MemberType = FuncType::ft_static_member;
else {
- Type.This = CMD->getThisObjectType();
+ Type.This = CMD->getFunctionObjectParameterType();
Type.MemberType = FuncType::ft_non_static_member;
}
Type.Func = CMD->getType()->castAs<FunctionProtoType>();
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 994d89e..080f005 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -753,7 +753,8 @@ Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS,
IsEnum = isa_and_nonnull<EnumType>(NNS->getAsType());
if (!MightBeCxx11UnevalField && !isAddressOfOperand && !IsEnum &&
- isa<CXXMethodDecl>(DC) && cast<CXXMethodDecl>(DC)->isInstance()) {
+ isa<CXXMethodDecl>(DC) &&
+ cast<CXXMethodDecl>(DC)->isImplicitObjectMemberFunction()) {
QualType ThisType = cast<CXXMethodDecl>(DC)->getThisType().getNonReferenceType();
// Since the 'this' expression is synthesized, we don't need to
@@ -7079,7 +7080,8 @@ CheckTemplateArgumentPointerToMember(Sema &S, NonTypeTemplateParmDecl *Param,
isa<CXXMethodDecl>(DRE->getDecl())) {
assert((isa<FieldDecl>(DRE->getDecl()) ||
isa<IndirectFieldDecl>(DRE->getDecl()) ||
- !cast<CXXMethodDecl>(DRE->getDecl())->isStatic()) &&
+ cast<CXXMethodDecl>(DRE->getDecl())
+ ->isImplicitObjectMemberFunction()) &&
"Only non-static member pointers can make it here");
// Okay: this is the address of a non-static member, and therefore
@@ -7631,7 +7633,10 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
if (DiagnoseUseOfDecl(Fn, Arg->getBeginLoc()))
return ExprError();
- Arg = FixOverloadedFunctionReference(Arg, FoundResult, Fn);
+ ExprResult Res = FixOverloadedFunctionReference(Arg, FoundResult, Fn);
+ if (Res.isInvalid())
+ return ExprError();
+ Arg = Res.get();
ArgType = Arg->getType();
} else
return ExprError();
@@ -7682,8 +7687,10 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
FoundResult)) {
if (DiagnoseUseOfDecl(Fn, Arg->getBeginLoc()))
return ExprError();
-
- Arg = FixOverloadedFunctionReference(Arg, FoundResult, Fn);
+ ExprResult Res = FixOverloadedFunctionReference(Arg, FoundResult, Fn);
+ if (Res.isInvalid())
+ return ExprError();
+ Arg = Res.get();
ArgType = Arg->getType();
} else
return ExprError();
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 31ea7be..69b857d 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -1905,11 +1905,11 @@ static Sema::TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
QualType PPT = MPP->getPointeeType();
if (PPT->isFunctionType())
- S.adjustMemberFunctionCC(PPT, /*IsStatic=*/true,
+ S.adjustMemberFunctionCC(PPT, /*HasThisPointer=*/false,
/*IsCtorOrDtor=*/false, Info.getLocation());
QualType APT = MPA->getPointeeType();
if (APT->isFunctionType())
- S.adjustMemberFunctionCC(APT, /*IsStatic=*/true,
+ S.adjustMemberFunctionCC(APT, /*HasThisPointer=*/false,
/*IsCtorOrDtor=*/false, Info.getLocation());
unsigned SubTDF = TDF & TDF_IgnoreQualifiers;
@@ -3685,7 +3685,9 @@ Sema::TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
OriginalCallArg OriginalArg = (*OriginalCallArgs)[I];
auto ParamIdx = OriginalArg.ArgIdx;
- if (ParamIdx >= Specialization->getNumParams())
+ unsigned ExplicitOffset =
+ Specialization->hasCXXExplicitFunctionObjectParameter() ? 1 : 0;
+ if (ParamIdx >= Specialization->getNumParams() - ExplicitOffset)
// FIXME: This presumably means a pack ended up smaller than we
// expected while deducing. Should this not result in deduction
// failure? Can it even happen?
@@ -3695,7 +3697,8 @@ Sema::TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
if (!OriginalArg.DecomposedParam) {
// P is one of the function parameters, just look up its substituted
// type.
- DeducedA = Specialization->getParamDecl(ParamIdx)->getType();
+ DeducedA =
+ Specialization->getParamDecl(ParamIdx + ExplicitOffset)->getType();
} else {
// P is a decomposed element of a parameter corresponding to a
// braced-init-list argument. Substitute back into P to find the
@@ -3745,7 +3748,7 @@ static QualType GetTypeOfFunction(Sema &S, const OverloadExpr::FindResult &R,
return {};
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Fn))
- if (Method->isInstance()) {
+ if (Method->isImplicitObjectMemberFunction()) {
// An instance method that's referenced in a form that doesn't
// look like a member pointer is just invalid.
if (!R.HasFormOfMemberPointer)
@@ -3874,7 +3877,8 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams,
/// overloaded function set that could not be resolved.
static bool AdjustFunctionParmAndArgTypesForDeduction(
Sema &S, TemplateParameterList *TemplateParams, unsigned FirstInnerIndex,
- QualType &ParamType, QualType &ArgType, Expr *Arg, unsigned &TDF,
+ QualType &ParamType, QualType &ArgType,
+ Expr::Classification ArgClassification, Expr *Arg, unsigned &TDF,
TemplateSpecCandidateSet *FailedTSC = nullptr) {
// C++0x [temp.deduct.call]p3:
// If P is a cv-qualified type, the top level cv-qualifiers of P's type
@@ -3892,6 +3896,7 @@ static bool AdjustFunctionParmAndArgTypesForDeduction(
// but there are sometimes special circumstances. Typically
// involving a template-id-expr.
if (ArgType == S.Context.OverloadTy) {
+ assert(Arg && "expected a non-null arg expression");
ArgType = ResolveOverloadForDeduction(S, TemplateParams, Arg, ParamType,
ParamRefType != nullptr, FailedTSC);
if (ArgType.isNull())
@@ -3900,14 +3905,16 @@ static bool AdjustFunctionParmAndArgTypesForDeduction(
if (ParamRefType) {
// If the argument has incomplete array type, try to complete its type.
- if (ArgType->isIncompleteArrayType())
+ if (ArgType->isIncompleteArrayType()) {
+ assert(Arg && "expected a non-null arg expression");
ArgType = S.getCompletedType(Arg);
+ }
// C++1z [temp.deduct.call]p3:
// If P is a forwarding reference and the argument is an lvalue, the type
// "lvalue reference to A" is used in place of A for type deduction.
if (isForwardingReference(QualType(ParamRefType, 0), FirstInnerIndex) &&
- Arg->isLValue()) {
+ ArgClassification.isLValue()) {
if (S.getLangOpts().OpenCL && !ArgType.hasAddressSpace())
ArgType = S.Context.getAddrSpaceQualType(
ArgType, S.Context.getDefaultOpenCLPointeeAddrSpace());
@@ -3968,7 +3975,9 @@ hasDeducibleTemplateParameters(Sema &S, FunctionTemplateDecl *FunctionTemplate,
static Sema::TemplateDeductionResult DeduceTemplateArgumentsFromCallArgument(
Sema &S, TemplateParameterList *TemplateParams, unsigned FirstInnerIndex,
- QualType ParamType, Expr *Arg, TemplateDeductionInfo &Info,
+ QualType ParamType, QualType ArgType,
+ Expr::Classification ArgClassification, Expr *Arg,
+ TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
SmallVectorImpl<Sema::OriginalCallArg> &OriginalCallArgs,
bool DecomposedParam, unsigned ArgIdx, unsigned TDF,
@@ -4013,8 +4022,9 @@ static Sema::TemplateDeductionResult DeduceFromInitializerList(
if (ElTy->isDependentType()) {
for (Expr *E : ILE->inits()) {
if (auto Result = DeduceTemplateArgumentsFromCallArgument(
- S, TemplateParams, 0, ElTy, E, Info, Deduced, OriginalCallArgs, true,
- ArgIdx, TDF))
+ S, TemplateParams, 0, ElTy, E->getType(),
+ E->Classify(S.getASTContext()), E, Info, Deduced,
+ OriginalCallArgs, true, ArgIdx, TDF))
return Result;
}
}
@@ -4045,23 +4055,25 @@ static Sema::TemplateDeductionResult DeduceFromInitializerList(
/// single parameter / argument pair.
static Sema::TemplateDeductionResult DeduceTemplateArgumentsFromCallArgument(
Sema &S, TemplateParameterList *TemplateParams, unsigned FirstInnerIndex,
- QualType ParamType, Expr *Arg, TemplateDeductionInfo &Info,
+ QualType ParamType, QualType ArgType,
+ Expr::Classification ArgClassification, Expr *Arg,
+ TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced,
SmallVectorImpl<Sema::OriginalCallArg> &OriginalCallArgs,
bool DecomposedParam, unsigned ArgIdx, unsigned TDF,
TemplateSpecCandidateSet *FailedTSC) {
- QualType ArgType = Arg->getType();
+
QualType OrigParamType = ParamType;
// If P is a reference type [...]
// If P is a cv-qualified type [...]
- if (AdjustFunctionParmAndArgTypesForDeduction(S, TemplateParams,
- FirstInnerIndex, ParamType,
- ArgType, Arg, TDF, FailedTSC))
+ if (AdjustFunctionParmAndArgTypesForDeduction(
+ S, TemplateParams, FirstInnerIndex, ParamType, ArgType,
+ ArgClassification, Arg, TDF, FailedTSC))
return Sema::TDK_Success;
// If [...] the argument is a non-empty initializer list [...]
- if (InitListExpr *ILE = dyn_cast<InitListExpr>(Arg))
+ if (InitListExpr *ILE = dyn_cast_if_present<InitListExpr>(Arg))
return DeduceFromInitializerList(S, TemplateParams, ParamType, ILE, Info,
Deduced, OriginalCallArgs, ArgIdx, TDF);
@@ -4070,8 +4082,9 @@ static Sema::TemplateDeductionResult DeduceTemplateArgumentsFromCallArgument(
//
// Keep track of the argument type and corresponding parameter index,
// so we can check for compatibility between the deduced A and A.
- OriginalCallArgs.push_back(
- Sema::OriginalCallArg(OrigParamType, DecomposedParam, ArgIdx, ArgType));
+ if (Arg)
+ OriginalCallArgs.push_back(
+ Sema::OriginalCallArg(OrigParamType, DecomposedParam, ArgIdx, ArgType));
return DeduceTemplateArgumentsByTypeMatch(S, TemplateParams, ParamType,
ArgType, Info, Deduced, TDF);
}
@@ -4106,12 +4119,19 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
FunctionDecl *&Specialization, TemplateDeductionInfo &Info,
bool PartialOverloading, bool AggregateDeductionCandidate,
+ QualType ObjectType, Expr::Classification ObjectClassification,
llvm::function_ref<bool(ArrayRef<QualType>)> CheckNonDependent) {
if (FunctionTemplate->isInvalidDecl())
return TDK_Invalid;
FunctionDecl *Function = FunctionTemplate->getTemplatedDecl();
unsigned NumParams = Function->getNumParams();
+ bool HasExplicitObject = false;
+ int ExplicitObjectOffset = 0;
+ if (Function->hasCXXExplicitFunctionObjectParameter()) {
+ HasExplicitObject = true;
+ ExplicitObjectOffset = 1;
+ }
unsigned FirstInnerIndex = getFirstInnerIndex(FunctionTemplate);
@@ -4119,9 +4139,11 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
// Template argument deduction is done by comparing each function template
// parameter type (call it P) with the type of the corresponding argument
// of the call (call it A) as described below.
- if (Args.size() < Function->getMinRequiredArguments() && !PartialOverloading)
+ if (Args.size() < Function->getMinRequiredExplicitArguments() &&
+ !PartialOverloading)
return TDK_TooFewArguments;
- else if (TooManyArguments(NumParams, Args.size(), PartialOverloading)) {
+ else if (TooManyArguments(NumParams, Args.size() + ExplicitObjectOffset,
+ PartialOverloading)) {
const auto *Proto = Function->getType()->castAs<FunctionProtoType>();
if (Proto->isTemplateVariadic())
/* Do nothing */;
@@ -4157,7 +4179,8 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
SmallVector<OriginalCallArg, 8> OriginalCallArgs;
// Deduce an argument of type ParamType from an expression with index ArgIdx.
- auto DeduceCallArgument = [&](QualType ParamType, unsigned ArgIdx) {
+ auto DeduceCallArgument = [&](QualType ParamType, unsigned ArgIdx,
+ bool ExplicitObjetArgument) {
// C++ [demp.deduct.call]p1: (DR1391)
// Template argument deduction is done by comparing each function template
// parameter that contains template-parameters that participate in
@@ -4165,10 +4188,21 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
if (!hasDeducibleTemplateParameters(*this, FunctionTemplate, ParamType))
return Sema::TDK_Success;
+ if (ExplicitObjetArgument) {
+ // ... with the type of the corresponding argument
+ return DeduceTemplateArgumentsFromCallArgument(
+ *this, TemplateParams, FirstInnerIndex, ParamType, ObjectType,
+ ObjectClassification,
+ /*Arg=*/nullptr, Info, Deduced, OriginalCallArgs,
+ /*Decomposed*/ false, ArgIdx, /*TDF*/ 0);
+ }
+
// ... with the type of the corresponding argument
return DeduceTemplateArgumentsFromCallArgument(
- *this, TemplateParams, FirstInnerIndex, ParamType, Args[ArgIdx], Info, Deduced,
- OriginalCallArgs, /*Decomposed*/false, ArgIdx, /*TDF*/ 0);
+ *this, TemplateParams, FirstInnerIndex, ParamType,
+ Args[ArgIdx]->getType(), Args[ArgIdx]->Classify(getASTContext()),
+ Args[ArgIdx], Info, Deduced, OriginalCallArgs, /*Decomposed*/ false,
+ ArgIdx, /*TDF*/ 0);
};
// Deduce template arguments from the function parameters.
@@ -4182,11 +4216,20 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
dyn_cast<PackExpansionType>(ParamType);
if (!ParamExpansion) {
// Simple case: matching a function parameter to a function argument.
- if (ArgIdx >= Args.size())
+ if (ArgIdx >= Args.size() && !(HasExplicitObject && ParamIdx == 0))
break;
ParamTypesForArgChecking.push_back(ParamType);
- if (auto Result = DeduceCallArgument(ParamType, ArgIdx++))
+
+ if (ParamIdx == 0 && HasExplicitObject) {
+ if (auto Result = DeduceCallArgument(ParamType, 0,
+ /*ExplicitObjetArgument=*/true))
+ return Result;
+ continue;
+ }
+
+ if (auto Result = DeduceCallArgument(ParamType, ArgIdx++,
+ /*ExplicitObjetArgument=*/false))
return Result;
continue;
@@ -4219,7 +4262,8 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
for (; ArgIdx < Args.size() && PackScope.hasNextElement();
PackScope.nextPackElement(), ++ArgIdx) {
ParamTypesForArgChecking.push_back(ParamPattern);
- if (auto Result = DeduceCallArgument(ParamPattern, ArgIdx))
+ if (auto Result = DeduceCallArgument(ParamPattern, ArgIdx,
+ /*ExplicitObjetArgument=*/false))
return Result;
}
} else {
@@ -4459,11 +4503,10 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
/// Deduce template arguments for a templated conversion
/// function (C++ [temp.deduct.conv]) and, if successful, produce a
/// conversion function template specialization.
-Sema::TemplateDeductionResult
-Sema::DeduceTemplateArguments(FunctionTemplateDecl *ConversionTemplate,
- QualType ToType,
- CXXConversionDecl *&Specialization,
- TemplateDeductionInfo &Info) {
+Sema::TemplateDeductionResult Sema::DeduceTemplateArguments(
+ FunctionTemplateDecl *ConversionTemplate, QualType ObjectType,
+ Expr::Classification ObjectClassification, QualType ToType,
+ CXXConversionDecl *&Specialization, TemplateDeductionInfo &Info) {
if (ConversionTemplate->isInvalidDecl())
return TDK_Invalid;
@@ -4558,6 +4601,19 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *ConversionTemplate,
if ((P->isPointerType() && A->isPointerType()) ||
(P->isMemberPointerType() && A->isMemberPointerType()))
TDF |= TDF_IgnoreQualifiers;
+
+ SmallVector<Sema::OriginalCallArg, 1> OriginalCallArgs;
+ if (ConversionGeneric->isExplicitObjectMemberFunction()) {
+ QualType ParamType = ConversionGeneric->getParamDecl(0)->getType();
+ if (TemplateDeductionResult Result =
+ DeduceTemplateArgumentsFromCallArgument(
+ *this, TemplateParams, getFirstInnerIndex(ConversionTemplate),
+ ParamType, ObjectType, ObjectClassification,
+ /*Arg=*/nullptr, Info, Deduced, OriginalCallArgs,
+ /*Decomposed*/ false, 0, /*TDF*/ 0))
+ return Result;
+ }
+
if (TemplateDeductionResult Result
= DeduceTemplateArgumentsByTypeMatch(*this, TemplateParams,
P, A, Info, Deduced, TDF))
@@ -4570,7 +4626,8 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *ConversionTemplate,
TemplateDeductionResult Result;
runWithSufficientStackSpace(Info.getLocation(), [&] {
Result = FinishTemplateArgumentDeduction(ConversionTemplate, Deduced, 0,
- ConversionSpecialized, Info);
+ ConversionSpecialized, Info,
+ &OriginalCallArgs);
});
Specialization = cast_or_null<CXXConversionDecl>(ConversionSpecialized);
return Result;
@@ -4846,7 +4903,8 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
if (isa<DesignatedInitExpr>(Init))
return TDK_Invalid;
if (auto TDK = DeduceTemplateArgumentsFromCallArgument(
- *this, TemplateParamsSt.get(), 0, TemplArg, Init, Info, Deduced,
+ *this, TemplateParamsSt.get(), 0, TemplArg, Init->getType(),
+ Init->Classify(getASTContext()), Init, Info, Deduced,
OriginalCallArgs, /*Decomposed=*/true,
/*ArgIdx=*/0, /*TDF=*/0)) {
if (TDK == TDK_Inconsistent) {
@@ -4872,7 +4930,8 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
assert(!FuncParam.isNull() &&
"substituting template parameter for 'auto' failed");
if (auto TDK = DeduceTemplateArgumentsFromCallArgument(
- *this, TemplateParamsSt.get(), 0, FuncParam, Init, Info, Deduced,
+ *this, TemplateParamsSt.get(), 0, FuncParam, Init->getType(),
+ Init->Classify(getASTContext()), Init, Info, Deduced,
OriginalCallArgs, /*Decomposed=*/false, /*ArgIdx=*/0, /*TDF=*/0,
FailedTSC))
return DeductionFailed(TDK);
@@ -5080,6 +5139,8 @@ AddImplicitObjectParameterType(ASTContext &Context,
//
// The standard doesn't say explicitly, but we pick the appropriate kind of
// reference type based on [over.match.funcs]p4.
+ assert(Method && Method->isImplicitObjectMemberFunction() &&
+ "expected an implicit objet function");
QualType ArgTy = Context.getTypeDeclType(Method->getParent());
ArgTy = Context.getQualifiedType(ArgTy, Method->getMethodQualifiers());
if (Method->getRefQualifier() == RQ_RValue)
@@ -5141,14 +5202,17 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
unsigned NumComparedArguments = NumCallArguments1;
- if (!Method2 && Method1 && !Method1->isStatic()) {
+ if (!Method2 && Method1 && Method1->isImplicitObjectMemberFunction()) {
// Compare 'this' from Method1 against first parameter from Method2.
AddImplicitObjectParameterType(S.Context, Method1, Args1);
++NumComparedArguments;
- } else if (!Method1 && Method2 && !Method2->isStatic()) {
+ } else if (!Method1 && Method2 &&
+ Method2->isImplicitObjectMemberFunction()) {
// Compare 'this' from Method2 against first parameter from Method1.
AddImplicitObjectParameterType(S.Context, Method2, Args2);
- } else if (Method1 && Method2 && Reversed) {
+ } else if (Method1 && Method2 && Reversed &&
+ Method1->isImplicitObjectMemberFunction() &&
+ Method2->isImplicitObjectMemberFunction()) {
// Compare 'this' from Method1 against second parameter from Method2
// and 'this' from Method2 against second parameter from Method1.
AddImplicitObjectParameterType(S.Context, Method1, Args1);
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 00a3669..8a74a5c 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2908,6 +2908,8 @@ ParmVarDecl *Sema::SubstParmVarDecl(
NewParm->setUninstantiatedDefaultArg(Arg);
}
+ NewParm->setExplicitObjectParameterLoc(
+ OldParm->getExplicitObjectParamThisLoc());
NewParm->setHasInheritedDefaultArg(OldParm->hasInheritedDefaultArg());
if (OldParm->isParameterPack() && !NewParm->isParameterPack()) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 1aa4036..e4974b1 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4434,7 +4434,7 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D,
Qualifiers ThisTypeQuals;
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
ThisContext = cast<CXXRecordDecl>(Owner);
- ThisTypeQuals = Method->getMethodQualifiers();
+ ThisTypeQuals = Method->getFunctionObjectParameterType().getQualifiers();
}
TypeSourceInfo *NewTInfo = SemaRef.SubstFunctionDeclType(
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index d13a556..8f73268 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -16,6 +16,7 @@
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/ASTStructuralEquivalence.h"
#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
@@ -40,6 +41,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/IR/DerivedTypes.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include <bitset>
#include <optional>
@@ -3122,7 +3124,7 @@ QualType Sema::BuildMemberPointerType(QualType T, QualType Class,
(Entity.getNameKind() == DeclarationName::CXXConstructorName) ||
(Entity.getNameKind() == DeclarationName::CXXDestructorName);
if (T->isFunctionType())
- adjustMemberFunctionCC(T, /*IsStatic=*/false, IsCtorOrDtor, Loc);
+ adjustMemberFunctionCC(T, /*HasThisPointer=*/true, IsCtorOrDtor, Loc);
return Context.getMemberPointerType(T, Class.getTypePtr());
}
@@ -5801,7 +5803,12 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
//
// Core issue 547 also allows cv-qualifiers on function types that are
// top-level template type arguments.
- enum { NonMember, Member, DeductionGuide } Kind = NonMember;
+ enum {
+ NonMember,
+ Member,
+ ExplicitObjectMember,
+ DeductionGuide
+ } Kind = NonMember;
if (D.getName().getKind() == UnqualifiedIdKind::IK_DeductionGuideName)
Kind = DeductionGuide;
else if (!D.getCXXScopeSpec().isSet()) {
@@ -5815,6 +5822,18 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
Kind = Member;
}
+ if (Kind == Member) {
+ unsigned I;
+ if (D.isFunctionDeclarator(I)) {
+ const DeclaratorChunk &Chunk = D.getTypeObject(I);
+ if (Chunk.Fun.NumParams) {
+ auto *P = dyn_cast_or_null<ParmVarDecl>(Chunk.Fun.Params->Param);
+ if (P && P->isExplicitObjectParameter())
+ Kind = ExplicitObjectMember;
+ }
+ }
+ }
+
// C++11 [dcl.fct]p6 (w/DR1417):
// An attempt to specify a function type with a cv-qualifier-seq or a
// ref-qualifier (including by typedef-name) is ill-formed unless it is:
@@ -5832,7 +5851,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
//
// ... for instance.
if (IsQualifiedFunction &&
- !(Kind == Member &&
+ !(Kind == Member && !D.isExplicitObjectMemberFunction() &&
D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static) &&
!IsTypedefName && D.getContext() != DeclaratorContext::TemplateArg &&
D.getContext() != DeclaratorContext::TemplateTypeArg) {
@@ -8123,14 +8142,15 @@ bool Sema::hasExplicitCallingConv(QualType T) {
return false;
}
-void Sema::adjustMemberFunctionCC(QualType &T, bool IsStatic, bool IsCtorOrDtor,
- SourceLocation Loc) {
+void Sema::adjustMemberFunctionCC(QualType &T, bool HasThisPointer,
+ bool IsCtorOrDtor, SourceLocation Loc) {
FunctionTypeUnwrapper Unwrapped(*this, T);
const FunctionType *FT = Unwrapped.get();
bool IsVariadic = (isa<FunctionProtoType>(FT) &&
cast<FunctionProtoType>(FT)->isVariadic());
CallingConv CurCC = FT->getCallConv();
- CallingConv ToCC = Context.getDefaultCallingConvention(IsVariadic, !IsStatic);
+ CallingConv ToCC =
+ Context.getDefaultCallingConvention(IsVariadic, HasThisPointer);
if (CurCC == ToCC)
return;
@@ -8150,7 +8170,7 @@ void Sema::adjustMemberFunctionCC(QualType &T, bool IsStatic, bool IsCtorOrDtor,
// we should adjust a __cdecl type to __thiscall for instance methods, and a
// __thiscall type to __cdecl for static methods.
CallingConv DefaultCC =
- Context.getDefaultCallingConvention(IsVariadic, IsStatic);
+ Context.getDefaultCallingConvention(IsVariadic, !HasThisPointer);
if (CurCC != DefaultCC)
return;
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 88cb54a..d553b3c6 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1707,6 +1707,7 @@ void ASTDeclReader::VisitParmVarDecl(ParmVarDecl *PD) {
PD->ParmVarDeclBits.HasInheritedDefaultArg = Record.readInt();
if (Record.readInt()) // hasUninstantiatedDefaultArg.
PD->setUninstantiatedDefaultArg(Record.readExpr());
+ PD->ExplicitObjectParameterIntroducerLoc = Record.readSourceLocation();
// FIXME: If this is a redeclaration of a function from another module, handle
// inheritance of default arguments.
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 8edb04f..1bdc3fa 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -596,6 +596,7 @@ void ASTStmtReader::VisitDeclRefExpr(DeclRefExpr *E) {
E->DeclRefExprBits.RefersToEnclosingVariableOrCapture = Record.readInt();
E->DeclRefExprBits.NonOdrUseReason = Record.readInt();
E->DeclRefExprBits.IsImmediateEscalating = Record.readInt();
+ E->DeclRefExprBits.CapturedByCopyInLambdaWithExplicitObjectParameter = false;
unsigned NumTemplateArgs = 0;
if (E->hasTemplateKWAndArgsInfo())
NumTemplateArgs = Record.readInt();
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 4426f5c..91c8ed9 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1162,28 +1162,21 @@ void ASTDeclWriter::VisitParmVarDecl(ParmVarDecl *D) {
Record.push_back(D->hasUninstantiatedDefaultArg());
if (D->hasUninstantiatedDefaultArg())
Record.AddStmt(D->getUninstantiatedDefaultArg());
+ Record.AddSourceLocation(D->getExplicitObjectParamThisLoc());
Code = serialization::DECL_PARM_VAR;
// If the assumptions about the DECL_PARM_VAR abbrev are true, use it. Here
// we dynamically check for the properties that we optimize for, but don't
// know are true of all PARM_VAR_DECLs.
- if (D->getDeclContext() == D->getLexicalDeclContext() &&
- !D->hasAttrs() &&
- !D->hasExtInfo() &&
- !D->isImplicit() &&
- !D->isUsed(false) &&
- !D->isInvalidDecl() &&
- !D->isReferenced() &&
- D->getAccess() == AS_none &&
- !D->isModulePrivate() &&
- D->getStorageClass() == 0 &&
+ if (D->getDeclContext() == D->getLexicalDeclContext() && !D->hasAttrs() &&
+ !D->hasExtInfo() && !D->isImplicit() && !D->isUsed(false) &&
+ !D->isInvalidDecl() && !D->isReferenced() && D->getAccess() == AS_none &&
+ !D->isModulePrivate() && D->getStorageClass() == 0 &&
D->getInitStyle() == VarDecl::CInit && // Can params have anything else?
- D->getFunctionScopeDepth() == 0 &&
- D->getObjCDeclQualifier() == 0 &&
- !D->isKNRPromoted() &&
- !D->hasInheritedDefaultArg() &&
- D->getInit() == nullptr &&
- !D->hasUninstantiatedDefaultArg()) // No default expr.
+ D->getFunctionScopeDepth() == 0 && D->getObjCDeclQualifier() == 0 &&
+ !D->isKNRPromoted() && !D->isExplicitObjectParameter() &&
+ !D->hasInheritedDefaultArg() && D->getInit() == nullptr &&
+ !D->hasUninstantiatedDefaultArg()) // No default expr.
AbbrevToUse = Writer.getDeclParmVarAbbrev();
// Check things we know are true of *every* PARM_VAR_DECL, which is more than
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index d7c5bd1..451ee91 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -299,7 +299,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
}
if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
- if (!MD->isStatic()) {
+ if (MD->isImplicitObjectMemberFunction()) {
// Precondition: 'this' is always non-null upon entry to the
// top-level function. This is our starting assumption for
// analyzing an "open" program.
@@ -2114,7 +2114,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
// valid region.
const Decl *Callee = OCE->getCalleeDecl();
if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Callee)) {
- if (MD->isInstance()) {
+ if (MD->isImplicitObjectMemberFunction()) {
ProgramStateRef State = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
ProgramStateRef NewState =
@@ -3357,7 +3357,7 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
// Handle C++ method calls.
if (const auto *MD = dyn_cast<CXXMethodDecl>(Member)) {
- if (MD->isInstance())
+ if (MD->isImplicitObjectMemberFunction())
state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr);
SVal MDVal = svalBuilder.getFunctionPointer(MD);
diff --git a/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp b/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
index 748c65f..a3b29ff 100644
--- a/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
+++ b/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
@@ -84,7 +84,7 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState,
// pointer should remain unchanged. Ignore static methods, since they do not
// have 'this' pointers.
const CXXMethodDecl *CXXMD = dyn_cast<CXXMethodDecl>(STC->getDecl());
- if (CXXMD && !CXXMD->isStatic()) {
+ if (CXXMD && CXXMD->isImplicitObjectMemberFunction()) {
const CXXThisRegion *ThisR =
MRMgr.getCXXThisRegion(CXXMD->getThisType(), STC);
ITraits.setTrait(ThisR,
diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index f827f43..d89d826 100644
--- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -275,7 +275,7 @@ DefinedSVal SValBuilder::getMemberPointer(const NamedDecl *ND) {
// We don't need to play a similar trick for static member fields
// because these are represented as plain VarDecls and not FieldDecls
// in the AST.
- if (MD->isStatic())
+ if (!MD->isImplicitObjectMemberFunction())
return getFunctionPointer(MD);
}
diff --git a/clang/test/Analysis/cxx2b-deducing-this.cpp b/clang/test/Analysis/cxx2b-deducing-this.cpp
new file mode 100644
index 0000000..d22a897
--- /dev/null
+++ b/clang/test/Analysis/cxx2b-deducing-this.cpp
@@ -0,0 +1,62 @@
+// RUN: %clang_analyze_cc1 -std=c++2b -verify %s \
+// RUN: -analyzer-checker=core,debug.ExprInspection
+
+template <typename T> void clang_analyzer_dump(T);
+
+struct S {
+ int num;
+ S *orig;
+
+ void a(this auto Self) {
+ clang_analyzer_dump(&Self); // expected-warning {{&Self}}
+ clang_analyzer_dump(Self.orig); // expected-warning {{&s}}
+ clang_analyzer_dump(Self.num); // expected-warning {{5 S32b}}
+ clang_analyzer_dump(Self.orig->num); // expected-warning {{5 S32b}}
+
+ Self.num = 1;
+ clang_analyzer_dump(Self.num); // expected-warning {{1 S32b}}
+ clang_analyzer_dump(Self.orig->num); // expected-warning {{5 S32b}}
+ }
+
+ void b(this auto& Self) {
+ clang_analyzer_dump(&Self); // expected-warning {{&s}}
+ clang_analyzer_dump(Self.orig); // expected-warning {{&s}}
+ clang_analyzer_dump(Self.num); // expected-warning {{5 S32b}}
+ clang_analyzer_dump(Self.orig->num); // expected-warning {{5 S32b}}
+
+ Self.num = 2;
+ clang_analyzer_dump(Self.num); // expected-warning {{2 S32b}}
+ clang_analyzer_dump(Self.orig->num); // expected-warning {{2 S32b}}
+ }
+
+ void c(this S Self) {
+ clang_analyzer_dump(&Self); // expected-warning {{&Self}}
+ clang_analyzer_dump(Self.orig); // expected-warning {{&s}}
+ clang_analyzer_dump(Self.num); // expected-warning {{2 S32b}}
+ clang_analyzer_dump(Self.orig->num); // expected-warning {{2 S32b}}
+
+ Self.num = 3;
+ clang_analyzer_dump(Self.num); // expected-warning {{3 S32b}}
+ clang_analyzer_dump(Self.orig->num); // expected-warning {{2 S32b}}
+ }
+
+ void c(this S Self, int I) {
+ clang_analyzer_dump(I); // expected-warning {{11 S32b}}
+ clang_analyzer_dump(&Self); // expected-warning {{&Self}}
+ clang_analyzer_dump(Self.orig); // expected-warning {{&s}}
+ clang_analyzer_dump(Self.num); // expected-warning {{2 S32b}}
+ clang_analyzer_dump(Self.orig->num); // expected-warning {{2 S32b}}
+
+ Self.num = 4;
+ clang_analyzer_dump(Self.num); // expected-warning {{4 S32b}}
+ clang_analyzer_dump(Self.orig->num); // expected-warning {{2 S32b}}
+ }
+};
+
+void top() {
+ S s = {/*num=*/5, /*orig=*/&s};
+ s.a();
+ s.b(); // This call changes 's.num' to 2.
+ s.c();
+ s.c(11);
+}
diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p1.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p1.cpp
index 21f71f0..b1b5b77 100644
--- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p1.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/dcl.fct.def.default/p1.cpp
@@ -5,10 +5,10 @@
// -- not have default arguments
struct DefArg {
static DefArg &&make();
- DefArg(int n = 5) = default; // expected-error {{an explicitly-defaulted constructor cannot have default arguments}}
- DefArg(const DefArg &DA = make()) = default; // expected-error {{an explicitly-defaulted constructor cannot have default arguments}}
+ DefArg(int n = 5) = default; // expected-error {{an explicitly-defaulted default constructor cannot have default arguments}}
+ DefArg(const DefArg &DA = make()) = default; // expected-error {{an explicitly-defaulted default constructor cannot have default arguments}}
DefArg(const DefArg &DA, int k = 3) = default; // expected-error {{an explicitly-defaulted copy constructor cannot have default arguments}}
- DefArg(DefArg &&DA = make()) = default; // expected-error {{an explicitly-defaulted constructor cannot have default arguments}}
+ DefArg(DefArg &&DA = make()) = default; // expected-error {{an explicitly-defaulted default constructor cannot have default arguments}}
DefArg(DefArg &&DA, int k = 3) = default; // expected-error {{an explicitly-defaulted move constructor cannot have default arguments}}
DefArg &operator=(const DefArg&, int k = 4) = default; // expected-error {{parameter of overloaded 'operator=' cannot have a default argument}}
DefArg &operator=(DefArg&&, int k = 4) = default; // expected-error {{parameter of overloaded 'operator=' cannot have a default argument}}
diff --git a/clang/test/CXX/drs/dr25xx.cpp b/clang/test/CXX/drs/dr25xx.cpp
index 68fb4bd..f1b5a1c 100644
--- a/clang/test/CXX/drs/dr25xx.cpp
+++ b/clang/test/CXX/drs/dr25xx.cpp
@@ -81,6 +81,63 @@ using ::dr2521::operator""_div;
#endif
} // namespace dr2521
+
+#if __cplusplus >= 202302L
+namespace dr2553 { // dr2553: 18
+struct B {
+ virtual void f(this B&); // expected-error {{an explicit object parameter cannot appear in a virtual function}} \
+ // expected-note {{here}}
+ static void f(this B&); // expected-error {{an explicit object parameter cannot appear in a static function}}
+ virtual void g(); // expected-note {{here}}
+};
+struct D : B {
+ void g(this D&); // expected-error {{an explicit object parameter cannot appear in a virtual function}}
+};
+
+struct D2 : B {
+ void f(this B&); // expected-error {{an explicit object parameter cannot appear in a virtual function}}
+};
+
+}
+#endif
+
+#if __cplusplus >= 202302L
+namespace dr2554 { // dr2554: 18 review
+struct B {
+ virtual void f(); // expected-note 3{{here}}
+};
+
+struct D : B {
+ void f(this D&); // expected-error {{an explicit object parameter cannot appear in a virtual function}}
+};
+
+struct D2 : B {
+ void f(this B&); // expected-error {{an explicit object parameter cannot appear in a virtual function}}
+};
+struct T {};
+struct D3 : B {
+ void f(this T&); // expected-error {{an explicit object parameter cannot appear in a virtual function}}
+};
+
+}
+#endif
+
+#if __cplusplus >= 202302L
+namespace dr2561 { // dr2561: 18 review
+struct C {
+ constexpr C(auto) { }
+};
+void foo() {
+ constexpr auto b = [](this C) { return 1; };
+ constexpr int (*fp)(C) = b;
+ static_assert(fp(1) == 1);
+ static_assert((&decltype(b)::operator())(1) == 1);
+}
+
+}
+#endif
+
+
namespace dr2565 { // dr2565: 16
#if __cplusplus >= 202002L
template<typename T>
diff --git a/clang/test/CXX/drs/dr26xx.cpp b/clang/test/CXX/drs/dr26xx.cpp
index 8dd07b6..8517cd5 100644
--- a/clang/test/CXX/drs/dr26xx.cpp
+++ b/clang/test/CXX/drs/dr26xx.cpp
@@ -1,4 +1,6 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify
+// RUN: %clang_cc1 -std=c++2b -triple x86_64-unknown-unknown %s -verify
+
namespace dr2621 { // dr2621: yes
enum class E { a };
@@ -108,6 +110,7 @@ auto z = [a = 42](int a) { // expected-error {{a lambda parameter cannot shadow
}
+#if __cplusplus >= 202302L
namespace dr2650 { // dr2650: yes
template <class T, T> struct S {};
template <class T> int f(S<T, T{}>*); // expected-note {{type 'X' of non-type template parameter is not a structural type}}
@@ -116,6 +119,16 @@ class X {
};
int i0 = f<X>(0); //expected-error {{no matching function for call to 'f'}}
}
+#endif
+
+#if __cplusplus >= 202302L
+namespace dr2653 { // dr2653: 18
+ struct Test { void f(this const auto& = Test{}); };
+ // expected-error@-1 {{the explicit object parameter cannot have a default argument}}
+ auto L = [](this const auto& = Test{}){};
+ // expected-error@-1 {{the explicit object parameter cannot have a default argument}}
+}
+#endif
namespace dr2654 { // dr2654: 16
void f() {
@@ -171,3 +184,18 @@ void m() {
bar(0);
}
}
+#if __cplusplus >= 202302L
+namespace dr2687 { // dr2687: 18
+struct S{
+ void f(int);
+ static void g(int);
+ void h(this const S&, int);
+};
+
+void test() {
+ (&S::f)(1); // expected-error {{called object type 'void (dr2687::S::*)(int)' is not a function or function pointer}}
+ (&S::g)(1);
+ (&S::h)(S(), 1);
+}
+}
+#endif
diff --git a/clang/test/CXX/drs/dr5xx.cpp b/clang/test/CXX/drs/dr5xx.cpp
index cba0c4e..a356348 100644
--- a/clang/test/CXX/drs/dr5xx.cpp
+++ b/clang/test/CXX/drs/dr5xx.cpp
@@ -771,10 +771,15 @@ namespace dr574 { // dr574: yes
};
#if __cplusplus >= 201103L
struct C {
- C &operator=(const C&) &; // expected-note {{not viable}} expected-note {{candidate}} expected-note {{here}}
+ C &operator=(const C&) &; // #574-overload1 \
+ // expected-note {{not viable}} \
+ // expected-note {{here}}
+
};
struct D {
- D &operator=(const D&) &&; // expected-note {{not viable}} expected-note {{candidate}} expected-note {{here}}
+ D &operator=(const D&) &&; // #574-overload2 \
+ // expected-note {{not viable}} \
+ // expected-note {{here}}
};
void test(C c, D d) {
c = c;
@@ -786,10 +791,17 @@ namespace dr574 { // dr574: yes
struct Test {
friend A &A::operator=(const A&); // expected-error {{does not match}}
friend B &B::operator=(const B&); // expected-error {{does not match}}
-#if __cplusplus >= 201103L
+#if __cplusplus >= 202302L
+ friend C &C::operator=(const C&); // expected-error {{conflicting types for 'operator='}}
+ friend D &D::operator=(const D&); // expected-error {{conflicting types for 'operator='}} __cplusplus >= 201103L
+#elif __cplusplus >= 201103L
// FIXME: We shouldn't produce the 'cannot overload' diagnostics here.
- friend C &C::operator=(const C&); // expected-error {{does not match}} expected-error {{cannot overload}}
- friend D &D::operator=(const D&); // expected-error {{does not match}} expected-error {{cannot overload}}
+ friend C &C::operator=(const C&); // expected-error {{does not match}} \
+ // expected-error {{cannot overload}} \
+ // expected-note@#574-overload1 {{candidate}}
+ friend D &D::operator=(const D&); // expected-error {{does not match}} \
+ // expected-error {{cannot overload}} \
+ // expected-note@#574-overload2 {{candidate}}
#endif
};
}
diff --git a/clang/test/CXX/special/class.copy/p25-0x.cpp b/clang/test/CXX/special/class.copy/p25-0x.cpp
index 133bb1a..c13c686 100644
--- a/clang/test/CXX/special/class.copy/p25-0x.cpp
+++ b/clang/test/CXX/special/class.copy/p25-0x.cpp
@@ -1,8 +1,16 @@
// RUN: %clang_cc1 -std=c++11 -verify %s -Wno-deprecated-builtins
// RUN: %clang_cc1 -std=c++11 -verify %s -Wno-deprecated-builtins -fclang-abi-compat=14 -DCLANG_ABI_COMPAT=14
+// RUN: %clang_cc1 -fsyntax-only -std=c++2b -DDEDUCING_THIS -Wno-deprecated-builtins %s -verify
// expected-no-diagnostics
+#if DEDUCING_THIS
+#define EXPLICIT_PARAMETER(...) this __VA_ARGS__,
+#else
+#define EXPLICIT_PARAMETER(Param)
+#endif
+
+
template<typename T, bool B> struct trivially_assignable_check {
static_assert(B == __has_trivial_assign(T), "");
static_assert(B == __is_trivially_assignable(T&, T), "");
@@ -23,14 +31,18 @@ using _ = trivially_assignable<Trivial>;
// A copy/move assignment operator for class X is trivial if it is not user-provided,
struct UserProvided {
- UserProvided &operator=(const UserProvided &);
+ UserProvided &operator=(EXPLICIT_PARAMETER(UserProvided&)
+ const UserProvided &);
};
using _ = not_trivially_assignable<UserProvided>;
// its declared parameter type is the same as if it had been implicitly
// declared,
struct NonConstCopy {
- NonConstCopy &operator=(NonConstCopy &) = default;
+ NonConstCopy &operator=(EXPLICIT_PARAMETER(NonConstCopy&) NonConstCopy &) = default;
+#if DEDUCING_THIS
+ NonConstCopy &operator=(EXPLICIT_PARAMETER(NonConstCopy&&) NonConstCopy &) = default;
+#endif
};
#if defined(CLANG_ABI_COMPAT) && CLANG_ABI_COMPAT <= 14
// Up until (and including) Clang 14, non-const copy assignment operators were not trivial because
@@ -50,9 +62,20 @@ static_assert(!__is_trivially_assignable(NonConstCopy &&, NonConstCopy), "");
static_assert(!__is_trivially_assignable(NonConstCopy &&, NonConstCopy &&), "");
struct DefaultedSpecialMembers {
- DefaultedSpecialMembers &operator=(const DefaultedSpecialMembers &) = default;
- DefaultedSpecialMembers &operator=(DefaultedSpecialMembers &) = default;
- DefaultedSpecialMembers &operator=(DefaultedSpecialMembers &&) = default;
+ DefaultedSpecialMembers &operator=(EXPLICIT_PARAMETER(DefaultedSpecialMembers&)
+ const DefaultedSpecialMembers &) = default;
+ DefaultedSpecialMembers &operator=(EXPLICIT_PARAMETER(DefaultedSpecialMembers&)
+ DefaultedSpecialMembers &) = default;
+ DefaultedSpecialMembers &operator=(EXPLICIT_PARAMETER(DefaultedSpecialMembers&)
+ DefaultedSpecialMembers &&) = default;
+#if DEDUCING_THIS
+ DefaultedSpecialMembers &operator=(EXPLICIT_PARAMETER(DefaultedSpecialMembers&&)
+ const DefaultedSpecialMembers &) = default;
+ DefaultedSpecialMembers &operator=(EXPLICIT_PARAMETER(DefaultedSpecialMembers&&)
+ DefaultedSpecialMembers &) = default;
+ DefaultedSpecialMembers &operator=(EXPLICIT_PARAMETER(DefaultedSpecialMembers&&)
+ DefaultedSpecialMembers &&) = default;
+#endif
};
using _ = trivially_assignable<DefaultedSpecialMembers>;
#endif
@@ -84,11 +107,16 @@ static_assert(__is_trivially_assignable(MutableTemplateCtorMember, MutableTempla
// Both trivial and non-trivial special members.
struct TNT {
- TNT &operator=(const TNT &) = default; // trivial
- TNT &operator=(TNT &); // non-trivial
-
- TNT &operator=(TNT &&) = default; // trivial
- TNT &operator=(const TNT &&); // non-trivial
+ TNT &operator=(EXPLICIT_PARAMETER(TNT&) const TNT &) = default; // trivial
+ TNT &operator=(EXPLICIT_PARAMETER(TNT&) TNT &); // non-trivial
+ TNT &operator=(EXPLICIT_PARAMETER(TNT&) TNT &&) = default; // trivial
+ TNT &operator=(EXPLICIT_PARAMETER(TNT&) const TNT &&); // non-trivial
+#if DEDUCING_THIS
+ TNT &operator=(this TNT&&, const TNT &) = default; // trivial
+ TNT &operator=(this TNT&&, TNT &); // non-trivial
+ TNT &operator=(this TNT&&, TNT &&) = default; // trivial
+ TNT &operator=(this TNT&&, const TNT &&); // non-trivial
+#endif
};
static_assert(!__has_trivial_assign(TNT), "lie deliberately for gcc compatibility");
diff --git a/clang/test/CodeGenCXX/cxx2b-deducing-this-cc.cpp b/clang/test/CodeGenCXX/cxx2b-deducing-this-cc.cpp
new file mode 100644
index 0000000..bfbb24f
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2b-deducing-this-cc.cpp
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -std=c++2b %s -emit-llvm -triple i386-windows-msvc -o - | FileCheck %s
+
+namespace CC {
+
+struct T {
+ static void f(T&);
+ void __cdecl g(this T&);
+ void __thiscall h(this T&);
+ void i(this T&);
+};
+
+void a() {
+ T t;
+ T::f(t);
+}
+// CHECK: define dso_local void @"?a@CC@@YAXXZ"{{.*}}
+// CHECK: call void @"?f@T@CC@@SAXAAU12@@Z"{{.*}}
+
+void b() {
+ T t;
+ t.g();
+}
+// CHECK: define dso_local void @"?b@CC@@YAXXZ"{{.*}}
+// CHECK: call void @"?g@T@CC@@SAX_VAAU12@@Z"{{.*}}
+
+void c() {
+ T t;
+ t.h();
+}
+// CHECK: define dso_local void @"?c@CC@@YAXXZ"{{.*}}
+// CHECK: call x86_thiscallcc void @"?h@T@CC@@SEX_VAAU12@@Z"{{.*}}
+
+void d() {
+ T t;
+ t.i();
+}
+// CHECK: define dso_local void @"?d@CC@@YAXXZ"{{.*}}
+// CHECK: call void @"?i@T@CC@@SAX_VAAU12@@Z"{{.*}}
+
+}
diff --git a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp
new file mode 100644
index 0000000..de8c124
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp
@@ -0,0 +1,111 @@
+// RUN: %clang_cc1 -std=c++2b %s -emit-llvm -triple x86_64-linux -o - | FileCheck %s
+
+struct TrivialStruct {
+ void explicit_object_function(this TrivialStruct) {}
+};
+void test() {
+ TrivialStruct s;
+ s.explicit_object_function();
+}
+// CHECK: define {{.*}}test{{.*}}
+// CHECK-NEXT: entry:
+// CHECK: {{.*}} = alloca %struct.TrivialStruct, align 1
+// CHECK: {{.*}} = alloca %struct.TrivialStruct, align 1
+// CHECK: call void {{.*}}explicit_object_function{{.*}}
+// CHECK-NEXT: ret void
+// CHECK-NEXT: }
+
+// CHECK: define {{.*}}explicit_object_function{{.*}}
+// CHECK-NEXT: entry:
+// CHECK: {{.*}} = alloca %struct.TrivialStruct, align 1
+// CHECK: ret void
+// CHECK-NEXT: }
+
+
+void test_lambda() {
+ [](this auto This) -> int {
+ return This();
+ }();
+}
+
+//CHECK: define dso_local void @{{.*}}test_lambda{{.*}}() #0 {
+//CHECK: entry:
+//CHECK: %agg.tmp = alloca %class.anon, align 1
+//CHECK: %ref.tmp = alloca %class.anon, align 1
+//CHECK: %call = call noundef i32 @"_ZZ11test_lambdavENH3$_0clIS_EEiT_"()
+//CHECK: ret void
+//CHECK: }
+
+//CHECK: define internal noundef i32 @"_ZZ11test_lambdavENH3$_0clIS_EEiT_"() #0 align 2 {
+//CHECK: entry:
+//CHECK: %This = alloca %class.anon, align 1
+//CHECK: %agg.tmp = alloca %class.anon, align 1
+//CHECK: %call = call noundef i32 @"_ZZ11test_lambdavENH3$_0clIS_EEiT_"()
+//CHECK: ret i32 %call
+//CHECK: }
+
+void test_lambda_ref() {
+ auto l = [i = 42](this auto & This, int j) -> int {
+ return This(j);
+ };
+ l(0);
+}
+
+// CHECK: define dso_local void @_Z15test_lambda_refv() #0 {
+// CHECK: entry:
+// CHECK: %[[This_address:.]] = alloca %class.anon{{.*}}, align 4
+// CHECK: %[[i_addr:.*]] = getelementptr inbounds %class.anon{{.*}}, ptr %[[This_address]], i32 0, i32 0
+// CHECK: store i32 42, ptr %[[i_addr]], align 4
+// CHECK: %call = call noundef i32 @"_ZZ15test_lambda_refvENH3$_0clIS_EEiRT_i"{{.*}}
+// CHECK: ret void
+// CHECK: }
+
+// CHECK: define internal noundef i32 @"_ZZ15test_lambda_refvENH3$_0clIS_EEiRT_i"{{.*}}
+// CHECK: entry:
+// CHECK: %This.addr = alloca ptr, align 8
+// CHECK: %j.addr = alloca i32, align 4
+// CHECK: store ptr %This, ptr %This.addr, align 8
+// CHECK: store i32 %j, ptr %j.addr, align 4
+// CHECK: %[[this_addr:.*]] = load ptr, ptr %This.addr, align 8
+// CHECK: %[[j_addr:.*]] = load i32, ptr %j.addr, align 4
+// CHECK: %call = call noundef i32 @"_ZZ15test_lambda_refvENH3$_0clIS_EEiRT_i"(ptr noundef nonnull align 4 dereferenceable(4) %[[this_addr]], i32 noundef %[[j_addr]])
+// CHECK: ret i32 %call
+// CHECK: }
+
+
+struct TestPointer {
+ void f(this TestPointer &);
+};
+
+void test_pointer() {
+ TestPointer t;
+ using Fn = void(TestPointer&);
+ Fn* fn = &TestPointer::f;
+ fn(t);
+}
+//CHECK: define dso_local void @_Z12test_pointerv() #0 {
+//CHECK-NEXT: entry:
+//CHECK-NEXT: %t = alloca %struct.TestPointer, align 1
+//CHECK-NEXT: %fn = alloca ptr, align 8
+//CHECK-NEXT: store ptr @_ZNH11TestPointer1fERS_, ptr %fn, align 8
+//CHECK: %[[fn_ptr:.*]] = load ptr, ptr %fn, align 8
+//CHECK-NEXT: call void %[[fn_ptr]](ptr noundef nonnull align 1 dereferenceable(1) %t)
+//CHECK-NEXT: ret void
+//CHECK-NEXT: }
+
+
+struct MaterializedTemporary {
+ void foo(this MaterializedTemporary&&);
+ MaterializedTemporary();
+ ~MaterializedTemporary();
+};
+
+void test_temporary() {
+ MaterializedTemporary{}.foo();
+}
+
+//CHECK: define dso_local void @_Z14test_temporaryv(){{.*}}
+//CHECK-NEXT: entry:
+//CHECK: %ref.tmp = alloca %struct.MaterializedTemporary, align 1
+//CHECK: call void @_ZN21MaterializedTemporaryC1Ev(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp){{.*}}
+//CHECK invoke void @_ZNH21MaterializedTemporary3fooEOS_(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp){{.*}}
diff --git a/clang/test/CodeGenCXX/cxx2b-mangle-deducing-this.cpp b/clang/test/CodeGenCXX/cxx2b-mangle-deducing-this.cpp
new file mode 100644
index 0000000..579e757
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2b-mangle-deducing-this.cpp
@@ -0,0 +1,64 @@
+// RUN: %clang_cc1 -std=c++2b -emit-llvm -triple x86_64-linux -o - %s 2>/dev/null | FileCheck %s
+
+struct S {
+friend void test();
+public:
+ void a(this auto){}
+ void b(this auto&){}
+ void c(this S){}
+ void c(this S, int){}
+private:
+ void d(this auto){}
+ void e(this auto&){}
+ void f(this S){}
+ void f(this S, int){}
+protected:
+ void g(this auto){}
+ void h(this auto&){}
+ void i(this S){}
+ void i(this S, int){}
+};
+
+
+void test() {
+ S s;
+ s.a();
+ // CHECK: call void @_ZNH1S1aIS_EEvT_
+ s.b();
+ // CHECK: call void @_ZNH1S1bIS_EEvRT_
+ s.c();
+ // CHECK: call void @_ZNH1S1cES_
+ s.c(0);
+ // CHECK: call void @_ZNH1S1cES_i
+ s.d();
+ // CHECK: call void @_ZNH1S1dIS_EEvT_
+ s.e();
+ // CHECK: call void @_ZNH1S1eIS_EEvRT_
+ s.f();
+ // CHECK: call void @_ZNH1S1fES_
+ s.f(0);
+ // CHECK: call void @_ZNH1S1fES_i
+ s.g();
+ // CHECK: call void @_ZNH1S1gIS_EEvT_
+ s.h();
+ // CHECK: call void @_ZNH1S1hIS_EEvRT_
+ s.i();
+ // CHECK: call void @_ZNH1S1iES_
+ s.i(0);
+ // CHECK: call void @_ZNH1S1iES_i
+}
+
+struct StaticAndExplicit {
+ static void f(StaticAndExplicit);
+ void f(this StaticAndExplicit);
+};
+
+void test2() {
+ StaticAndExplicit s;
+
+ StaticAndExplicit::f(s);
+ // CHECK: call void @_ZN17StaticAndExplicit1fES_
+
+ s.f();
+ // CHECK: call void @_ZNH17StaticAndExplicit1fES_
+}
diff --git a/clang/test/CodeGenCXX/microsoft-abi-explicit-object-parameters.cpp b/clang/test/CodeGenCXX/microsoft-abi-explicit-object-parameters.cpp
new file mode 100644
index 0000000..b633f6c
--- /dev/null
+++ b/clang/test/CodeGenCXX/microsoft-abi-explicit-object-parameters.cpp
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -std=c++2b -emit-llvm -triple=x86_64-pc-win32 -o - %s 2>/dev/null | FileCheck %s
+
+struct S {
+friend void test();
+public:
+ void a(this auto){}
+ void b(this auto&){}
+ void c(this S){}
+ void c(this S, int){}
+private:
+ void d(this auto){}
+ void e(this auto&){}
+ void f(this S){}
+ void f(this S, int){}
+protected:
+ void g(this auto){}
+ void h(this auto&){}
+ void i(this S){}
+ void i(this S, int){}
+};
+
+void test() {
+ S s;
+ s.a();
+ // CHECK: call void @"??$a@US@@@S@@SAX_VU0@@Z"
+ s.b();
+ // CHECK: call void @"??$b@US@@@S@@SAX_VAEAU0@@Z"
+ s.c();
+ // CHECK: call void @"?c@S@@SAX_VU1@@Z"
+ s.c(0);
+ // CHECK: call void @"?c@S@@SAX_VU1@H@Z"
+ s.d();
+ // CHECK: call void @"??$d@US@@@S@@CAX_VU0@@Z"
+ s.e();
+ // CHECK: call void @"??$e@US@@@S@@CAX_VAEAU0@@Z"
+ s.f();
+ // CHECK: call void @"?f@S@@CAX_VU1@@Z"
+ s.f(0);
+ // CHECK: call void @"?f@S@@CAX_VU1@H@Z"
+ s.g();
+ // CHECK: call void @"??$g@US@@@S@@KAX_VU0@@Z"
+ s.h();
+ // CHECK: call void @"??$h@US@@@S@@KAX_VAEAU0@@Z"
+ s.i();
+ // CHECK: call void @"?i@S@@KAX_VU1@@Z"
+ s.i(0);
+ // CHECK: call void @"?i@S@@KAX_VU1@H@Z"
+}
+
+
+struct S2 {
+ int i = 0;
+ void foo(this const S2&, int);
+};
+struct T {
+ S2 bar(this const T&, int);
+};
+void chain_test() {
+ T t;
+ t.bar(0).foo(0);
+}
+// CHECK: define {{.*}}chain_test{{.*}}
+// CHECK-NEXT: entry:
+// CHECK: {{.*}} = alloca %struct.T, align 1
+// CHECK: {{.*}} = alloca %struct.S2, align 4
+// CHECK: %call = call i32 @"?bar@T@@SA?AUS2@@_VAEBU1@H@Z"{{.*}}
+// CHECK: %coerce.dive = getelementptr inbounds %struct.S2, {{.*}} %{{.*}}, i32 0, i32 0
+// CHECK store i32 %call, ptr %coerce.dive, align 4
+// CHECK: call void @"?foo@S2@@SAX_VAEBU1@H@Z"
+// CHECK: ret void
diff --git a/clang/test/FixIt/fixit-deducing-this.cpp b/clang/test/FixIt/fixit-deducing-this.cpp
new file mode 100644
index 0000000..c073fd3
--- /dev/null
+++ b/clang/test/FixIt/fixit-deducing-this.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -verify -std=c++2c %s
+// RUN: cp %s %t
+// RUN: not %clang_cc1 -x c++ -std=c++2c -fixit %t
+// RUN: %clang_cc1 -x c++ -std=c++2c %t
+// RUN: not %clang_cc1 -std=c++2c -x c++ -fsyntax-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+struct S {
+ void f(this S);
+ void g() {
+ (void)&f; // expected-error {{must explicitly qualify name of member function when taking its address}}
+// CHECK: fix-it:{{.*}}:{9:16-9:16}:"S::"
+ }
+};
+
+struct S2 {
+ void f(this S2 foo) {
+ g(); // expected-error {{call to non-static member function without an object argument}}
+// CHECK: fix-it:{{.*}}:{16:9-16:9}:"foo."
+
+ h(); // expected-error {{call to explicit member function without an object argument}}
+// CHECK: fix-it:{{.*}}:{19:9-19:9}:"foo."
+
+ i();
+
+ var; // expected-error {{invalid use of member 'var' in explicit object member function}}
+// CHECK: fix-it:{{.*}}:{24:9-24:9}:"foo."
+
+ }
+ void g();
+ void h(this S2 s);
+ static void i();
+ int var;
+};
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this-compat.cpp b/clang/test/SemaCXX/cxx2b-deducing-this-compat.cpp
new file mode 100644
index 0000000..32406df
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2b-deducing-this-compat.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++20 %s -verify
+
+struct S {
+ void f(this auto &a); // expected-error {{explicit object parameters are incompatible with C++ standards before C++2b}}
+};
+
+void f() {
+ (void)[](this auto&a){}; // expected-error {{explicit object parameters are incompatible with C++ standards before C++2b}}
+}
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this-constexpr.cpp b/clang/test/SemaCXX/cxx2b-deducing-this-constexpr.cpp
new file mode 100644
index 0000000..44de0d7
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2b-deducing-this-constexpr.cpp
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++2b %s -verify
+// expected-no-diagnostics
+
+template <typename Base>
+struct Wrap : Base {
+
+};
+
+struct S {
+ constexpr int f(this const S&) {
+ return 42;
+ }
+ constexpr int f(this const S&, auto&&... args) {
+ return (args + ... + 0);
+ }
+ constexpr int operator[](this const S&) {
+ return 42;
+ }
+ constexpr int operator[](this const S& self, int i) {
+ return i + self.base;
+ }
+ constexpr int operator()(this const S&) {
+ return 42;
+ }
+ constexpr int operator()(this const S& self, int i) {
+ return self.base + i;
+ }
+ constexpr bool operator==(this const S& self, auto && test) {
+ return self.base == test;
+ };
+ constexpr int operator*(this const S& self) {
+ return self.base + 22;
+ };
+ constexpr operator Wrap<S> (this const S& self) {
+ return Wrap<S>{self};
+ };
+ constexpr int operator <<(this Wrap<S> self, int i) {
+ return self.base+i;
+ }
+
+ int base = 20;
+};
+
+consteval void test() {
+ constexpr S s;
+ static_assert(s.f() == 42);
+ static_assert(s[] == 42);
+ static_assert(s[22] == 42);
+ static_assert(s.f() == 42);
+ static_assert(s() == 42);
+ static_assert(s(22) == 42);
+ static_assert(s == 20);
+ static_assert(s != 0);
+ static_assert(*s == 42);
+ static_assert((s << 11) == 31);
+}
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this-coro.cpp b/clang/test/SemaCXX/cxx2b-deducing-this-coro.cpp
new file mode 100644
index 0000000..dfa50cb
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2b-deducing-this-coro.cpp
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -std=c++2b %s -fsyntax-only -verify
+
+#include "Inputs/std-coroutine.h"
+
+struct S;
+template <typename T>
+class coro_test {
+public:
+ struct promise_type;
+ using handle = std::coroutine_handle<promise_type>;
+ struct promise_type {
+ promise_type(const promise_type&) = delete; // #copy-ctr
+ promise_type(T); // #candidate
+ coro_test get_return_object();
+ std::suspend_never initial_suspend();
+ std::suspend_never final_suspend() noexcept;
+ void return_void();
+ void unhandled_exception();
+
+
+ template<typename Arg, typename... Args>
+ void* operator new(decltype(0zu) sz, Arg&&, Args&... args) {
+ static_assert(!__is_same(__decay(Arg), S), "Ok"); // expected-error 2{{Ok}}
+ }
+
+ };
+private:
+ handle h;
+};
+
+
+template <typename Ret, typename... P>
+struct std::coroutine_traits<coro_test<S&>, Ret, P...> {
+ using promise_type = coro_test<S&>::promise_type;
+ static_assert(!__is_same(Ret, S&), "Ok"); // expected-error{{static assertion failed due to requirement '!__is_same(S &, S &)': Ok}}
+};
+
+
+struct S {
+
+ coro_test<S&> ok(this S&, int) {
+ co_return; // expected-note {{in instantiation}}
+ }
+
+ coro_test<const S&> ok2(this const S&) { // expected-note {{in instantiation}}
+ co_return;
+ }
+
+ coro_test<int> ko(this const S&) { // expected-error {{no matching constructor for initialization of 'std::coroutine_traits<coro_test<int>, const S &>::promise_type'}} \
+ // expected-note {{in instantiation}} \
+ // FIXME: the message below is unhelpful but this is pre-existing
+ // expected-note@#candidate {{candidate constructor not viable: requires 1 argument, but 0 were provided}} \
+ // expected-note@#copy-ctr {{candidate constructor not viable}}
+ co_return;
+ }
+
+};
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
new file mode 100644
index 0000000..cb83270
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
@@ -0,0 +1,544 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c++2b -Woverloaded-virtual %s -verify
+
+
+// FIXME: can we improve these diagnostics?
+void f(this); // expected-error{{variable has incomplete type 'void'}} \
+ // expected-error{{invalid use of 'this' outside of a non-static member function}}
+
+void g(this auto); // expected-error{{an explicit object parameter cannot appear in a non-member function}}
+
+auto l1 = [] (this auto) static {}; // expected-error{{an explicit object parameter cannot appear in a static lambda}}
+auto l2 = [] (this auto) mutable {}; // expected-error{{a lambda with an explicit object parameter cannot be mutable}}
+auto l3 = [](this auto...){}; // expected-error {{the explicit object parameter cannot be a function parameter pack}}
+auto l4 = [](int, this auto){}; // expected-error {{an explicit object parameter can only appear as the first parameter of the lambda}}
+
+struct S {
+ static void f(this auto); // expected-error{{an explicit object parameter cannot appear in a static function}}
+ virtual void f(this S); // expected-error{{an explicit object parameter cannot appear in a virtual function}}
+
+ void g(this auto) const; // expected-error{{explicit object member function cannot have 'const' qualifier}}
+ void h(this auto) &; // expected-error{{explicit object member function cannot have '&' qualifier}}
+ void i(this auto) &&; // expected-error{{explicit object member function cannot have '&&' qualifier}}
+ void j(this auto) volatile; // expected-error{{explicit object member function cannot have 'volatile' qualifier}}
+ void k(this auto) __restrict; // expected-error{{explicit object member function cannot have '__restrict' qualifier}}
+ void l(this auto) _Nonnull; // expected-error{{explicit object member function cannot have '' qualifie}}
+
+
+ void variadic(this auto...); // expected-error{{the explicit object parameter cannot be a function parameter pack}}
+ void not_first(int, this auto); // expected-error {{an explicit object parameter can only appear as the first parameter of the function}}
+
+ S(this auto); // expected-error {{an explicit object parameter cannot appear in a constructor}}
+ ~S(this S) {} // expected-error {{an explicit object parameter cannot appear in a destructor}} \
+ // expected-error {{destructor cannot have any parameters}}
+};
+
+namespace Override {
+struct A {
+ virtual void f(); // expected-note 2{{here}}
+ virtual void g(int); // expected-note {{here}}
+ virtual void h() const; // expected-note 5{{here}}
+};
+
+// CWG2553
+struct B : A {
+ int f(this B&, int); // expected-warning {{hides overloaded virtual function}}
+ int f(this B&); // expected-error {{an explicit object parameter cannot appear in a virtual function}}
+ int g(this B&); // expected-warning {{hides overloaded virtual function}}
+ int h(this B&); // expected-error {{an explicit object parameter cannot appear in a virtual function}}
+ int h(this B&&); // expected-error {{an explicit object parameter cannot appear in a virtual function}}
+ int h(this const B&&); // expected-error {{an explicit object parameter cannot appear in a virtual function}}
+ int h(this A&); // expected-error {{an explicit object parameter cannot appear in a virtual function}}
+ int h(this int); // expected-error {{an explicit object parameter cannot appear in a virtual function}}
+};
+}
+
+namespace DefaultArgs {
+ struct Test { void f(this const auto& = Test{}); };
+ // expected-error@-1 {{the explicit object parameter cannot have a default argument}}
+ auto L = [](this const auto& = Test{}){};
+ // expected-error@-1 {{the explicit object parameter cannot have a default argument}}
+}
+
+struct CannotUseThis {
+ int fun();
+ int m;
+ void f(this auto) {
+ this->fun(); // expected-error{{invalid use of 'this' in a function with an explicit object parameter}}
+ fun(); // expected-error {{call to non-static member function without an object argument}}
+ m = 0; // expected-error {{invalid use of member 'm' in explicit object member function}}
+ }
+};
+
+struct CannotUseThisBase {
+ void foo();
+ int n;
+ static int i;
+};
+
+struct CannotUseThisDerived : CannotUseThisBase {
+ void bar(this auto) {
+ foo(); // expected-error {{call to non-static member function without an object argument}}
+ n = 12; // expected-error {{invalid use of member 'n' in explicit object member function}}
+ i = 100;
+ }
+};
+
+namespace ThisInLambdaWithCaptures {
+
+struct Test {
+ Test(auto&&);
+};
+
+void test() {
+
+ [i = 0](this Test) { }();
+ // expected-error@-1 {{invalid explicit object parameter type 'ThisInLambdaWithCaptures::Test' in lambda with capture; the type must be the same as, or derived from, the lambda}}
+
+ struct Derived;
+ auto ok = [i = 0](this const Derived&) {};
+ auto ko = [i = 0](this const Test&) {};
+ // expected-error@-1 {{invalid explicit object parameter type 'ThisInLambdaWithCaptures::Test' in lambda with capture; the type must be the same as, or derived from, the lambda}}
+
+ struct Derived : decltype(ok){};
+ Derived dok{ok};
+ dok();
+
+ struct DerivedErr : decltype(ko){};
+ DerivedErr dko{ko};
+ dko();
+
+ auto alsoOk = [](this const Test &) {};
+ alsoOk();
+}
+
+struct Frobble;
+auto nothingIsOkay = [i = 0](this const Frobble &) {}; // expected-note {{candidate function not viable: requires 0 non-object arguments, but 1 was provided}}
+struct Frobble {} f;
+void test2() {
+ nothingIsOkay(f); // expected-error {{no matching function for call to object of type}}
+}
+
+}
+
+struct Corresponding {
+ void a(this Corresponding&); // expected-note 2{{here}}
+ void a(); // expected-error{{cannot be redeclared}}
+ void a() &; // expected-error{{cannot be redeclared}}
+ void a(this Corresponding&, int);
+ void a(this Corresponding&, double);
+
+ void b(this const Corresponding&); // expected-note 2{{here}}
+ void b() const; // expected-error{{cannot be redeclared}}
+ void b() const &; // expected-error{{cannot be redeclared}}
+
+ void c(this Corresponding&&); // expected-note {{here}}
+ void c() &&; // expected-error{{cannot be redeclared}}
+
+ void d(this Corresponding&);
+ void d(this Corresponding&&);
+ void d(this const Corresponding&);
+ void d(this const int&);
+ void d(this const int);
+ void d(this int);
+
+ void e(this const Corresponding&&); // expected-note {{here}}
+ void e() const &&; // expected-error{{cannot be redeclared}}
+
+};
+
+template <typename T>
+struct CorrespondingTpl {
+ void a(this CorrespondingTpl&); // expected-note 2{{here}}
+ void a(); // expected-error{{cannot be redeclared}}
+ void a() &; // expected-error{{cannot be redeclared}}
+ void a(this Corresponding&, int);
+ void a(this Corresponding&, double);
+ void a(long);
+
+
+ void b(this const CorrespondingTpl&); // expected-note 2{{here}}
+ void b() const; // expected-error{{cannot be redeclared}}
+ void b() const &; // expected-error{{cannot be redeclared}}
+
+ void c(this CorrespondingTpl&&); // expected-note {{here}}
+ void c() &&; // expected-error{{cannot be redeclared}}
+
+ void d(this Corresponding&);
+ void d(this Corresponding&&);
+ void d(this const Corresponding&);
+ void d(this const int&);
+ void d(this const int);
+ void d(this int);
+
+ void e(this const CorrespondingTpl&&); // expected-note {{here}}
+ void e() const &&; // expected-error{{cannot be redeclared}}
+};
+
+struct C {
+ template <typename T>
+ C(T){}
+};
+
+void func(int i) {
+ (void)[=](this auto&&) { return i; }();
+ (void)[=](this const auto&) { return i; }();
+ (void)[i](this C) { return i; }(); // expected-error{{invalid explicit object parameter type 'C'}}
+ (void)[=](this C) { return i; }(); // expected-error{{invalid explicit object parameter type 'C'}}
+ (void)[](this C) { return 42; }();
+ auto l = [=](this auto&) {};
+ struct D : decltype(l) {};
+ D d{l};
+ d();
+}
+
+void TestMutationInLambda() {
+ [i = 0](this auto &&){ i++; }();
+ [i = 0](this auto){ i++; }();
+ [i = 0](this const auto&){ i++; }();
+ // expected-error@-1 {{cannot assign to a variable captured by copy in a non-mutable lambda}}
+}
+
+struct Over_Call_Func_Example {
+ void a();
+ void b() {
+ a(); // ok, (*this).a()
+ }
+
+ void f(this const Over_Call_Func_Example&); // expected-note {{here}}
+ void g() const {
+ f(); // ok: (*this).f()
+ f(*this); // expected-error{{too many non-object arguments to function call}}
+ this->f(); // ok
+ }
+
+ static void h() {
+ f(); // expected-error{{call to non-static member function without an object argument}}
+ f(Over_Call_Func_Example{}); // expected-error{{call to non-static member function without an object argument}}
+ Over_Call_Func_Example{}.f(); // ok
+ }
+
+ void k(this int);
+ operator int() const;
+ void m(this const Over_Call_Func_Example& c) {
+ c.k(); // ok
+ }
+};
+
+struct AmbiguousConversion {
+ void f(this int); // expected-note {{candidate function}}
+ void f(this float); // expected-note {{candidate function}}
+
+ operator int() const;
+ operator float() const;
+
+ void test(this const AmbiguousConversion &s) {
+ s.f(); // expected-error {{call to member function 'f' is ambiguous}}
+ }
+};
+
+struct IntToShort {
+ void s(this short);
+ operator int() const;
+ void test(this const IntToShort &val) {
+ val.s();
+ }
+};
+
+struct ShortToInt {
+ void s(this int);
+ operator short() const;
+ void test(this const ShortToInt &val) {
+ val.s();
+ }
+};
+
+namespace arity_diagnostics {
+struct S {
+ void f(this auto &&, auto, auto); // expected-note {{requires 2 non-object arguments, but 0 were provided}}
+ void g(this auto &&, auto, auto); // expected-note {{requires 2 non-object arguments, but 3 were provided}}
+ void h(this auto &&, int, int i = 0); // expected-note {{requires at least 1 non-object argument, but 0 were provided}}
+ void i(this S&&, int); // expected-note 2{{declared here}}
+};
+
+int test() {
+ void(*f)(S&&, int, int) = &S::f;
+ f(S{}, 1, 2);
+ f(S{}, 1); // expected-error {{too few arguments to function call, expected 3, have 2}}
+ f(S{}); // expected-error {{too few arguments to function call, expected 3, have 1}}
+ f(S{}, 1, 2, 3); //expected-error {{too many arguments to function call, expected 3, have 4}}
+
+ S{}.f(1, 2);
+ S{}.f(); // expected-error{{no matching member function for call to 'f'}}
+ S{}.g(1,2,3); // expected-error {{no matching member function for call to 'g'}}
+ S{}.h(); // expected-error {{no matching member function for call to 'h'}}
+ S{}.i(); // expected-error {{too few non-object arguments to function call, expected 1, have 0}}
+ S{}.i(1, 2, 3); // expected-error {{too many non-object arguments to function call, expected 1, have 3}}
+}
+
+}
+
+namespace AddressOf {
+
+struct s {
+ static void f(int);
+ void f(this auto &&) {}
+ void g(this s &&) {};
+
+ void test_qual() {
+ using F = void(s&&);
+ F* a = &f; // expected-error {{must explicitly qualify name of member function when taking its address}}
+ F* b = &g; // expected-error {{must explicitly qualify name of member function when taking its address}}
+ F* c = &s::f;
+ F* d = &s::g;
+ }
+};
+
+void test() {
+ using F = void(s&&);
+ F* a = &s::f;
+ F* b = &s::g;
+ a(s{});
+ b(s{});
+}
+
+}
+
+namespace std {
+ struct strong_ordering {
+ int n;
+ constexpr operator int() const { return n; }
+ static const strong_ordering equal, greater, less;
+ };
+ constexpr strong_ordering strong_ordering::equal = {0};
+ constexpr strong_ordering strong_ordering::greater = {1};
+ constexpr strong_ordering strong_ordering::less = {-1};
+}
+
+namespace operators_deduction {
+
+template <typename T, typename U>
+constexpr bool is_same = false;
+
+template <typename T>
+constexpr bool is_same<T, T> = true;
+
+template <template <typename> typename T>
+struct Wrap {
+void f();
+struct S {
+ operator int(this auto&& self) {
+ static_assert(is_same<decltype(self), typename T<S>::type>);
+ return 0;
+ }
+ Wrap* operator->(this auto&& self) {
+ static_assert(is_same<decltype(self), typename T<S>::type>);
+ return new Wrap();
+ }
+ int operator[](this auto&& self, int) {
+ static_assert(is_same<decltype(self), typename T<S>::type>);
+ return 0;
+ }
+ int operator()(this auto&& self, int) {
+ static_assert(is_same<decltype(self), typename T<S>::type>);
+ return 0;
+ }
+ int operator++(this auto&& self, int) {
+ static_assert(is_same<decltype(self), typename T<S>::type>);
+ return 0;
+ }
+ int operator++(this auto&& self) {
+ static_assert(is_same<decltype(self), typename T<S>::type>);
+ return 0;
+ }
+ int operator--(this auto&& self, int) {
+ static_assert(is_same<decltype(self), typename T<S>::type>);
+ return 0;
+ }
+ int operator--(this auto&& self) {
+ static_assert(is_same<decltype(self), typename T<S>::type>);
+ return 0;
+ }
+ int operator*(this auto&& self) {
+ static_assert(is_same<decltype(self), typename T<S>::type>);
+ return 0;
+ }
+ bool operator==(this auto&& self, int) {
+ static_assert(is_same<decltype(self), typename T<S>::type>);
+ return false;
+ }
+ bool operator<=>(this auto&& self, int) {
+ static_assert(is_same<decltype(self), typename T<S>::type>);
+ return false;
+ }
+ bool operator<<(this auto&& self, int b) {
+ static_assert(is_same<decltype(self), typename T<S>::type>);
+ return false;
+ }
+};
+};
+
+template <typename T>
+struct lvalue_reference {
+ using type = T&;
+};
+template <typename T>
+struct const_lvalue_reference {
+ using type = const T&;
+};
+template <typename T>
+struct volatile_lvalue_reference {
+ using type = volatile T&;
+};
+template <typename T>
+struct rvalue_reference {
+ using type = T&&;
+};
+template <typename T>
+struct const_rvalue_reference {
+ using type = const T&&;
+};
+
+
+void test() {
+ {
+ Wrap<lvalue_reference>::S s;
+ s++;
+ s.operator++(0);
+ ++s;
+ s.operator++();
+ s--;
+ s.operator--(0);
+ --s;
+ s.operator--();
+ s[0];
+ s.operator[](0);
+ s(0);
+ s.operator()(0);
+ *s;
+ s.operator*();
+ s->f();
+ s.operator->();
+ int i = s;
+ (void)(s << 0);
+ s.operator<<(0);
+ (void)(s == 0);
+ s.operator==(0);
+ (void)(s <=> 0);
+ s.operator<=>(0);
+ }
+ {
+ const Wrap<const_lvalue_reference>::S s;
+ s++;
+ s.operator++(0);
+ ++s;
+ s.operator++();
+ s--;
+ s.operator--(0);
+ --s;
+ s.operator--();
+ s[0];
+ s.operator[](0);
+ s(0);
+ s.operator()(0);
+ *s;
+ s.operator*();
+ s->f();
+ s.operator->();
+ int i = s;
+ (void)(s << 0);
+ s.operator<<(0);
+ (void)(s == 0);
+ s.operator==(0);
+ (void)(s <=> 0);
+ s.operator<=>(0);
+ }
+ {
+ volatile Wrap<volatile_lvalue_reference>::S s;
+ s++;
+ s.operator++(0);
+ ++s;
+ s.operator++();
+ s--;
+ s.operator--(0);
+ --s;
+ s.operator--();
+ s[0];
+ s.operator[](0);
+ s(0);
+ s.operator()(0);
+ *s;
+ s.operator*();
+ s->f();
+ s.operator->();
+ int i = s;
+ (void)(s << 0);
+ s.operator<<(0);
+ (void)(s == 0);
+ s.operator==(0);
+ (void)(s <=> 0);
+ s.operator<=>(0);
+ }
+ {
+ Wrap<rvalue_reference>::S s;
+ using M = Wrap<rvalue_reference>::S&&;
+ ((M)s)++;
+ ((M)s).operator++(0);
+ ++((M)s);
+ ((M)s).operator++();
+ ((M)s)--;
+ ((M)s).operator--(0);
+ --((M)s);
+ ((M)s).operator--();
+ ((M)s)[0];
+ ((M)s).operator[](0);
+ ((M)s)(0);
+ ((M)s).operator()(0);
+ *((M)s);
+ ((M)s).operator*();
+ ((M)s)->f();
+ ((M)s).operator->();
+ int i = ((M)s);
+ (void)(((M)s) << 0);
+ ((M)s).operator<<(0);
+ (void)(((M)s) == 0);
+ ((M)s).operator==(0);
+ (void)(((M)s) <=> 0);
+ ((M)s).operator<=>(0);
+ }
+}
+}
+
+namespace conversions {
+//[over.best.ics]
+struct Y { Y(int); }; //expected-note 3{{candidate}}
+struct A { operator int(this auto&&); }; //expected-note {{candidate}}
+Y y1 = A(); // expected-error{{no viable conversion from 'A' to 'Y'}}
+
+struct X { X(); }; //expected-note 3{{candidate}}
+struct B { operator X(this auto&&); };
+B b;
+X x{{b}}; // expected-error{{no matching constructor for initialization of 'X'}}
+
+struct T{}; // expected-note 2{{candidate constructor}}
+struct C {
+ operator T (this int); // expected-note {{candidate function not viable: no known conversion from 'C' to 'int' for object argument}}
+ operator int() const; // expected-note {{candidate function}}
+};
+
+void foo(C c) {
+ T d = c; // expected-error {{no viable conversion from 'C' to 'T'}}
+}
+
+}
+
+namespace surrogate {
+using fn_t = void();
+struct C {
+ operator fn_t * (this C const &);
+};
+
+void foo(C c) {
+ c();
+}
+
+}
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index ee9712e..315be01 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -15125,13 +15125,13 @@ and <I>POD class</I></td>
<td><a href="https://cplusplus.github.io/CWG/issues/2553.html">2553</a></td>
<td>ready</td>
<td>Restrictions on explicit object member functions</td>
- <td class="none" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 18</td>
</tr>
<tr class="open" id="2554">
<td><a href="https://cplusplus.github.io/CWG/issues/2554.html">2554</a></td>
<td>review</td>
<td>Overriding virtual functions, also with explicit object parameters</td>
- <td align="center">Not resolved</td>
+ <td class="unreleased" align="center">Clang 18</td>
</tr>
<tr class="open" id="2555">
<td><a href="https://cplusplus.github.io/CWG/issues/2555.html">2555</a></td>
@@ -15173,7 +15173,7 @@ and <I>POD class</I></td>
<td><a href="https://cplusplus.github.io/CWG/issues/2561.html">2561</a></td>
<td>review</td>
<td>Conversion to function pointer for lambda with explicit object parameter</td>
- <td align="center">Not resolved</td>
+ <td class="unreleased" align="center">Clang 18</td>
</tr>
<tr class="open" id="2562">
<td><a href="https://cplusplus.github.io/CWG/issues/2562.html">2562</a></td>
@@ -15725,7 +15725,7 @@ and <I>POD class</I></td>
<td><a href="https://cplusplus.github.io/CWG/issues/2653.html">2653</a></td>
<td>C++23</td>
<td>Can an explicit object parameter have a default argument?</td>
- <td class="none" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 18</td>
</tr>
<tr id="2654">
<td><a href="https://cplusplus.github.io/CWG/issues/2654.html">2654</a></td>
@@ -15929,7 +15929,7 @@ and <I>POD class</I></td>
<td><a href="https://cplusplus.github.io/CWG/issues/2687.html">2687</a></td>
<td>C++23</td>
<td>Calling an explicit object member function via an address-of-overload-set</td>
- <td class="none" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 18</td>
</tr>
<tr class="open" id="2688">
<td><a href="https://cplusplus.github.io/CWG/issues/2688.html">2688</a></td>
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 41acb47..e2cf9ab 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -222,7 +222,7 @@ C++23, informally referred to as C++26.</p>
<tr>
<td rowspan=2>Deducing this</td>
<td><a href="https://wg21.link/P0847R7">P0847R7</a></td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 18</td>
</tr>
<tr>
<td><a href="https://wg21.link/P2797R0">P2797R0</a></td>