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,17 @@ 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) && + !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 @@ -1011,6 +1011,9 @@ /// Whether this parameter is an ObjC method parameter or not. unsigned IsObjCMethodParam : 1; + /// Whether this is a C++23 explicit object parameter + unsigned IsExplicitObjectParameter : 1; + /// If IsObjCMethodParam, a Decl::ObjCDeclQualifier. /// Otherwise, the number of function parameter scopes enclosing /// the function parameter scope in which this parameter was @@ -1720,6 +1723,9 @@ /// Represents a parameter to a function. class ParmVarDecl : public VarDecl { + SourceLocation ExplicitObjectParameterIntroducerLoc; + friend class ASTDeclReader; + public: enum { MaxFunctionScopeDepth = 255 }; enum { MaxFunctionScopeIndex = 255 }; @@ -1731,6 +1737,7 @@ : VarDecl(DK, C, DC, StartLoc, IdLoc, Id, T, TInfo, S) { assert(ParmVarDeclBits.HasInheritedDefaultArg == false); assert(ParmVarDeclBits.DefaultArgKind == DAK_None); + assert(ParmVarDeclBits.IsExplicitObjectParameter == false); assert(ParmVarDeclBits.IsKNRPromoted == false); assert(ParmVarDeclBits.IsObjCMethodParam == false); setDefaultArg(DefArg); @@ -1807,6 +1814,20 @@ ParmVarDeclBits.IsKNRPromoted = promoted; } + bool isExplicitObjectParameter() const { + return ParmVarDeclBits.IsExplicitObjectParameter; + } + + void setIsExplicitObjectParameter(bool isExplicitObjectParam, + SourceLocation Loc = SourceLocation()) { + ParmVarDeclBits.IsExplicitObjectParameter = isExplicitObjectParam; + ExplicitObjectParameterIntroducerLoc = Loc; + } + + SourceLocation getExplicitObjectParamThisLoc() const { + return ExplicitObjectParameterIntroducerLoc; + } + Expr *getDefaultArg(); const Expr *getDefaultArg() const { return const_cast(this)->getDefaultArg(); @@ -2638,6 +2659,10 @@ /// parameters have default arguments (in C++). unsigned getMinRequiredArguments() const; + unsigned getMinRequiredExplicitArguments() const; + + bool hasCXXExplicitFunctionObjectParameter() const; + /// 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 @@ -2060,6 +2060,14 @@ bool isStatic() const; bool isInstance() const { return !isStatic(); } + bool isExplicitObjectMemberFunction() const; + bool isImplicitObjectMemberFunction() const; + const ParmVarDecl *getNonObjectParameter(unsigned I) const { + return getParamDecl(isExplicitObjectMemberFunction() ? I + 1 : I); + } + ParmVarDecl *getNonObjectParameter(unsigned I) { + return getParamDecl(isExplicitObjectMemberFunction() ? I + 1 : I); + } /// Returns true if the given operator is implicitly static in a record /// context. @@ -2169,14 +2177,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 @@ -1275,7 +1275,9 @@ ExprValueKind VK, NonOdrUseReason NOUR); /// Construct an empty declaration reference expression. - explicit DeclRefExpr(EmptyShell Empty) : Expr(DeclRefExprClass, Empty) {} + explicit DeclRefExpr(EmptyShell Empty) : Expr(DeclRefExprClass, Empty) { + DeclRefExprBits.CapturedByCopyInLambdaWithExplicitObjectParameter = false; + } public: DeclRefExpr(const ASTContext &Ctx, ValueDecl *D, @@ -1449,6 +1451,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 @@ -4640,12 +4640,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< @@ -4828,7 +4830,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< @@ -7238,12 +7240,14 @@ "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}1 member function">; + def err_invalid_qualified_function_type : Error< "%select{non-member function|static member function|deduction guide}0 " "%select{of type %2 |}1cannot have '%3' qualifier">; @@ -7253,6 +7257,31 @@ def err_qualified_function_typeid : Error< "type operand %0 of 'typeid' cannot have '%1' qualifier">; +def ext_deducing_this : ExtWarn< + "explicit object parameters are a C++2b extension">, + InGroup; +def warn_cxx20_ext_deducing_this : Warning< + "explicit object parameters are incompatible with C++ standards before C++2b">, + DefaultIgnore, InGroup; +def err_explicit_object_default_arg: Error< + "an explicit object parameter cannot have a default argument">; +def err_explicit_object_parameter_pack: Error< + "an 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_qualifiers: Error< + "a %select{function|lambda}0 with an explicit object parameter cannot " + "%select{be const|be mutable|have reference qualifiers|be volatile}1">; +def err_invalid_explicit_object_type_in_lambda: Error< + "invalid explicit object parameter type %0 in lambda with capture" + "(it must be the type of the lambda or be derived from it)">; + 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{" @@ -8474,53 +8503,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" @@ -9338,10 +9379,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 " @@ -9404,7 +9445,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/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 @@ -2175,20 +2175,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, unsigned SkipSupersetFirstParameter, + SourceLocation SuperLoc, const FunctionProtoType *Subset, + unsigned SkipSubsetFirstParameter, SourceLocation SubLoc); + bool CheckParamExceptionSpec( + const PartialDiagnostic &NestedDiagID, const PartialDiagnostic &NoteID, + const FunctionProtoType *Target, unsigned SkipTargetFirstParameter, + SourceLocation TargetLoc, const FunctionProtoType *Source, + unsigned SkipSourceFirstParameter, SourceLocation SourceLoc); TypeResult ActOnTypeName(Scope *S, Declarator &D); @@ -3005,7 +3002,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); @@ -3774,7 +3772,7 @@ bool UseMemberUsingDeclRules); bool IsOverload(FunctionDecl *New, FunctionDecl *Old, bool UseMemberUsingDeclRules, bool ConsiderCudaAttrs = true, - bool ConsiderRequiresClauses = true); + bool UseOverrideRules = false); // Calculates whether the expression Constraint depends on an enclosing // template, for the purposes of [temp.friend] p9. @@ -3832,6 +3830,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, @@ -3872,10 +3876,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 @@ -4197,9 +4202,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); @@ -5762,6 +5766,10 @@ Expr *Input, bool IsAfterAmp = false); bool isQualifiedMemberAccess(Expr *E); + bool DiagnoseMissingQualifiersinAddresOfOperand(SourceLocation OpLoc, + Expr *Op, + const CXXMethodDecl *MD); + QualType CheckAddressOfOperand(ExprResult &Operand, SourceLocation OpLoc); bool CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N); @@ -7186,6 +7194,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. @@ -7834,10 +7844,12 @@ void CheckConstructor(CXXConstructorDecl *Constructor); QualType CheckDestructorDeclarator(Declarator &D, QualType R, StorageClass& SC); + bool CheckDestructor(CXXDestructorDecl *Destructor); void CheckConversionDeclarator(Declarator &D, QualType &R, StorageClass& SC); Decl *ActOnConversionDeclarator(CXXConversionDecl *Conversion); + bool CheckDeductionGuideDeclarator(Declarator &D, QualType &R, StorageClass &SC); void CheckDeductionGuideTemplate(FunctionTemplateDecl *TD); @@ -7856,6 +7868,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 // @@ -7907,6 +7926,10 @@ bool CheckOverridingFunctionReturnType(const CXXMethodDecl *New, const CXXMethodDecl *Old); + // Check that the overriding method has no explicit object parameter. + bool CheckOverridingExplicitObjectMembers(CXXMethodDecl *New, + const CXXMethodDecl *Old); + /// CheckOverridingFunctionExceptionSpec - Checks whether the exception /// spec is a subset of base spec. bool CheckOverridingFunctionExceptionSpec(const CXXMethodDecl *New, @@ -9148,6 +9171,7 @@ TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, FunctionDecl *&Specialization, sema::TemplateDeductionInfo &Info, bool PartialOverloading, bool AggregateDeductionCandidate, + QualType ObjectType, Expr::Classification ObjectClassification, llvm::function_ref)> CheckNonDependent); TemplateDeductionResult @@ -9158,11 +9182,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 @@ -4413,6 +4413,9 @@ Error ASTNodeImporter::ImportDefaultArgOfParmVarDecl( const ParmVarDecl *FromParam, ParmVarDecl *ToParam) { ToParam->setHasInheritedDefaultArg(FromParam->hasInheritedDefaultArg()); + ToParam->setIsExplicitObjectParameter( + FromParam->isExplicitObjectParameter(), + 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 @@ -1362,6 +1362,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,12 @@ 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 @@ -3616,6 +3616,15 @@ return NumRequiredArgs; } +bool FunctionDecl::hasCXXExplicitFunctionObjectParameter() const { + return getNumParams() != 0 && getParamDecl(0)->isExplicitObjectParameter(); +} + +unsigned FunctionDecl::getMinRequiredExplicitArguments() const { + return getMinRequiredArguments() - + (hasCXXExplicitFunctionObjectParameter() ? 1 : 0); +} + 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,19 @@ 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() && getNumParams() != 0 && + getParamDecl(0)->isExplicitObjectParameter(); +} + +bool CXXMethodDecl::isImplicitObjectMemberFunction() const { + return !isStatic() && + (getNumParams() == 0 || !getParamDecl(0)->isExplicitObjectParameter()); +} + bool CXXMethodDecl::isCopyAssignmentOperator() const { // C++0x [class.copy]p17: // A user-declared copy assignment operator X::operator= is a non-static @@ -2416,11 +2429,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 +2451,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(); @@ -2485,7 +2499,8 @@ static QualType getThisObjectType(ASTContext &C, const FunctionProtoType *FPT, const CXXRecordDecl *Decl) { QualType ClassTy = C.getTypeDeclType(Decl); - return C.getQualifiedType(ClassTy, FPT->getMethodQuals()); + auto Qualifiers = FPT->getMethodQuals(); + return C.getQualifiedType(ClassTy, Qualifiers); } QualType CXXMethodDecl::getThisType(const FunctionProtoType *FPT, @@ -2495,12 +2510,6 @@ return 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*, @@ -2512,11 +2521,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 @@ -865,6 +865,12 @@ void DeclPrinter::VisitVarDecl(VarDecl *D) { prettyPrintPragmas(D); + if (ParmVarDecl *Param = dyn_cast(D)) { + if (Param->isExplicitObjectParameter()) { + Out << "this "; + } + } + QualType T = D->getTypeSourceInfo() ? D->getTypeSourceInfo()->getType() : D->getASTContext().getUnqualifiedObjCPointerType(D->getType()); 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 (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(), @@ -4698,6 +4698,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; } @@ -7762,15 +7765,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 = + isa(FD) && + cast(FD)->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! @@ -7854,7 +7860,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. @@ -16227,7 +16233,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) && @@ -16322,9 +16329,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 @@ -46,7 +46,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/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(0)->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 @@ -1782,6 +1782,10 @@ void TextNodeDumper::VisitVarDecl(const VarDecl *D) { dumpName(D); + if (const ParmVarDecl *P = dyn_cast(D)) { + if (P->isExplicitObjectParameter()) + OS << " this"; + } dumpType(D->getType()); StorageClass SC = D->getStorageClass(); if (SC != SC_None) 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 @@ -317,8 +317,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/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp --- a/clang/lib/Analysis/ThreadSafetyCommon.cpp +++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -149,7 +149,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 @@ -297,7 +297,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); @@ -447,7 +447,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 @@ -516,7 +516,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, @@ -1462,7 +1462,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); } @@ -1496,7 +1496,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; @@ -2463,7 +2463,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 @@ -2134,7 +2134,8 @@ 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 ((MD = dyn_cast(D)) && + 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 @@ -2654,9 +2654,11 @@ 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); + // QualType TagType = CGF.getContext().getTagDeclType(FD->getParent()); + // LValue LV = CGF.MakeNaturalAlignAddrLValue(ThisValue, TagType); + // return CGF.EmitLValueForField(LV, FD); } /// Named Registers are named metadata pointing to the register name @@ -4269,17 +4271,39 @@ /// 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 CXXMethodDecl *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, @@ -4991,9 +5015,13 @@ 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())) + dyn_cast_or_null(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,11 @@ 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 (auto *Op = llvm::dyn_cast(CE)) { + if (const CXXMethodDecl *M = dyn_cast(Op->getCalleeDecl())) + ArgsToSkip = M->isExplicitObjectMemberFunction() ? 0 : 1; + } CGF.EmitCallArgs(Args, FPT, drop_begin(CE->arguments(), ArgsToSkip), CE->getDirectCallee()); } else { @@ -484,7 +488,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/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/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -5753,8 +5753,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) @@ -5763,7 +5762,6 @@ if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, /*ObjectHasErrors=*/false, /*EnteringContext=*/true)) { - TPA.Revert(); return false; } @@ -5775,7 +5773,6 @@ } else if (Tok.is(tok::annot_template_id)) { ConsumeAnnotationToken(); } else { - TPA.Revert(); return false; } @@ -5785,7 +5782,6 @@ // Current class name must be followed by a left parenthesis. if (Tok.isNot(tok::l_paren)) { - TPA.Revert(); return false; } ConsumeParen(); @@ -5794,7 +5790,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; } @@ -5803,7 +5798,6 @@ if (getLangOpts().CPlusPlus11 && isCXX11AttributeSpecifier(/*Disambiguate*/ false, /*OuterMightBeMessageSend*/ true)) { - TPA.Revert(); return true; } @@ -5824,9 +5818,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))) { @@ -5895,8 +5897,6 @@ break; } } - - TPA.Revert(); return IsConstructor; } @@ -7274,6 +7274,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, @@ -7338,9 +7339,16 @@ SourceLocation DSStart = Tok.getLocation(); + // Parse a C++23 Explicit Object Parameter + 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 @@ -7354,6 +7362,9 @@ : DeclaratorContext::Prototype); ParseDeclarator(ParmDeclarator); + if (ThisLoc.isValid()) + ParmDeclarator.SetRangeBegin(ThisLoc); + // Parse GNU attributes, if present. MaybeParseGNUAttributes(ParmDeclarator); if (getLangOpts().HLSL) @@ -7423,7 +7434,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 @@ -1559,6 +1559,15 @@ case tok::kw___vector: return TPResult::True; + case tok::kw_this: { + if (getLangOpts().CPlusPlus) { + RevertingTentativeParsingAction PA(*this); + ConsumeToken(); + return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult, + InvalidAsDeclSpec); + } + [[fallthrough]]; + } 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/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 @@ -125,7 +125,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 @@ -138,7 +138,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 @@ -167,7 +167,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) { @@ -216,7 +216,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; } @@ -1530,7 +1530,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; } @@ -6781,8 +6781,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); @@ -6802,14 +6803,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(); @@ -6822,8 +6825,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); @@ -7150,13 +7153,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(); } @@ -7519,7 +7522,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; } @@ -7552,7 +7556,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(); } @@ -7828,7 +7833,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(); } @@ -8218,7 +8223,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)) @@ -8459,7 +8465,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) @@ -8579,7 +8585,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. @@ -8718,13 +8725,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/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -8847,9 +8847,10 @@ /*ConsiderCudaAttrs=*/true, // C++2a [class.virtual]p2 does not consider requires // clauses when overriding. - /*ConsiderRequiresClauses=*/false)) + /*UseOverrideRules=*/true)) + continue; + if (!CheckOverridingExplicitObjectMembers(MD, BaseMD)) continue; - if (Overridden.insert(BaseMD).second) { MD->addOverriddenMethod(BaseMD); CheckOverridingFunctionReturnType(MD, BaseMD); @@ -9183,6 +9184,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() && @@ -14718,9 +14721,33 @@ } } +static void CheckExplicitObjectParameter(Sema &S, ParmVarDecl *P, + SourceLocation ExplicitThisLoc) { + if (!ExplicitThisLoc.isValid()) + return; + assert(S.getLangOpts().CPlusPlus && + "explicit parameter in non-cplusplus mode"); + S.Diag(ExplicitThisLoc, S.getLangOpts().CPlusPlus23 + ? diag::warn_cxx20_ext_deducing_this + : diag::ext_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->setIsExplicitObjectParameter(true, 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'. @@ -14798,6 +14825,8 @@ if (D.isInvalidType()) New->setInvalidDecl(); + CheckExplicitObjectParameter(*this, New, ExplicitThisLoc); + assert(S->isFunctionPrototypeScope()); assert(S->getFunctionPrototypeDepth() >= 1); New->setScopeInfo(S->getFunctionPrototypeDepth() - 1, @@ -15191,6 +15220,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. 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 @@ -7611,7 +7611,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. @@ -7640,10 +7640,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)) { @@ -7653,7 +7655,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 { @@ -7665,7 +7667,10 @@ } // Check for parameter type matching. - QualType ArgType = ExpectedParams ? Type->getParamType(0) : QualType(); + QualType ArgType = + ExpectedParams && Type + ? Type->getParamType(MD->isExplicitObjectMemberFunction() ? 1 : 0) + : QualType(); bool HasConstParam = false; if (ExpectedParams && ArgType->isReferenceType()) { // Argument must be reference to possibly-const T. @@ -7759,8 +7764,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)); } } @@ -8411,7 +8416,8 @@ ExprPair getCompleteObject() { unsigned Param = 0; ExprResult LHS; - if (isa(FD)) { + if (auto *MD = dyn_cast(FD); + MD && MD->isImplicitObjectMemberFunction()) { // LHS is '*this'. LHS = S.ActOnCXXThis(Loc); if (!LHS.isInvalid()) @@ -8717,15 +8723,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"); } @@ -8749,7 +8762,9 @@ } } - if (FD->getNumParams() != (IsMethod ? 1 : 2)) { + if (FD->getNumParams() - + (FD->hasCXXExplicitFunctionObjectParameter() ? 1 : 0) != + (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) @@ -8759,6 +8774,8 @@ const ParmVarDecl *KnownParm = nullptr; for (const ParmVarDecl *Param : FD->parameters()) { + if (Param->isExplicitObjectParameter()) + continue; QualType ParmTy = Param->getType(); if (!KnownParm) { @@ -9142,9 +9159,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(); } } @@ -10024,7 +10041,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 @@ -10055,7 +10072,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()) { @@ -11021,15 +11038,24 @@ << 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 (ParmVarDecl *First = dyn_cast_or_null(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); @@ -11187,6 +11213,107 @@ 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() << 0 << IsLambda; + return; + } + + if (D.getDeclSpec().isVirtualSpecified()) { + Diag(ExplicitObjectParam->getBeginLoc(), + diag::err_explicit_object_parameter_nonmember) + << D.getSourceRange() << 1 << IsLambda; + return; + } + + if (FTI.hasMutableQualifier()) { + Diag(ExplicitObjectParam->getBeginLoc(), + diag::err_explicit_object_parameter_qualifiers) + << D.getSourceRange() << IsLambda << 1; + return; + } + + if (IsLambda) + return; + + if (!DC || !DC->isRecord()) { + Diag(ExplicitObjectParam->getLocation(), + diag::err_explicit_object_parameter_nonmember) + << D.getSourceRange() << 2 << IsLambda; + return; + } + + assert(DC && "Expected a non-null declaration context"); + + if (FTI.hasMethodTypeQualifiers()) { + if (FTI.getConstQualifierLoc().isValid()) + Diag(FTI.getConstQualifierLoc(), + diag::err_explicit_object_parameter_qualifiers) + << 0 << 0 << D.getSourceRange(); + if (FTI.getVolatileQualifierLoc().isValid()) + Diag(FTI.getVolatileQualifierLoc(), + diag::err_explicit_object_parameter_qualifiers) + << 0 << 3 << D.getSourceRange(); + } + if (FTI.hasRefQualifier()) + Diag(FTI.getRefQualifierLoc(), + diag::err_explicit_object_parameter_qualifiers) + << 0 << 2 << D.getSourceRange(); + + // There should be a CWG issue for that + 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. @@ -14781,12 +14908,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(); } @@ -14798,8 +14924,25 @@ // 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 ? (ExprBuilder &)*ExplicitObject : (ExprBuilder &)*This; // Assign base classes. bool Invalid = false; @@ -14821,11 +14964,11 @@ VK_LValue, BasePath); // Dereference "this". - DerefBuilder DerefThis(This); - CastBuilder To(DerefThis, - Context.getQualifiedType( - BaseType, CopyAssignOperator->getMethodQualifiers()), - VK_LValue, BasePath); + CastBuilder To( + ExplicitObject ? (ExprBuilder &)*ExplicitObject + : (ExprBuilder &)*DerefThis, + Context.getQualifiedType(BaseType, ObjectType.getQualifiers()), + VK_LValue, BasePath); // Build the copy. StmtResult Copy = buildSingleCopyAssign(*this, Loc, BaseType, @@ -14891,10 +15034,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, @@ -14911,15 +15051,10 @@ 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 ? (ExprBuilder &)*ExplicitObject + : LangOpts.HLSL ? (ExprBuilder &)*This + : (ExprBuilder &)*DerefThis) + .build(*this, Loc); StmtResult Return = BuildReturnStmt(Loc, ThisExpr); if (Return.isInvalid()) Invalid = true; @@ -15150,7 +15285,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(); @@ -15164,8 +15299,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 ? (ExprBuilder &)*ExplicitObject : (ExprBuilder &)*This; // Assign base classes. bool Invalid = false; @@ -15193,14 +15343,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 ? (ExprBuilder &)*ExplicitObject + : (ExprBuilder &)*DerefThis, + Context.getQualifiedType(BaseType, ObjectType.getQualifiers()), + VK_LValue, BasePath); // Build the move. StmtResult Move = buildSingleCopyAssign(*this, Loc, BaseType, @@ -15265,8 +15414,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 " @@ -15288,10 +15437,12 @@ if (!Invalid) { // Add a "return *this;" - ExprResult ThisObj = - CreateBuiltinUnaryOp(Loc, UO_Deref, This.build(*this, Loc)); + // Add a "return *this;" + Expr *ThisExpr = (ExplicitObject ? (ExprBuilder &)*ExplicitObject + : (ExprBuilder &)*DerefThis) + .build(*this, Loc); - StmtResult Return = BuildReturnStmt(Loc, ThisObj.get()); + StmtResult Return = BuildReturnStmt(Loc, ThisExpr); if (Return.isInvalid()) Invalid = true; else @@ -15610,7 +15761,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( @@ -16216,8 +16369,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) || @@ -18015,6 +18171,20 @@ return true; } +bool Sema::CheckOverridingExplicitObjectMembers(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, unsigned SkipSupersetFirstParameter, + SourceLocation SuperLoc, const FunctionProtoType *Subset, + unsigned 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, unsigned SkipTargetFirstParameter, + SourceLocation TargetLoc, const FunctionProtoType *Source, + unsigned SkipSourceFirstParameter, SourceLocation SourceLoc) { auto RetDiag = DiagID; RetDiag << 0; if (CheckSpecForTypesEquivalent( @@ -910,14 +909,18 @@ // We shouldn't even be testing this unless the arguments are otherwise // compatible. - assert(Target->getNumParams() == Source->getNumParams() && + assert((Target->getNumParams() - SkipTargetFirstParameter) == + (Source->getNumParams() - SkipSourceFirstParameter) && "Functions have different argument counts."); for (unsigned i = 0, E = Target->getNumParams(); i != E; ++i) { + // Target->getExtParameterInfo(0). + 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 +961,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 +993,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 @@ -2355,7 +2355,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; } @@ -3128,7 +3128,7 @@ FromRecordType = FromType; } } else if (const auto *Method = dyn_cast(Member)) { - if (Method->isStatic()) + if (!Method->isImplicitObjectMemberFunction()) return From; DestType = Method->getThisType(); @@ -6437,6 +6437,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; @@ -6455,21 +6458,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) @@ -6494,17 +6505,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 @@ -6512,8 +6529,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()); @@ -7589,9 +7608,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) @@ -14865,6 +14884,34 @@ S.Diag(Loc, diag::err_typecheck_address_of) << Type << E->getSourceRange(); } +bool Sema::DiagnoseMissingQualifiersinAddresOfOperand(SourceLocation OpLoc, + Expr *Op, + const CXXMethodDecl *MD) { + auto *DRE = cast(Op->IgnoreParens()); + + if (Op != DRE) { + 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)) + Diag(OpLoc, diag::err_typecheck_addrof_dtor) << DRE->getSourceRange(); + + if (DRE->getQualifier()) + return true; + if (MD->getParent()->getName().empty()) + Diag(OpLoc, diag::err_unqualified_pointer_member_function) + << DRE->getSourceRange(); + else { + SmallString<32> Str; + StringRef Qual = (MD->getParent()->getName() + "::").toStringRef(Str); + Diag(OpLoc, diag::err_unqualified_pointer_member_function) + << DRE->getSourceRange() + << FixItHint::CreateInsertion(DRE->getSourceRange().getBegin(), Qual); + } + return false; +} + /// 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. @@ -14974,28 +15021,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(); + DiagnoseMissingQualifiersinAddresOfOperand(OpLoc, OrigOp.get(), MD); QualType MPTy = Context.getMemberPointerType( op->getType(), Context.getTypeDeclType(MD->getParent()).getTypePtr()); @@ -15015,7 +15041,12 @@ << op->getType() << op->getSourceRange(); return QualType(); } + } else if (DeclRefExpr *DRE = dyn_cast(op)) { + CXXMethodDecl *MD = dyn_cast_or_null(DRE->getDecl()); + if (MD) + DiagnoseMissingQualifiersinAddresOfOperand(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; @@ -16406,7 +16437,7 @@ if (isa(VD) || isa(VD)) return true; if (CXXMethodDecl *Method = dyn_cast(VD)) - return Method->isInstance(); + return Method->isImplicitObjectMemberFunction(); return false; } @@ -16417,7 +16448,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. @@ -19126,7 +19157,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(); @@ -19443,7 +19475,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(); } @@ -20510,6 +20543,32 @@ 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) { + DeclRefExpr *ID = dyn_cast(E); + if (!ID || ID->isTypeDependent()) + return; + auto IsDependent = [&]() { + auto *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, @@ -20519,14 +20578,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 @@ -1153,7 +1153,7 @@ auto C = CurLSI->getCXXThisCapture(); if (C.isCopyCapture()) { - if (!CurLSI->Mutable) + if (CurLSI->lambdaCaptureShouldBeConst()) ClassType.addConst(); return ASTCtx.getPointerType(ClassType); } @@ -1209,11 +1209,11 @@ QualType ThisTy = CXXThisTypeOverride; if (CXXMethodDecl *method = dyn_cast(DC)) { - if (method && method->isInstance()) + if (method && method->isImplicitObjectMemberFunction()) ThisTy = method->getThisType(); } - 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 @@ -1392,8 +1392,16 @@ /// 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 (CXXMethodDecl *method = dyn_cast(DC); + method && method->isExplicitObjectMemberFunction()) { + return Diag(Loc, diag::err_invalid_this_use) << 1; + } else 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); } @@ -4318,15 +4326,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(); } @@ -8038,68 +8048,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,15 @@ 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 { + auto Diag = SemaRef.Diag(Loc, diag::err_member_call_without_object) + << Range << /*explicit*/ 1; + if (!Replacement.empty()) + Diag << FixItHint::CreateInsertion(Loc, Replacement); + } } /// Builds an expression which might be an implicit member expression. @@ -255,13 +281,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 @@ -7395,8 +7395,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())) { @@ -7531,7 +7532,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 @@ -378,6 +378,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->isDependentContext()) + 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) { @@ -859,9 +891,18 @@ if (ParamInfo.getNumTypeObjects() == 0) { MethodTyInfo = getDummyLambdaType(S, Loc); } else { + // Check explicit parameters + S.CheckExplicitObjectLambda(ParamInfo); + DeclaratorChunk::FunctionTypeInfo &FTI = ParamInfo.getFunctionTypeInfo(); + + bool HasExplicitObjectParameter = + FTI.NumParams > 0 && + cast(FTI.Params[0].Param)->isExplicitObjectParameter(); + ExplicitResultType = FTI.hasTrailingReturnType(); - if (!FTI.hasMutableQualifier() && !IsLambdaStatic) + if (!FTI.hasMutableQualifier() && !IsLambdaStatic && + !HasExplicitObjectParameter) FTI.getOrCreateMethodQualifiers().SetTypeQual(DeclSpec::TQ_const, Loc); if (ExplicitResultType && S.getLangOpts().HLSL) { @@ -1659,7 +1700,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 @@ -1237,7 +1237,7 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, bool UseMemberUsingDeclRules, bool ConsiderCudaAttrs, - bool ConsiderRequiresClauses) { + bool UseOverrideRules) { // C++ [basic.start.main]p2: This function shall not be overloaded. if (New->isMain()) return false; @@ -1275,10 +1275,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 +1283,128 @@ Old->isMemberLikeConstrainedFriend()) && !New->getLexicalDeclContext()->Equals(Old->getLexicalDeclContext())) return true; + CXXMethodDecl *OldMethod = dyn_cast(Old); + CXXMethodDecl *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 = [&](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 (!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()) { + auto ParentType = + Context.getTypeDeclType(OldMethod->getParent()).getCanonicalType(); + if (ParentType.getTypePtr() != BS.Ty) + return false; + BS.Ty = DS.Ty; + } + + 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 (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)) { + Diag(NewMethod->getLocation(), diag::err_ref_qualifier_overload) + << NewMethod->getRefQualifier() << OldMethod->getRefQualifier(); + 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 || + !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 = [&](CXXMethodDecl *Old, + CXXMethodDecl *New) { + auto NewObjectType = New->getFunctionObjectParameterReferenceType(); + auto OldObjectType = Old->getFunctionObjectParameterReferenceType(); + + auto IsImplicitWithNoRefQual = [](CXXMethodDecl *F) -> bool { + return F->getRefQualifier() == RQ_None && + !F->isExplicitObjectMemberFunction(); + }; + + if (IsImplicitWithNoRefQual(Old) != IsImplicitWithNoRefQual(New)) { + if (CompareType(OldObjectType.getNonReferenceType(), + NewObjectType.getNonReferenceType())) { + return true; + } + } + return CompareType(OldObjectType, NewObjectType); + }(OldMethod, NewMethod); + + if (!HaveCorrespondingObjectParameters) { + DiagnoseInconsistentRefQualifiers(); + // CWG2554 + // and, if at least one is an explicit object member function, ignoring + // object parameters + if (UseOverrideRules && (NewMethod->isExplicitObjectMemberFunction() || + OldMethod->isExplicitObjectMemberFunction())) + ; + else + return true; + } + } if (NewTemplate) { // C++ [temp.over.link]p4: @@ -1324,7 +1443,7 @@ return true; } - if (ConsiderRequiresClauses) { + if (!UseOverrideRules) { Expr *NewRC = New->getTrailingRequiresClause(), *OldRC = Old->getTrailingRequiresClause(); if ((NewRC != nullptr) != (OldRC != nullptr)) @@ -1334,49 +1453,9 @@ 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); - } - 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; } @@ -1864,7 +1943,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() @@ -3084,30 +3164,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 @@ -5486,11 +5576,44 @@ /// 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, + CXXRecordDecl *ActingContext, bool InOverloadResolution = false, + QualType ExplicitParameterType = QualType()) { + + // 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()); + } + + 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, false, + /*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. @@ -5506,17 +5629,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 @@ -5563,7 +5675,7 @@ SecondKind = ICK_Identity; } 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; @@ -5613,11 +5725,9 @@ /// 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->getThisType()->castAs()->getPointeeType(); @@ -6114,6 +6224,65 @@ return ExprResult(); } +static QualType GetExplicitObjectType(Sema &S, Expr *MemExprE) { + Expr *Base = nullptr; + assert((isa(MemExprE)) && + "expected a member expression"); + + if (auto M = dyn_cast(MemExprE); + M && !M->isImplicitAccess()) + Base = M->getBase(); + else if (auto M = dyn_cast(MemExprE); M && !M->isImplicitAccess()) + Base = M->getBase(); + QualType T; + if (!Base) + T = S.getCurrentThisType(); + else + T = Base->getType(); + + if (T->isPointerType()) + T = T->getPointeeType(); + + return T; +} + +static Expr *GetExplicitObjectExpr(Sema &S, Expr *Obj, 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) { @@ -6197,7 +6366,6 @@ Converter.diagnoseConversion(SemaRef, Loc, T, ToType) << From->getSourceRange(); } - ExprResult Result = SemaRef.BuildCXXMemberCallExpr(From, Found, Conversion, HadMultipleCandidates); if (Result.isInvalid()) @@ -6348,7 +6516,6 @@ QualType CurToType = Conversion->getConversionType().getNonReferenceType(); if (Converter.match(CurToType) || ConvTemplate) { - if (Conversion->isExplicit()) { // FIXME: For C++1y, do we need this restriction? // cf. diagnoseNoViableConversion() @@ -6844,7 +7011,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; @@ -7144,7 +7311,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 @@ -7162,7 +7330,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; @@ -7191,7 +7359,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; @@ -7232,7 +7400,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, @@ -7297,8 +7465,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, @@ -7382,6 +7550,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, @@ -7454,16 +7624,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 @@ -7596,11 +7774,11 @@ // // 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 PointerType *FromPtrType = ObjectType->getAs()) + ObjectType = FromPtrType->getPointeeType(); + CXXRecordDecl *ConversionContext = + cast(ObjectType->castAs()->getDecl()); Candidate.Conversions[0] = TryObjectArgumentInitialization( *this, CandidateSet.getLocation(), From->getType(), @@ -7758,11 +7936,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(); @@ -7814,9 +7995,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; @@ -9842,11 +10032,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, @@ -11044,39 +11230,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); } @@ -12420,7 +12610,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) @@ -12465,7 +12657,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) @@ -12832,7 +13026,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 @@ -13494,10 +13691,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: { @@ -13552,10 +13752,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); } } @@ -13629,6 +13832,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; + 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; + } + } + 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. /// @@ -13723,14 +14003,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 @@ -13757,6 +14040,7 @@ ResultTy = ResultTy.getNonLValueExprType(Context); Args[0] = Input; + CallExpr *TheCall = CXXOperatorCallExpr::Create( Context, Op, FnExpr.get(), ArgsArray, ResultTy, VK, OpLoc, CurFPFeatureOverrides(), Best->IsADLCandidate); @@ -14060,13 +14344,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 { @@ -14084,19 +14371,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 { @@ -14131,22 +14421,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 (CXXMethodDecl *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); @@ -14154,7 +14449,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); @@ -14533,8 +14828,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(); @@ -14736,6 +15038,7 @@ MemberExpr *MemExpr; CXXMethodDecl *Method = nullptr; + bool HadMultipleCandidates = false; DeclAccessPair FoundDecl = DeclAccessPair::make(nullptr, AS_public); NestedNameSpecifier *Qualifier = nullptr; if (isa(NakedMemExpr)) { @@ -14767,11 +15070,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 (auto *M = dyn_cast(Func); + M && M->hasCXXExplicitFunctionObjectParameter()) + HasExplicitParameter = true; + else if (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)) { @@ -14784,17 +15100,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(); @@ -14848,10 +15167,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); @@ -14866,27 +15189,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)) @@ -14915,8 +15252,8 @@ if ((isa(CurContext) || isa(CurContext)) && - TheCall->getMethodDecl()->isPure()) { - const CXXMethodDecl *MD = TheCall->getMethodDecl(); + TheCall->getDirectCallee()->isPure()) { + const FunctionDecl *MD = TheCall->getDirectCallee(); if (isa(MemExpr->getBase()->IgnoreParenCasts()) && MemExpr->performsVirtualDispatch(getLangOpts())) { @@ -14933,7 +15270,7 @@ } if (CXXDestructorDecl *DD = - dyn_cast(TheCall->getMethodDecl())) { + 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, @@ -14942,7 +15279,7 @@ } return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), - TheCall->getMethodDecl()); + TheCall->getDirectCallee()); } /// BuildCallToObjectOfClassType - Build a call to an object of class @@ -15167,8 +15504,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; @@ -15310,12 +15652,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, @@ -15326,7 +15675,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()); @@ -15492,37 +15842,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 @@ -15530,7 +15887,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( @@ -15560,15 +15917,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 (!DiagnoseMissingQualifiersinAddresOfOperand(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 @@ -15581,18 +15944,22 @@ 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) + return CreateBuiltinUnaryOp(UnOp->getOperatorLoc(), UO_AddrOf, + SubExpr.get()) .get(); } 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 @@ -752,7 +752,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(); // Since the 'this' expression is synthesized, we don't need to @@ -7073,7 +7074,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 @@ -7625,7 +7627,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(); @@ -7676,8 +7681,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 @@ -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 @@ -2891,6 +2891,9 @@ NewParm->setUninstantiatedDefaultArg(Arg); } + NewParm->setIsExplicitObjectParameter( + OldParm->isExplicitObjectParameter(), + 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 @@ -4423,7 +4423,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/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1703,9 +1703,12 @@ PD->setScopeInfo(scopeDepth, scopeIndex); } PD->ParmVarDeclBits.IsKNRPromoted = Record.readInt(); + PD->ParmVarDeclBits.IsExplicitObjectParameter = Record.readInt(); PD->ParmVarDeclBits.HasInheritedDefaultArg = Record.readInt(); if (Record.readInt()) // hasUninstantiatedDefaultArg. PD->setUninstantiatedDefaultArg(Record.readExpr()); + if (PD->ParmVarDeclBits.IsExplicitObjectParameter) + 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/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1158,32 +1158,27 @@ Record.push_back(D->getFunctionScopeIndex()); Record.push_back(D->getObjCDeclQualifier()); // FIXME: stable encoding Record.push_back(D->isKNRPromoted()); + Record.push_back(D->isExplicitObjectParameter()); Record.push_back(D->hasInheritedDefaultArg()); Record.push_back(D->hasUninstantiatedDefaultArg()); if (D->hasUninstantiatedDefaultArg()) Record.AddStmt(D->getUninstantiatedDefaultArg()); + if (D->isExplicitObjectParameter()) + 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. @@ -2112,7 +2112,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 = @@ -3355,7 +3355,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/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,48 @@ #endif } // namespace dr2521 + +#if __cplusplus >= 202302L +namespace dr2553 { // dr2553: 16 open +struct B { + virtual void f(this B&); // expected-error {{an explicit object parameter cannot appear in a virtual function}} + 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}} +}; +} +#endif + +#if __cplusplus >= 202302L +namespace dr2554 { // dr2554: 16 open +struct B { + virtual void f(); // expected-note {{here}} +}; + +struct D : B { + void f(this D&); // expected-error {{an explicit object parameter cannot appear in a virtual function}} +}; +} +#endif + +#if __cplusplus >= 202302L +namespace dr2561 { // dr2561: 16 open +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 open #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++20 -Wno-c++2b-extensions -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 }; @@ -117,6 +119,13 @@ int i0 = f(0); //expected-error {{no matching function for call to 'f'}} } +namespace dr2653 { // dr2653: 16 + struct Test { void f(this const auto& = Test{}); }; + // expected-error@-1 {{an explicit object parameter cannot have a default argument}} + auto L =[](this const auto& = Test{}){}; + // expected-error@-1 {{an explicit object parameter cannot have a default argument}} +} + namespace dr2654 { // dr2654: 16 void f() { int neck, tail; @@ -150,3 +159,19 @@ J j = { "ghi" }; // expected-error {{no viable constructor or deduction guide}} } + +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); +} + +} diff --git a/clang/test/CXX/over/over.load/p2-0x.cpp b/clang/test/CXX/over/over.load/p2-0x.cpp --- a/clang/test/CXX/over/over.load/p2-0x.cpp +++ b/clang/test/CXX/over/over.load/p2-0x.cpp @@ -7,10 +7,10 @@ // the same template parameter lists cannot be overloaded if any of // them, but not all, have a ref-qualifier (8.3.5). -class Y { - void h() &; - void h() const &; - void h() &&; +class Y { + void h() &; + void h() const &; + void h() &&; void i() &; // expected-note{{previous declaration}} void i() const; // expected-error{{cannot overload a member function without a ref-qualifier with a member function with ref-qualifier '&'}} diff --git a/clang/test/CXX/special/class.copy/p20.cpp b/clang/test/CXX/special/class.copy/p20.cpp --- a/clang/test/CXX/special/class.copy/p20.cpp +++ b/clang/test/CXX/special/class.copy/p20.cpp @@ -11,7 +11,7 @@ NonConstCopy &operator=(NonConstCopy&); }; -struct VirtualInheritsNonConstCopy : virtual NonConstCopy { +struct VirtualInheritsNonConstCopy : virtual NonConstCopy { VirtualInheritsNonConstCopy(); VirtualInheritsNonConstCopy &operator=(const VirtualInheritsNonConstCopy&); }; 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=(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 +#endif }; static_assert(!__has_trivial_assign(TNT), "lie deliberately for gcc compatibility"); 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,23 @@ +// RUN: %clang_cc1 -std=c++2b %s -emit-llvm -triple x86_64-linux -o - | FileCheck %s +// RUN: %clang_cc1 -std=c++2b %s -emit-llvm -triple x86_64-windows-msvc -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: } 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,47 @@ +// RUN: %clang_cc1 -std=c++2b -fno-rtti -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" +} 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.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,436 @@ +// 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 {{an 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{{a function with an explicit object parameter cannot be const}} + void h(this auto) &; // expected-error{{a function with an explicit object parameter cannot have reference qualifiers}} + void i(this auto) &&; // expected-error{{a function with an explicit object parameter cannot have reference qualifiers}} + void j(this auto) volatile; // expected-error{{a function with an explicit object parameter cannot be volatile}} + + + void variadic(this auto...); // expected-error{{an 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}} +}; + +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 {{an explicit object parameter cannot have a default argument}} + auto L =[](this const auto& = Test{}){}; + // expected-error@-1 {{an explicit object parameter cannot have a default argument}} +} + +struct CannotUseThis { + int fun(); + void f(this auto) { + this->fun(); // expected-error{{invalid use of 'this' in a function with an explicit object parameter}} + } +}; +//// +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 + } +}; + +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