diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -91,6 +91,10 @@ C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^ +- Implemented `P0847R7: Deducing this `_. Some related core issues were also + implemented (`CWG2553 `_, `CWG2554 `_, + `CWG2653 `_, `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 --- a/clang/include/clang/AST/ASTLambda.h +++ b/clang/include/clang/AST/ASTLambda.h @@ -35,6 +35,21 @@ return isLambdaCallOperator(cast(DC)); } +inline bool isLambdaCallWithExplicitObjectParameter(const DeclContext *DC) { + return isLambdaCallOperator(DC) && + cast(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(DC)->getType().isNull() && + !cast(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 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1809,6 +1809,18 @@ 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(this)->getDefaultArg(); @@ -1875,7 +1887,10 @@ 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 @@ /// 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 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -2061,6 +2061,17 @@ 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 @@ /// 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()->getMethodQuals(); } diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1449,6 +1449,16 @@ 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 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -383,6 +383,7 @@ 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 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4681,12 +4681,14 @@ // 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 @@ "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 @@ "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_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_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 @@ // 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 @@ "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 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -2674,6 +2674,8 @@ /// 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 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -847,6 +847,8 @@ /// is known. bool AfterParameterList = true; + ParmVarDecl *ExplicitObjectParameter = nullptr; + /// Source range covering the lambda introducer [...]. SourceRange IntroducerRange; @@ -1042,6 +1044,8 @@ void visitPotentialCaptures( llvm::function_ref Callback) const; + + bool lambdaCaptureShouldBeConst() const; }; FunctionScopeInfo::WeakObjectProfileTy::WeakObjectProfileTy() diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2183,20 +2183,17 @@ 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 @@ 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 @@ 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 @@ QualType &ConvertedType); bool IsBlockPointerConversion(QualType FromType, QualType ToType, QualType& ConvertedType); + + bool FunctionParamTypesAreEqual(ArrayRef Old, + ArrayRef New, + unsigned *ArgPos = nullptr, + bool Reversed = false); + bool FunctionParamTypesAreEqual(const FunctionProtoType *OldType, const FunctionProtoType *NewType, unsigned *ArgPos = nullptr, @@ -3896,10 +3904,11 @@ 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 @@ 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 @@ /// 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 @@ 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 @@ StorageClass SC, ArrayRef 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 @@ 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 @@ 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 @@ TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info, bool PartialOverloading, bool AggregateDeductionCandidate, + QualType ObjectType, Expr::Classification ObjectClassification, llvm::function_ref)> CheckNonDependent); TemplateDeductionResult @@ -9220,11 +9246,10 @@ 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 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -4521,6 +4521,8 @@ 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 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -1403,6 +1403,8 @@ 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 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -484,6 +484,10 @@ // - 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 @@ 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 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3652,6 +3652,20 @@ return NumRequiredArgs; } +bool FunctionDecl::hasCXXExplicitFunctionObjectParameter() const { + return getNumParams() != 0 && getParamDecl(0)->isExplicitObjectParameter(); +} + +unsigned FunctionDecl::getNumNonObjectParams() const { + return getNumParams() - + static_cast(hasCXXExplicitFunctionObjectParameter()); +} + +unsigned FunctionDecl::getMinRequiredExplicitArguments() const { + return getMinRequiredArguments() - + static_cast(hasCXXExplicitFunctionObjectParameter()); +} + bool FunctionDecl::hasOneParamOrDefaultArgs() const { return getNumParams() == 1 || (getNumParams() > 1 && diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -838,7 +838,7 @@ SMKind |= SMF_CopyAssignment; const auto *ParamTy = - Method->getParamDecl(0)->getType()->getAs(); + Method->getNonObjectParameter(0)->getType()->getAs(); if (!ParamTy || ParamTy->getPointeeType().isConstQualified()) data().HasDeclaredCopyAssignmentWithConstParam = true; } @@ -2409,6 +2409,17 @@ 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 @@ // 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()) ParamType = Ref->getPointeeType(); @@ -2437,10 +2449,10 @@ // 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 @@ : 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 @@ getParent()); } -QualType CXXMethodDecl::getThisObjectType() const { - // Ditto getThisType. - assert(isInstance() && "No 'this' for static methods!"); - return CXXMethodDecl::getThisObjectType( - getType()->castAs(), getParent()); +QualType CXXMethodDecl::getFunctionObjectParameterReferenceType() const { + if (isExplicitObjectMemberFunction()) + return parameters()[0]->getType(); + + ASTContext &C = getParentASTContext(); + const FunctionProtoType *FPT = getType()->castAs(); + 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 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -962,6 +962,10 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { prettyPrintPragmas(D); + if (const auto *Param = dyn_cast(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 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -491,6 +491,7 @@ DeclRefExprBits.HadMultipleCandidates = false; DeclRefExprBits.RefersToEnclosingVariableOrCapture = RefersToEnclosingVariableOrCapture; + DeclRefExprBits.CapturedByCopyInLambdaWithExplicitObjectParameter = false; DeclRefExprBits.NonOdrUseReason = NOUR; DeclRefExprBits.IsImmediateEscalating = false; DeclRefExprBits.Loc = L; @@ -518,6 +519,7 @@ = (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 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -465,8 +465,13 @@ // lvalue unless it's a reference type (C++ [temp.param]p6), so we need to // special-case this. - if (isa(D) && cast(D)->isInstance()) - return Cl::CL_MemberFunction; + if (const auto *M = dyn_cast(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(D)) @@ -551,8 +556,13 @@ // -- 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(Member)) - return Method->isStatic() ? Cl::CL_LValue : Cl::CL_MemberFunction; + if (const auto *Method = dyn_cast(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 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1945,9 +1945,9 @@ /// Produce a string describing the given constexpr call. void CallStackFrame::describe(raw_ostream &Out) const { unsigned ArgIndex = 0; - bool IsMemberCall = isa(Callee) && - !isa(Callee) && - cast(Callee)->isInstance(); + bool IsMemberCall = + isa(Callee) && !isa(Callee) && + cast(Callee)->isImplicitObjectMemberFunction(); if (!IsMemberCall) Callee->getNameForDiagnostic(Out, Info.Ctx.getPrintingPolicy(), @@ -4711,6 +4711,9 @@ 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 @@ if (OCE && OCE->isAssignmentOp()) { assert(Args.size() == 2 && "wrong number of arguments in assignment"); Call = Info.CurrentCall->createCall(FD); - if (!EvaluateArgs(isa(FD) ? Args.slice(1) : Args, Call, - Info, FD, /*RightToLeft=*/true)) + bool HasThis = false; + if (const auto *MD = dyn_cast(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(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 @@ 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 @@ #ifndef NDEBUG auto *MD = dyn_cast(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 @@ 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 --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -44,7 +44,7 @@ // InterpStack when calling the function. bool HasThisPointer = false; if (const auto *MD = dyn_cast(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 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -1721,7 +1721,7 @@ // If we have a member function, we need to include the 'this' pointer. if (const auto *MD = dyn_cast(ND)) - if (!MD->isStatic()) + if (MD->isImplicitObjectMemberFunction()) Arity++; } [[fallthrough]]; @@ -1781,6 +1781,8 @@ 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 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -876,6 +876,9 @@ void JSONNodeDumper::VisitVarDecl(const VarDecl *VD) { VisitNamedDecl(VD); JOS.attribute("type", createQualType(VD->getType())); + if (const auto *P = dyn_cast(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 --- a/clang/lib/AST/Mangle.cpp +++ b/clang/lib/AST/Mangle.cpp @@ -225,7 +225,7 @@ assert(!Proto->isVariadic()); unsigned ArgWords = 0; if (const CXXMethodDecl *MD = dyn_cast(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 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -2636,7 +2636,7 @@ if (const CXXMethodDecl *MD = dyn_cast_or_null(D)) { if (MD->getParent()->isLambda()) IsInLambda = true; - if (MD->isInstance()) + if (MD->isImplicitObjectMemberFunction()) HasThisQuals = true; if (isa(MD)) { IsStructor = true; @@ -2745,6 +2745,10 @@ } 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 @@ 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 @@ Out << 'A'; break; case AS_protected: - if (MD->isStatic()) + if (!MD->isImplicitObjectMemberFunction()) Out << 'K'; else if (IsVirtual) Out << 'M'; @@ -2826,7 +2830,7 @@ 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 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1949,6 +1949,10 @@ void TextNodeDumper::VisitVarDecl(const VarDecl *D) { dumpNestedNameSpecifier(D->getQualifier()); dumpName(D); + if (const auto *P = dyn_cast(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 --- a/clang/lib/Analysis/Consumed.cpp +++ b/clang/lib/Analysis/Consumed.cpp @@ -771,7 +771,7 @@ 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 @@ const FunctionDecl *D) { QualType ReturnType; if (const auto *Constructor = dyn_cast(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 --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -452,8 +452,8 @@ MethodDecl = dyn_cast(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(createValue(ThisPointeeType))->getLoc(); } diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -525,7 +525,9 @@ // 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 --- a/clang/lib/Analysis/ThreadSafetyCommon.cpp +++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -154,7 +154,9 @@ // If the attribute has no arguments, then assume the argument is "this". if (!AttrExp) return CapabilityExpr( - Self, ClassifyDiagnostic(cast(D)->getThisObjectType()), + Self, + ClassifyDiagnostic( + cast(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 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -298,7 +298,7 @@ setCUDAKernelCallingConvention(FT, CGM, MD); auto prototype = FT.getAs(); - 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 @@ const CGFunctionInfo & CodeGenTypes::arrangeFunctionDeclaration(const FunctionDecl *FD) { if (const CXXMethodDecl *MD = dyn_cast(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 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -138,7 +138,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -2130,14 +2130,14 @@ // attribute, i.e. that value is not available at the host side. if (!CGM.getLangOpts().CUDA || CGM.getLangOpts().CUDAIsDevice || !D->hasAttr()) { - const CXXMethodDecl *MD; // Variable pointer template parameters have a value that is the address // of the variable. if (const auto *VD = dyn_cast(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(D)) && MD->isInstance()) + else if (const auto *MD = dyn_cast(D); + MD && MD->isImplicitObjectMemberFunction()) V = CGM.getCXXABI().EmitMemberFunctionPointer(MD); else if (const auto *FD = dyn_cast(D)) V = CGM.GetAddrOfFunction(FD); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -2647,9 +2647,8 @@ 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 @@ /// 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(CurCodeDecl)->getParent()->isLambda()); - assert(cast(CurCodeDecl)->getParent() == Field->getParent()); +/// +LValue CodeGenFunction::EmitLValueForLambdaField(const FieldDecl *Field, + llvm::Value *ThisValue) { + bool HasExplicitObjectParameter = false; + if (const auto *MD = dyn_cast_if_present(CurCodeDecl)) { + HasExplicitObjectParameter = MD->isExplicitObjectMemberFunction(); + assert(MD->getParent()->isLambda()); + assert(MD->getParent() == Field->getParent()); + } + LValue LambdaLV; + if (HasExplicitObjectParameter) { + const VarDecl *D = cast(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 @@ if (const auto *CE = dyn_cast(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(E)) - if (const CXXMethodDecl *MD = - dyn_cast_or_null(CE->getCalleeDecl())) + if (const auto *MD = + dyn_cast_if_present(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 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -41,7 +41,7 @@ assert(CE == nullptr || isa(CE) || isa(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 @@ Args.addFrom(*RtlArgs); } else if (CE) { // Special case: skip first argument of CXXOperatorCall (it is "this"). - unsigned ArgsToSkip = isa(CE) ? 1 : 0; + unsigned ArgsToSkip = 0; + if (const auto *Op = dyn_cast(CE)) { + if (const auto *M = dyn_cast(Op->getCalleeDecl())) + ArgsToSkip = + static_cast(!M->isExplicitObjectMemberFunction()); + } CGF.EmitCallArgs(Args, FPT, drop_begin(CE->arguments(), ArgsToSkip), CE->getDirectCallee()); } else { @@ -484,7 +489,7 @@ 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 --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp @@ -8260,7 +8260,7 @@ // 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 --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -201,7 +201,7 @@ // 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.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4017,6 +4017,8 @@ 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/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1167,12 +1167,13 @@ EmitFunctionProlog(*CurFnInfo, CurFn, Args); - if (isa_and_nonnull(D) && - cast(D)->isInstance()) { - CGM.getCXXABI().EmitInstanceFunctionProlog(*this); - const CXXMethodDecl *MD = cast(D); - if (MD->getParent()->isLambda() && - MD->getOverloadedOperator() == OO_Call) { + if (const CXXMethodDecl *MD = dyn_cast_if_present(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 @@ 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 ResTy = FD->getReturnType(); const CXXMethodDecl *MD = dyn_cast(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/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2246,8 +2246,8 @@ // Only functions whose address can be taken with a member function pointer // need this sort of type metadata. - return !MD->isStatic() && !MD->isVirtual() && !isa(MD) && - !isa(MD); + return MD->isImplicitObjectMemberFunction() && !MD->isVirtual() && + !isa(MD); } SmallVector diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -5835,8 +5835,7 @@ 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 @@ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, /*ObjectHasErrors=*/false, /*EnteringContext=*/true)) { - TPA.Revert(); return false; } @@ -5857,7 +5855,6 @@ } else if (Tok.is(tok::annot_template_id)) { ConsumeAnnotationToken(); } else { - TPA.Revert(); return false; } @@ -5867,7 +5864,6 @@ // 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 @@ // 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 @@ if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier(/*Disambiguate*/ false, /*OuterMightBeMessageSend*/ true)) { - TPA.Revert(); return true; } @@ -5906,9 +5900,17 @@ // 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 @@ break; } } - - TPA.Revert(); return IsConstructor; } @@ -7356,6 +7356,7 @@ /// '=' 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 @@ 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 @@ : DeclaratorContext::Prototype); ParseDeclarator(ParmDeclarator); + if (ThisLoc.isValid()) + ParmDeclarator.SetRangeBegin(ThisLoc); + // Parse GNU attributes, if present. MaybeParseGNUAttributes(ParmDeclarator); if (getLangOpts().HLSL) @@ -7505,7 +7516,8 @@ } // 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 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1571,6 +1571,17 @@ 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 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -420,6 +420,18 @@ getName().OperatorFunctionId.Operator)); } +bool Declarator::isExplicitObjectMemberFunction() { + if (!isFunctionDeclarator()) + return false; + DeclaratorChunk::FunctionTypeInfo &Fun = getFunctionTypeInfo(); + if (Fun.NumParams) { + auto *P = dyn_cast_or_null(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 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -175,8 +175,8 @@ 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 @@ auto FnProtoLoc = TSInfo->getTypeLoc().getAs(); 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 --- a/clang/lib/Sema/ScopeInfo.cpp +++ b/clang/lib/Sema/ScopeInfo.cpp @@ -248,6 +248,14 @@ } } +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 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -126,7 +126,7 @@ 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 @@ 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 @@ 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 @@ // 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 @@ 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 @@ Proto->isVariadic() ? VariadicConstructor : VariadicDoesNotApply; auto *Ctor = cast(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 @@ 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(TheCall)->getImplicitObjectArgument(); @@ -6965,8 +6968,8 @@ ThisType = Context.getPointerType(ThisType); } - QualType ThisTypeFromDecl = - Context.getPointerType(cast(FDecl)->getThisObjectType()); + QualType ThisTypeFromDecl = Context.getPointerType( + cast(FDecl)->getFunctionObjectParameterType()); CheckArgAlignment(TheCall->getRParenLoc(), FDecl, "'this'", ThisType, ThisTypeFromDecl); @@ -7293,13 +7296,13 @@ if (Args.size() < AdjustedNumArgs) { Diag(CallRange.getEnd(), diag::err_typecheck_call_too_few_args) << 0 << AdjustedNumArgs << static_cast(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(Args.size()) - << ExprRange; + << /*is non object*/ 0 << ExprRange; return ExprError(); } @@ -7662,7 +7665,8 @@ 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 @@ // 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 @@ // 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 @@ 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 @@ 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 @@ 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 @@ 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 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -70,7 +70,7 @@ // 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(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 @@ // 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 @@ assert(isa(CurContext) && "not in a function scope"); auto *FD = cast(CurContext); bool IsThisDependentType = [&] { - if (auto *MD = dyn_cast_or_null(FD)) - return MD->isInstance() && MD->getThisObjectType()->isDependentType(); - else - return false; + if (const auto *MD = dyn_cast_if_present(FD)) + return MD->isImplicitObjectMemberFunction() && + MD->getThisType()->isDependentType(); + return false; }(); QualType T = FD->getType()->isDependentType() || IsThisDependentType @@ -592,7 +592,7 @@ // Add implicit object parameter. if (auto *MD = dyn_cast(FD)) { - if (MD->isInstance() && !isLambdaCallOperator(MD)) { + if (MD->isImplicitObjectMemberFunction() && !isLambdaCallOperator(MD)) { ExprResult ThisExpr = ActOnCXXThis(Loc); if (ThisExpr.isInvalid()) return nullptr; @@ -1367,7 +1367,7 @@ static bool collectPlacementArgs(Sema &S, FunctionDecl &FD, SourceLocation Loc, SmallVectorImpl &PlacementArgs) { if (auto *MD = dyn_cast(&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 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -8924,13 +8924,11 @@ CXXMethodDecl *BaseMD = dyn_cast(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 @@ } 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 @@ << 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 @@ // struct B { struct Y { ~Y(); }; using X = Y; }; // template struct A; 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 @@ } } +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 @@ 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 @@ 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 @@ 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 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1234,7 +1234,7 @@ 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()) { @@ -1336,23 +1336,23 @@ // 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(D)) { + // if (const ParmVarDecl *Param = dyn_cast(D)) { // ReturnType = Param->getType(); // //} else if (const CXXConstructorDecl *Constructor = // dyn_cast(D)) { - // ReturnType = Constructor->getThisObjectType(); + // ReturnType = Constructor->getFunctionObjectParameterType(); // //} else { // // ReturnType = cast(D)->getCallResultType(); //} // - //const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl(); + // const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl(); // - //if (!RD || !RD->hasAttr()) { + // if (!RD || !RD->hasAttr()) { // 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 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -7692,7 +7692,7 @@ 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 @@ 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 @@ } // 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 @@ } // 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 @@ 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 @@ ExprPair getCompleteObject() { unsigned Param = 0; ExprResult LHS; - if (isa(FD)) { + if (const auto *MD = dyn_cast(FD); + MD && MD->isImplicitObjectMemberFunction()) { // LHS is '*this'. LHS = S.ActOnCXXThis(Loc); if (!LHS.isInvalid()) @@ -8798,15 +8804,22 @@ // 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 @@ } } - 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 @@ const ParmVarDecl *KnownParm = nullptr; for (const ParmVarDecl *Param : FD->parameters()) { + if (Param->isExplicitObjectParameter()) + continue; QualType ParmTy = Param->getType(); if (!KnownParm) { @@ -9223,9 +9240,9 @@ llvm_unreachable("invalid special member kind"); } - if (MD->getNumParams()) { + if (MD->getNumExplicitParams()) { if (const ReferenceType *RT = - MD->getParamDecl(0)->getType()->getAs()) + MD->getNonObjectParameter(0)->getType()->getAs()) ConstArg = RT->getPointeeType().isConstQualified(); } } @@ -10105,7 +10122,7 @@ case CXXCopyConstructor: case CXXCopyAssignment: { - const ParmVarDecl *Param0 = MD->getParamDecl(0); + const ParmVarDecl *Param0 = MD->getNonObjectParameter(0); const ReferenceType *RT = Param0->getType()->getAs(); // When ClangABICompat14 is true, CXX copy constructors will only be trivial @@ -10136,7 +10153,7 @@ 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(); if (!RT || RT->getPointeeType().getCVRQualifiers()) { @@ -11102,15 +11119,25 @@ << SourceRange(D.getIdentifierLoc()) << 0; D.setInvalidType(); } - const auto *Proto = R->castAs(); - // 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(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 @@ 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(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 @@ SmallVector 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()) { - OtherRefType = OtherRef->getPointeeType(); + if (OtherRefType->isLValueReferenceType()) { + OtherRefType = OtherRefType->getPointeeType(); OtherQuals = OtherRefType.getQualifiers(); } @@ -14879,8 +14986,26 @@ // Builds a DeclRefExpr for the "other" object. RefBuilder OtherRef(Other, OtherRefType); - // Builds the "this" pointer. - ThisBuilder This; + // Builds the function object parameter. + std::optional This; + std::optional DerefThis; + std::optional 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(*ExplicitObject) + : static_cast(*This); // Assign base classes. bool Invalid = false; @@ -14902,11 +15027,11 @@ VK_LValue, BasePath); // Dereference "this". - DerefBuilder DerefThis(This); - CastBuilder To(DerefThis, - Context.getQualifiedType( - BaseType, CopyAssignOperator->getMethodQualifiers()), - VK_LValue, BasePath); + CastBuilder To( + ExplicitObject ? static_cast(*ExplicitObject) + : static_cast(*DerefThis), + Context.getQualifiedType(BaseType, ObjectType.getQualifiers()), + VK_LValue, BasePath); // Build the copy. StmtResult Copy = buildSingleCopyAssign(*this, Loc, BaseType, @@ -14972,10 +15097,7 @@ 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 @@ 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(*ExplicitObject) + : LangOpts.HLSL ? static_cast(*This) + : static_cast(*DerefThis)) + .build(*this, Loc); StmtResult Return = BuildReturnStmt(Loc, ThisExpr); if (Return.isInvalid()) Invalid = true; @@ -15231,7 +15349,7 @@ SmallVector 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()->getPointeeType(); @@ -15245,8 +15363,23 @@ // Cast to rvalue. MoveCastBuilder MoveOther(OtherRef); - // Builds the "this" pointer. - ThisBuilder This; + // Builds the function object parameter. + std::optional This; + std::optional DerefThis; + std::optional 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(*This); // Assign base classes. bool Invalid = false; @@ -15274,14 +15407,13 @@ // 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(*ExplicitObject) + : static_cast(*DerefThis), + Context.getQualifiedType(BaseType, ObjectType.getQualifiers()), + VK_LValue, BasePath); // Build the move. StmtResult Move = buildSingleCopyAssign(*this, Loc, BaseType, @@ -15346,8 +15478,8 @@ 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 @@ if (!Invalid) { // Add a "return *this;" - ExprResult ThisObj = - CreateBuiltinUnaryOp(Loc, UO_Deref, This.build(*this, Loc)); + Expr *ThisExpr = + (ExplicitObject ? static_cast(*ExplicitObject) + : static_cast(*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 @@ 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 @@ // [...] 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(FnDecl)? 1 : 0); + unsigned NumParams = FnDecl->getNumParams() + + (isa(FnDecl) && + !FnDecl->hasCXXExplicitFunctionObjectParameter() + ? 1 + : 0); if (Op != OO_Call && Op != OO_Subscript && ((NumParams == 1 && !CanBeUnaryOperator) || (NumParams == 2 && !CanBeBinaryOperator) || (NumParams < 1) || @@ -18106,6 +18245,20 @@ 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()->getReturnType(); diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -769,14 +769,12 @@ /// 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 @@ // 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 @@ } } // 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 @@ /// 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 @@ // 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 @@ // 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 @@ 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(), - Old->getLocation(), - New->getType()->castAs(), - 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(), + Old->hasCXXExplicitFunctionObjectParameter(), Old->getLocation(), + New->getType()->castAs(), + 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 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2444,7 +2444,7 @@ // 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 @@ FromRecordType = FromType; } } else if (const auto *Method = dyn_cast(Member)) { - if (Method->isStatic()) + if (!Method->isImplicitObjectMemberFunction()) return From; DestType = Method->getThisType().getNonReferenceType(); - DestRecordType = Method->getThisObjectType(); + DestRecordType = Method->getFunctionObjectParameterType(); if (FromType->getAs()) { FromRecordType = FromType->getPointeeType(); @@ -6503,6 +6503,9 @@ // 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 @@ 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(Args.size()) - << TC.getCorrectionRange()); - } else if (MinArgs == 1 && FDecl && FDecl->getParamDecl(0)->getDeclName()) + diagnoseTypo( + TC, PDiag(diag_id) + << FnKind << MinArgs - ExplicitObjectParameterOffset + << static_cast(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(Args.size()) - << Fn->getSourceRange(); + << FnKind << MinArgs - ExplicitObjectParameterOffset + << static_cast(Args.size()) - + ExplicitObjectParameterOffset + << HasExplicitObjectParameter << Fn->getSourceRange(); // Emit the location of the prototype. if (!TC && FDecl && !FDecl->getBuiltinID() && !IsExecConfig) @@ -6560,17 +6571,23 @@ 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(Args.size()) - << TC.getCorrectionRange()); - } else if (NumParams == 1 && FDecl && - FDecl->getParamDecl(0)->getDeclName()) + diagnoseTypo( + TC, PDiag(diag_id) + << FnKind << NumParams - ExplicitObjectParameterOffset + << static_cast(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(Args.size()) << Fn->getSourceRange() + << FnKind << FDecl->getParamDecl(ExplicitObjectParameterOffset) + << static_cast(Args.size()) - + ExplicitObjectParameterOffset + << HasExplicitObjectParameter << Fn->getSourceRange() << SourceRange(Args[NumParams]->getBeginLoc(), Args.back()->getEndLoc()); else @@ -6578,8 +6595,10 @@ MinArgs == NumParams ? diag::err_typecheck_call_too_many_args : diag::err_typecheck_call_too_many_args_at_most) - << FnKind << NumParams << static_cast(Args.size()) - << Fn->getSourceRange() + << FnKind << NumParams - ExplicitObjectParameterOffset + << static_cast(Args.size()) - + ExplicitObjectParameterOffset + << HasExplicitObjectParameter << Fn->getSourceRange() << SourceRange(Args[NumParams]->getBeginLoc(), Args.back()->getEndLoc()); @@ -7661,9 +7680,9 @@ } if (CXXMethodDecl *Method = dyn_cast_or_null(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 @@ 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(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(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 @@ DeclRefExpr *DRE = cast(op); CXXMethodDecl *MD = cast(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(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 @@ << op->getType() << op->getSourceRange(); return QualType(); } + } else if (const auto *DRE = dyn_cast(op)) { + if (const auto *MD = dyn_cast_or_null(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 @@ if (isa(VD) || isa(VD)) return true; if (CXXMethodDecl *Method = dyn_cast(VD)) - return Method->isInstance(); + return Method->isImplicitObjectMemberFunction(); return false; } @@ -16507,7 +16537,7 @@ for (NamedDecl *D : ULE->decls()) { if (CXXMethodDecl *Method = dyn_cast(D)) { - if (Method->isInstance()) + if (Method->isImplicitObjectMemberFunction()) return true; } else { // Overload set does not contain methods. @@ -19227,7 +19257,8 @@ // private instances of the captured declarations. const Capture &Cap = CSI->getCapture(Var); if (Cap.isCopyCapture() && - !(isa(CSI) && cast(CSI)->Mutable) && + !(isa(CSI) && + !cast(CSI)->lambdaCaptureShouldBeConst()) && !(isa(CSI) && cast(CSI)->CapRegionKind == CR_OpenMP)) DeclRefType.addConst(); @@ -19544,7 +19575,8 @@ // 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 @@ 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(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 @@ if (VarDecl *Var = dyn_cast(D)) { DoMarkVarDeclReferenced(SemaRef, Loc, Var, E, RefsMinusAssignments); + if (SemaRef.getLangOpts().CPlusPlus) + FixDependencyOfIdExpressionsInLambdaWithDependentObjectParameter(SemaRef, + Var, E); return; } if (BindingDecl *Decl = dyn_cast(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 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1160,7 +1160,7 @@ auto C = CurLSI->getCXXThisCapture(); if (C.isCopyCapture()) { - if (!CurLSI->Mutable) + if (CurLSI->lambdaCaptureShouldBeConst()) ClassType.addConst(); return ASTCtx.getPointerType(ClassType); } @@ -1216,11 +1216,11 @@ QualType ThisTy = CXXThisTypeOverride; if (CXXMethodDecl *method = dyn_cast(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(DC)) { // This is a lambda call operator that is being instantiated as a default @@ -1398,10 +1398,22 @@ /// 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(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 @@ 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 @@ 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 @@ 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(SubE); - if (CE && CE->getCastKind() == CK_NoOp) - SubE = CE->getSubExpr(); - SubE = SubE->IgnoreParens(); - if (CXXBindTemporaryExpr *BE = dyn_cast(SubE)) - SubE = BE->getSubExpr(); - if (isa(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())) - 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 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -46,7 +46,7 @@ /// 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 @@ /// 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 @@ /// 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 @@ DeclContext *DC = SemaRef.getFunctionLevelDeclContext(); - bool isStaticContext = SemaRef.CXXThisTypeOverride.isNull() && - (!isa(DC) || cast(DC)->isStatic()); + bool isStaticOrExplicitContext = + SemaRef.CXXThisTypeOverride.isNull() && + (!isa(DC) || cast(DC)->isStatic() || + cast(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 @@ // 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 @@ 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 @@ CXXRecordDecl *RepClass = dyn_cast(Rep->getDeclContext()); bool InStaticMethod = Method && Method->isStatic(); + bool InExplicitObjectMethod = + Method && Method->isExplicitObjectMemberFunction(); bool IsField = isa(Rep) || isa(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 @@ 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(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 @@ [[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 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -1800,7 +1800,8 @@ // FIXME. This need be cleaned up. if (Args.size() < NumNamedArgs) { Diag(SelLoc, diag::err_typecheck_call_too_few_args) - << 2 << NumNamedArgs << static_cast(Args.size()); + << 2 << NumNamedArgs << static_cast(Args.size()) + << /*is non object*/ 0; return false; } @@ -1898,7 +1899,7 @@ Diag(Args[NumNamedArgs]->getBeginLoc(), diag::err_typecheck_call_too_many_args) << 2 /*method*/ << NumNamedArgs << static_cast(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 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -7421,8 +7421,9 @@ return true; if (!isInStlNamespace(Callee->getParent())) return false; - if (!isRecordWithAttr(Callee->getThisObjectType()) && - !isRecordWithAttr(Callee->getThisObjectType())) + if (!isRecordWithAttr( + Callee->getFunctionObjectParameterType()) && + !isRecordWithAttr(Callee->getFunctionObjectParameterType())) return false; if (Callee->getReturnType()->isPointerType() || isRecordWithAttr(Callee->getReturnType())) { @@ -7557,7 +7558,7 @@ QualType LHST; auto *MD = dyn_cast(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 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -379,6 +379,38 @@ 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 NumberingOverride) { @@ -860,9 +892,17 @@ 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 @@ // 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 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -910,6 +910,9 @@ 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 @@ } // 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(); @@ -1235,9 +1238,11 @@ 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 @@ 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 @@ // 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 @@ Old->isMemberLikeConstrainedFriend()) && !New->getLexicalDeclContext()->Equals(Old->getLexicalDeclContext())) return true; + const auto *OldMethod = dyn_cast(Old); + const auto *NewMethod = dyn_cast(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(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 @@ // // 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 @@ 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(Old); - CXXMethodDecl *NewMethod = dyn_cast(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(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 @@ 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(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 @@ 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 @@ // fact that non-static member functions *must* have such an address-of // expression. CXXMethodDecl *Method = dyn_cast(Fn); - if (Method && !Method->isStatic()) { + if (Method && !Method->isStatic() && + !Method->isExplicitObjectMemberFunction()) { assert(isa(From->IgnoreParens()) && "Non-unary operator on non-static member address"); assert(cast(From->IgnoreParens())->getOpcode() @@ -3104,30 +3203,40 @@ /// 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 Old, + ArrayRef 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 @@ case OR_Success: { // Record the standard conversion we used and the conversion function. CXXConstructorDecl *Constructor = cast(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 @@ 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 @@ /// 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()) { + 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 @@ // to exit early. ImplicitConversionSequence ICS; - // We need to have an object of class type. - if (const PointerType *PT = FromType->getAs()) { - 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 @@ 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 @@ /// 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()) { @@ -6134,6 +6264,64 @@ return ExprResult(); } +static QualType GetExplicitObjectType(Sema &S, const Expr *MemExprE) { + const Expr *Base = nullptr; + assert((isa(MemExprE)) && + "expected a member expression"); + + if (const auto M = dyn_cast(MemExprE); + M && !M->isImplicitAccess()) + Base = M->getBase(); + else if (const auto M = dyn_cast(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 &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 @@ assert(!isa(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 @@ 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 @@ // (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 @@ // 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 @@ // 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 @@ ConversionSequenceList Conversions; if (TemplateDeductionResult Result = DeduceTemplateArguments( MethodTmpl, ExplicitTemplateArgs, Args, Specialization, Info, - PartialOverloading, /*AggregateDeductionCandidate=*/false, - [&](ArrayRef ParamTypes) { + PartialOverloading, /*AggregateDeductionCandidate=*/false, ObjectType, + ObjectClassification, [&](ArrayRef ParamTypes) { return CheckNonDependentConversions( MethodTmpl, ParamTypes, Args, CandidateSet, Conversions, SuppressUserConversions, ActingContext, ObjectType, @@ -7405,6 +7594,8 @@ if (TemplateDeductionResult Result = DeduceTemplateArguments( FunctionTemplate, ExplicitTemplateArgs, Args, Specialization, Info, PartialOverloading, AggregateCandidateDeduction, + /*ObjectType=*/QualType(), + /*ObjectClassification=*/Expr::Classification(), [&](ArrayRef ParamTypes) { return CheckNonDependentConversions( FunctionTemplate, ParamTypes, Args, CandidateSet, Conversions, @@ -7477,16 +7668,24 @@ if (HasThisConversion && !cast(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 @@ // // Determine the implicit conversion sequence for the implicit // object parameter. - QualType ImplicitParamType = From->getType(); - if (const PointerType *FromPtrType = ImplicitParamType->getAs()) - ImplicitParamType = FromPtrType->getPointeeType(); - CXXRecordDecl *ConversionContext - = cast(ImplicitParamType->castAs()->getDecl()); - + QualType ObjectType = From->getType(); + if (const auto *FromPtrType = ObjectType->getAs()) + ObjectType = FromPtrType->getPointeeType(); + const auto *ConversionContext = + cast(ObjectType->castAs()->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 @@ 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 @@ // 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 @@ // 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 @@ // TODO: treat calls to a missing default constructor as a special case const auto *FnTy = Fn->getType()->castAs(); - 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 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 @@ = dyn_cast(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 @@ if (CXXMethodDecl *Method = dyn_cast(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 @@ // 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 @@ 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 @@ // 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 @@ 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(SubE); + if (CE && CE->getCastKind() == CK_NoOp) + SubE = CE->getSubExpr(); + SubE = SubE->IgnoreParens(); + if (auto *BE = dyn_cast(SubE)) + SubE = BE->getSubExpr(); + if (isa(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())) + return ExprError(); + + return CheckForImmediateInvocation(CE, CE->getDirectCallee()); +} + /// Create a unary operation that may resolve to an overloaded /// operator. /// @@ -13786,14 +14093,17 @@ // Convert the arguments. if (CXXMethodDecl *Method = dyn_cast(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 @@ if (auto *MD = dyn_cast(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 @@ // 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(); Args[1] = RHS = Arg1.getAs(); } else { @@ -14198,22 +14514,27 @@ ExprValueKind VK = Expr::getValueKindForType(ResultTy); ResultTy = ResultTy.getNonLValueExprType(Context); - CXXOperatorCallExpr *TheCall = CXXOperatorCallExpr::Create( + CallExpr *TheCall; + ArrayRef 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 ArgsArray(Args, 2); - const Expr *ImplicitThis = nullptr; - // Cut off the implicit 'this'. - if (isa(FnDecl)) { + if (const auto *Method = dyn_cast(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 @@ if (ImplicitThis) { QualType ThisType = Context.getPointerType(ImplicitThis->getType()); QualType ThisTypeFromDecl = Context.getPointerType( - cast(FnDecl)->getThisObjectType()); + cast(FnDecl)->getFunctionObjectParameterType()); CheckArgAlignment(OpLoc, FnDecl, "'this'", ThisType, ThisTypeFromDecl); @@ -14600,8 +14921,15 @@ SmallVector 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 @@ MemberExpr *MemExpr; CXXMethodDecl *Method = nullptr; + bool HadMultipleCandidates = false; DeclAccessPair FoundDecl = DeclAccessPair::make(nullptr, AS_public); NestedNameSpecifier *Qualifier = nullptr; if (isa(NakedMemExpr)) { @@ -14834,11 +15163,24 @@ for (UnresolvedMemberExpr::decls_iterator I = UnresExpr->decls_begin(), E = UnresExpr->decls_end(); I != E; ++I) { + QualType ExplicitObjectType = ObjectType; + NamedDecl *Func = *I; CXXRecordDecl *ActingDC = cast(Func->getDeclContext()); if (isa(Func)) Func = cast(Func)->getTargetDecl(); + bool HasExplicitParameter = false; + if (const auto *M = dyn_cast(Func); + M && M->hasCXXExplicitFunctionObjectParameter()) + HasExplicitParameter = true; + else if (const auto *M = dyn_cast(Func); + M && + M->getTemplatedDecl()->hasCXXExplicitFunctionObjectParameter()) + HasExplicitParameter = true; + + if (HasExplicitParameter) + ExplicitObjectType = GetExplicitObjectType(*this, UnresExpr); // Microsoft supports direct constructor calls. if (getLangOpts().MicrosoftExt && isa(Func)) { @@ -14851,17 +15193,20 @@ if (TemplateArgs) continue; - AddMethodCandidate(Method, I.getPair(), ActingDC, ObjectType, + AddMethodCandidate(Method, I.getPair(), ActingDC, ExplicitObjectType, ObjectClassification, Args, CandidateSet, /*SuppressUserConversions=*/false); } else { - AddMethodTemplateCandidate( - cast(Func), I.getPair(), ActingDC, - TemplateArgs, ObjectType, ObjectClassification, Args, CandidateSet, - /*SuppressUserConversions=*/false); + AddMethodTemplateCandidate(cast(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 @@ 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 @@ assert(Method && "Member call to something that isn't a method?"); const auto *Proto = Method->getType()->castAs(); - 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 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 @@ } } - if ((isa(CurContext) || - isa(CurContext)) && - TheCall->getMethodDecl()->isPure()) { - const CXXMethodDecl *MD = TheCall->getMethodDecl(); + if (isa(CurContext) && + TheCall->getDirectCallee()->isPure()) { + const FunctionDecl *MD = TheCall->getDirectCallee(); if (isa(MemExpr->getBase()->IgnoreParenCasts()) && MemExpr->performsVirtualDispatch(getLangOpts())) { @@ -14999,8 +15361,7 @@ } } - if (CXXDestructorDecl *DD = - dyn_cast(TheCall->getMethodDecl())) { + if (auto *DD = dyn_cast(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 @@ } return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), - TheCall->getMethodDecl()); + TheCall->getDirectCallee()); } /// BuildCallToObjectOfClassType - Build a call to an object of class @@ -15234,8 +15595,13 @@ // 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 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 @@ // Convert the object parameter. CXXMethodDecl *Method = cast(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 @@ 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 @@ /// 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(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(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(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 @@ ArrayRef A = GSE->getAssocExprs(); SmallVector 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 @@ // 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(SubExpr) - && "fixed to something other than a decl ref"); - assert(cast(SubExpr)->getQualifier() - && "fixed to a member ref with no nested name qualifier"); + if (CheckUseOfCXXMethodAsAddressOfOperand(UnOp->getBeginLoc(), + SubExpr.get(), Method)) + return ExprError(); + + assert(isa(SubExpr.get()) && + "fixed to something other than a decl ref"); + assert(cast(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 @@ 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(E)) { diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -689,7 +689,7 @@ 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(); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -753,7 +753,8 @@ IsEnum = isa_and_nonnull(NNS->getAsType()); if (!MightBeCxx11UnevalField && !isAddressOfOperand && !IsEnum && - isa(DC) && cast(DC)->isInstance()) { + isa(DC) && + cast(DC)->isImplicitObjectMemberFunction()) { QualType ThisType = cast(DC)->getThisType().getNonReferenceType(); // Since the 'this' expression is synthesized, we don't need to @@ -7079,7 +7080,8 @@ isa(DRE->getDecl())) { assert((isa(DRE->getDecl()) || isa(DRE->getDecl()) || - !cast(DRE->getDecl())->isStatic()) && + cast(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 @@ 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 @@ 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 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -1905,11 +1905,11 @@ 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 @@ 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 @@ 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 @@ return {}; if (CXXMethodDecl *Method = dyn_cast(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 @@ /// 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 @@ // 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 @@ 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 @@ 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 &Deduced, SmallVectorImpl &OriginalCallArgs, bool DecomposedParam, unsigned ArgIdx, unsigned TDF, @@ -4013,8 +4022,9 @@ 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 @@ /// 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 &Deduced, SmallVectorImpl &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(Arg)) + if (InitListExpr *ILE = dyn_cast_if_present(Arg)) return DeduceFromInitializerList(S, TemplateParams, ParamType, ILE, Info, Deduced, OriginalCallArgs, ArgIdx, TDF); @@ -4070,8 +4082,9 @@ // // 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 @@ TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, FunctionDecl *&Specialization, TemplateDeductionInfo &Info, bool PartialOverloading, bool AggregateDeductionCandidate, + QualType ObjectType, Expr::Classification ObjectClassification, llvm::function_ref)> 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 @@ // 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(); if (Proto->isTemplateVariadic()) /* Do nothing */; @@ -4157,7 +4179,8 @@ SmallVector 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 @@ 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 @@ dyn_cast(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 @@ 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 @@ /// 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 @@ if ((P->isPointerType() && A->isPointerType()) || (P->isMemberPointerType() && A->isMemberPointerType())) TDF |= TDF_IgnoreQualifiers; + + SmallVector 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 @@ TemplateDeductionResult Result; runWithSufficientStackSpace(Info.getLocation(), [&] { Result = FinishTemplateArgumentDeduction(ConversionTemplate, Deduced, 0, - ConversionSpecialized, Info); + ConversionSpecialized, Info, + &OriginalCallArgs); }); Specialization = cast_or_null(ConversionSpecialized); return Result; @@ -4846,7 +4903,8 @@ if (isa(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 @@ 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 @@ // // 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 @@ 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 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -2908,6 +2908,8 @@ 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 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -4434,7 +4434,7 @@ Qualifiers ThisTypeQuals; if (CXXMethodDecl *Method = dyn_cast(D)) { ThisContext = cast(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 --- 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 #include @@ -3122,7 +3124,7 @@ (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 @@ // // 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 @@ 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(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 @@ // // ... 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 @@ 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(FT) && cast(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 @@ // 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 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1707,6 +1707,7 @@ 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 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -596,6 +596,7 @@ 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 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1162,28 +1162,21 @@ 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 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -299,7 +299,7 @@ } if (const auto *MD = dyn_cast(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 @@ // valid region. const Decl *Callee = OCE->getCalleeDecl(); if (const auto *MD = dyn_cast_or_null(Callee)) { - if (MD->isInstance()) { + if (MD->isImplicitObjectMemberFunction()) { ProgramStateRef State = Pred->getState(); const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef NewState = @@ -3357,7 +3357,7 @@ // Handle C++ method calls. if (const auto *MD = dyn_cast(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 --- a/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp +++ b/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp @@ -84,7 +84,7 @@ // pointer should remain unchanged. Ignore static methods, since they do not // have 'this' pointers. const CXXMethodDecl *CXXMD = dyn_cast(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 --- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -275,7 +275,7 @@ // 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 --- /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 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 --- 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 --- a/clang/test/CXX/drs/dr25xx.cpp +++ b/clang/test/CXX/drs/dr25xx.cpp @@ -81,6 +81,63 @@ #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 diff --git a/clang/test/CXX/drs/dr26xx.cpp b/clang/test/CXX/drs/dr26xx.cpp --- 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 @@ } +#if __cplusplus >= 202302L namespace dr2650 { // dr2650: yes template struct S {}; template int f(S*); // expected-note {{type 'X' of non-type template parameter is not a structural type}} @@ -116,6 +119,16 @@ }; int i0 = f(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 @@ 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 --- a/clang/test/CXX/drs/dr5xx.cpp +++ b/clang/test/CXX/drs/dr5xx.cpp @@ -771,10 +771,15 @@ }; #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 @@ 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 --- 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 struct trivially_assignable_check { static_assert(B == __has_trivial_assign(T), ""); static_assert(B == __is_trivially_assignable(T&, T), ""); @@ -23,14 +31,18 @@ // 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; // 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 &&), ""); 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; #endif @@ -84,11 +107,16 @@ // 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 --- /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 --- /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 --- /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 --- /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 --- /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 --- /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 --- /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 +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 (this const S& self) { + return Wrap{self}; + }; + constexpr int operator <<(this Wrap 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 --- /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 +class coro_test { +public: + struct promise_type; + using handle = std::coroutine_handle; + 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 + 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 +struct std::coroutine_traits, Ret, P...> { + using promise_type = coro_test::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 ok(this S&, int) { + co_return; // expected-note {{in instantiation}} + } + + coro_test ok2(this const S&) { // expected-note {{in instantiation}} + co_return; + } + + coro_test ko(this const S&) { // expected-error {{no matching constructor for initialization of 'std::coroutine_traits, 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 --- /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 +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 + 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 +constexpr bool is_same = false; + +template +constexpr bool is_same = true; + +template