Skip to content

Commit

Permalink
[C++] Implement "Deducing this" (P0847R7)
Browse files Browse the repository at this point in the history
This patch implements P0847R7 (partially),
CWG2561 and CWG2653.

Reviewed By: aaron.ballman, #clang-language-wg

Differential Revision: https://reviews.llvm.org/D140828
  • Loading branch information
cor3ntin committed Oct 2, 2023
1 parent bc7d88f commit af47517
Show file tree
Hide file tree
Showing 86 changed files with 3,028 additions and 803 deletions.
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ C++20 Feature Support

C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
- Implemented `P0847R7: Deducing this <https://wg21.link/P0847R7>`_. Some related core issues were also
implemented (`CWG2553 <https://wg21.link/CWG2553>`_, `CWG2554 <https://wg21.link/CWG2554>`_,
`CWG2653 <https://wg21.link/CWG2653>`_, `CWG2687 <https://wg21.link/CWG2687>`_). Because the
support for this feature is still experimental, the feature test macro ``__cpp_explicit_this_parameter``

C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
Expand Down
15 changes: 15 additions & 0 deletions clang/include/clang/AST/ASTLambda.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ inline bool isLambdaCallOperator(const DeclContext *DC) {
return isLambdaCallOperator(cast<CXXMethodDecl>(DC));
}

inline bool isLambdaCallWithExplicitObjectParameter(const DeclContext *DC) {
return isLambdaCallOperator(DC) &&
cast<CXXMethodDecl>(DC)->isExplicitObjectMemberFunction();
}

inline bool isLambdaCallWithImplicitObjectParameter(const DeclContext *DC) {
return isLambdaCallOperator(DC) &&
// FIXME: Checking for a null type is not great
// but lambdas with invalid captures or whose closure parameter list
// have not fully been parsed may have a call operator whose type is
// null.
!cast<CXXMethodDecl>(DC)->getType().isNull() &&
!cast<CXXMethodDecl>(DC)->isExplicitObjectMemberFunction();
}

inline bool isGenericLambdaCallOperatorSpecialization(const CXXMethodDecl *MD) {
if (!MD) return false;
const CXXRecordDecl *LambdaClass = MD->getParent();
Expand Down
32 changes: 32 additions & 0 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1809,6 +1809,18 @@ class ParmVarDecl : public VarDecl {
ParmVarDeclBits.IsKNRPromoted = promoted;
}

bool isExplicitObjectParameter() const {
return ExplicitObjectParameterIntroducerLoc.isValid();
}

void setExplicitObjectParameterLoc(SourceLocation Loc) {
ExplicitObjectParameterIntroducerLoc = Loc;
}

SourceLocation getExplicitObjectParamThisLoc() const {
return ExplicitObjectParameterIntroducerLoc;
}

Expr *getDefaultArg();
const Expr *getDefaultArg() const {
return const_cast<ParmVarDecl *>(this)->getDefaultArg();
Expand Down Expand Up @@ -1875,7 +1887,10 @@ class ParmVarDecl : public VarDecl {
static bool classofKind(Kind K) { return K == ParmVar; }

private:
friend class ASTDeclReader;

enum { ParameterIndexSentinel = (1 << NumParameterIndexBits) - 1 };
SourceLocation ExplicitObjectParameterIntroducerLoc;

void setParameterIndex(unsigned parameterIndex) {
if (parameterIndex >= ParameterIndexSentinel) {
Expand Down Expand Up @@ -2640,6 +2655,23 @@ class FunctionDecl : public DeclaratorDecl,
/// parameters have default arguments (in C++).
unsigned getMinRequiredArguments() const;

/// Returns the minimum number of non-object arguments needed to call this
/// function. This produces the same value as getMinRequiredArguments except
/// it does not count the explicit object argument, if any.
unsigned getMinRequiredExplicitArguments() const;

bool hasCXXExplicitFunctionObjectParameter() const;

unsigned getNumNonObjectParams() const;

const ParmVarDecl *getNonObjectParameter(unsigned I) const {
return getParamDecl(hasCXXExplicitFunctionObjectParameter() ? I + 1 : I);
}

ParmVarDecl *getNonObjectParameter(unsigned I) {
return getParamDecl(hasCXXExplicitFunctionObjectParameter() ? I + 1 : I);
}

/// Determine whether this function has a single parameter, or multiple
/// parameters where all but the first have default arguments.
///
Expand Down
24 changes: 20 additions & 4 deletions clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -2061,6 +2061,17 @@ class CXXMethodDecl : public FunctionDecl {
bool isStatic() const;
bool isInstance() const { return !isStatic(); }

/// [C++2b][dcl.fct]/p7
/// An explicit object member function is a non-static
/// member function with an explicit object parameter. e.g.,
/// void func(this SomeType);
bool isExplicitObjectMemberFunction() const;

/// [C++2b][dcl.fct]/p7
/// An implicit object member function is a non-static
/// member function without an explicit object parameter.
bool isImplicitObjectMemberFunction() const;

/// Returns true if the given operator is implicitly static in a record
/// context.
static bool isStaticOverloadedOperator(OverloadedOperatorKind OOK) {
Expand Down Expand Up @@ -2169,14 +2180,19 @@ class CXXMethodDecl : public FunctionDecl {
/// Return the type of the object pointed by \c this.
///
/// See getThisType() for usage restriction.
QualType getThisObjectType() const;

QualType getFunctionObjectParameterReferenceType() const;
QualType getFunctionObjectParameterType() const {
return getFunctionObjectParameterReferenceType().getNonReferenceType();
}

unsigned getNumExplicitParams() const {
return getNumParams() - (isExplicitObjectMemberFunction() ? 1 : 0);
}

static QualType getThisType(const FunctionProtoType *FPT,
const CXXRecordDecl *Decl);

static QualType getThisObjectType(const FunctionProtoType *FPT,
const CXXRecordDecl *Decl);

Qualifiers getMethodQualifiers() const {
return getType()->castAs<FunctionProtoType>()->getMethodQuals();
}
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1449,6 +1449,16 @@ class DeclRefExpr final
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;
}
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ class alignas(void *) Stmt {
unsigned HasFoundDecl : 1;
unsigned HadMultipleCandidates : 1;
unsigned RefersToEnclosingVariableOrCapture : 1;
unsigned CapturedByCopyInLambdaWithExplicitObjectParameter : 1;
unsigned NonOdrUseReason : 2;
unsigned IsImmediateEscalating : 1;

Expand Down
86 changes: 61 additions & 25 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -4681,12 +4681,14 @@ def note_ovl_candidate_non_deduced_mismatch_qualified : Note<
// Note that we don't treat templates differently for this diagnostic.
def note_ovl_candidate_arity : Note<"candidate "
"%sub{select_ovl_candidate_kind}0,1,2 not viable: "
"requires%select{ at least| at most|}3 %4 argument%s4, but %5 "
"requires%select{ at least| at most|}3 %4 "
"%select{|non-object }6argument%s4, but %5 "
"%plural{1:was|:were}5 provided">;

def note_ovl_candidate_arity_one : Note<"candidate "
"%sub{select_ovl_candidate_kind}0,1,2 not viable: "
"%select{requires at least|allows at most single|requires single}3 "
"%select{|non-object }6"
"argument %4, but %plural{0:no|:%5}5 arguments were provided">;

def note_ovl_candidate_deleted : Note<
Expand Down Expand Up @@ -4869,7 +4871,7 @@ def err_ovl_deleted_object_call : Error<
"call to deleted function call operator in type %0">;
def note_ovl_surrogate_cand : Note<"conversion candidate of type %0">;
def err_member_call_without_object : Error<
"call to non-static member function without an object argument">;
"call to %select{non-static|explicit}0 member function without an object argument">;

// C++ Address of Overloaded Function
def err_addr_ovl_no_viable : Error<
Expand Down Expand Up @@ -7289,21 +7291,43 @@ def note_logical_not_silence_with_parens : Note<
"add parentheses around left hand side expression to silence this warning">;

def err_invalid_this_use : Error<
"invalid use of 'this' outside of a non-static member function">;
"invalid use of 'this' %select{outside of a non-static member function"
"|in a function with an explicit object parameter}0">;
def err_this_static_member_func : Error<
"'this' cannot be%select{| implicitly}0 used in a static member function "
"declaration">;
def err_invalid_member_use_in_static_method : Error<
"invalid use of member %0 in static member function">;
def err_invalid_member_use_in_method : Error<
"invalid use of member %0 in %select{static|explicit object}1 member function">;

def err_invalid_qualified_function_type : Error<
"%select{non-member function|static member function|deduction guide}0 "
"%select{non-member function|static member function|explicit object member function|deduction guide}0 "
"%select{of type %2 |}1cannot have '%3' qualifier">;
def err_compound_qualified_function_type : Error<
"%select{block pointer|pointer|reference}0 to function type %select{%2 |}1"
"cannot have '%3' qualifier">;
def err_qualified_function_typeid : Error<
"type operand %0 of 'typeid' cannot have '%1' qualifier">;

def err_cxx20_deducing_this : Error<
"explicit object parameters are incompatible with C++ standards before C++2b">;
def err_explicit_object_default_arg: Error<
"the explicit object parameter cannot have a default argument">;
def err_explicit_object_parameter_pack: Error<
"the explicit object parameter cannot be a function parameter pack">;
def err_explicit_object_parameter_must_be_first: Error<
"an explicit object parameter can only appear as the first parameter "
"of the %select{function|lambda}0">;
def err_explicit_object_parameter_nonmember: Error<
"an explicit object parameter cannot appear in a "
"%select{static|virtual|non-member}0 %select{function|lambda}1">;
def err_explicit_object_parameter_constructor: Error<
"an explicit object parameter cannot appear in a %select{constructor|destructor}0">;
def err_explicit_object_parameter_mutable: Error<
"a lambda with an explicit object parameter cannot be mutable">;
def err_invalid_explicit_object_type_in_lambda: Error<
"invalid explicit object parameter type %0 in lambda with capture; "
"the type must be the same as, or derived from, the lambda">;

def err_ref_qualifier_overload : Error<
"cannot overload a member function %select{without a ref-qualifier|with "
"ref-qualifier '&'|with ref-qualifier '&&'}0 with a member function %select{"
Expand Down Expand Up @@ -8525,53 +8549,65 @@ def err_call_function_incomplete_return : Error<
def err_call_incomplete_argument : Error<
"argument type %0 is incomplete">;
def err_typecheck_call_too_few_args : Error<
"too few %select{|||execution configuration }0arguments to "
"too few %select{|||execution configuration }0"
"%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected %1, have %2">;
def err_typecheck_call_too_few_args_one : Error<
"too few %select{|||execution configuration }0arguments to "
"too few %select{|||execution configuration }0"
"%select{|non-object }2arguments to "
"%select{function|block|method|kernel function}0 call, "
"single argument %1 was not specified">;
def err_typecheck_call_too_few_args_at_least : Error<
"too few %select{|||execution configuration }0arguments to "
"too few %select{|||execution configuration }0"
"%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected at least %1, have %2">;
def err_typecheck_call_too_few_args_at_least_one : Error<
"too few %select{|||execution configuration }0arguments to "
"too few %select{|||execution configuration }0"
"%select{|non-object }2arguments to "
"%select{function|block|method|kernel function}0 call, "
"at least argument %1 must be specified">;
def err_typecheck_call_too_few_args_suggest : Error<
"too few %select{|||execution configuration }0arguments to "
"too few %select{|||execution configuration }0"
"%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected %1, have %2; did you mean %3?">;
"expected %1, have %2; did you mean %4?">;
def err_typecheck_call_too_few_args_at_least_suggest : Error<
"too few %select{|||execution configuration }0arguments to "
"too few %select{|||execution configuration }0"
"%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected at least %1, have %2; did you mean %3?">;
"expected at least %1, have %2; did you mean %4?">;
def err_typecheck_call_too_many_args : Error<
"too many %select{|||execution configuration }0arguments to "
"too many %select{|||execution configuration }0"
"%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected %1, have %2">;
def err_typecheck_call_too_many_args_one : Error<
"too many %select{|||execution configuration }0arguments to "
"too many %select{|||execution configuration }0"
"%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected single argument %1, have %2 arguments">;
def err_typecheck_call_too_many_args_at_most : Error<
"too many %select{|||execution configuration }0arguments to "
"too many %select{|||execution configuration }0"
"%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected at most %1, have %2">;
def err_typecheck_call_too_many_args_at_most_one : Error<
"too many %select{|||execution configuration }0arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected at most single argument %1, have %2 arguments">;
"expected at most single %select{|non-object }3argument %1, "
"have %2%select{|non-object}3 arguments">;
def err_typecheck_call_too_many_args_suggest : Error<
"too many %select{|||execution configuration }0arguments to "
"too many %select{|||execution configuration }0"
"%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected %1, have %2; did you mean %3?">;
"expected %1, have %2; did you mean %4?">;
def err_typecheck_call_too_many_args_at_most_suggest : Error<
"too many %select{|||execution configuration }0arguments to "
"too many %select{|||execution configuration }0"
"%select{|non-object }3arguments to "
"%select{function|block|method|kernel function}0 call, "
"expected at most %1, have %2; did you mean %3?">;
"expected at most %1, have %2; did you mean %4?">;

def err_arc_typecheck_convert_incompatible_pointer : Error<
"incompatible pointer types passing retainable parameter of type %0"
Expand Down Expand Up @@ -9389,10 +9425,10 @@ def warn_cxx98_compat_explicit_conversion_functions : Warning<

// C++11 defaulted functions
def err_defaulted_special_member_params : Error<
"an explicitly-defaulted %select{|copy |move }0constructor cannot "
"an explicitly-defaulted %sub{select_special_member_kind}0 cannot "
"have default arguments">;
def err_defaulted_special_member_variadic : Error<
"an explicitly-defaulted %select{|copy |move }0constructor cannot "
"an explicitly-defaulted %sub{select_special_member_kind}0 cannot "
"be variadic">;
def err_defaulted_special_member_return_type : Error<
"explicitly-defaulted %select{copy|move}0 assignment operator must "
Expand Down Expand Up @@ -9455,7 +9491,7 @@ def err_defaulted_comparison_template : Error<
"comparison operator template cannot be defaulted">;
def err_defaulted_comparison_num_args : Error<
"%select{non-member|member}0 %sub{select_defaulted_comparison_kind}1"
" comparison operator must have %select{2|1}0 parameters">;
" must have %select{2|1}0 parameters">;
def err_defaulted_comparison_param : Error<
"invalid parameter type for defaulted %sub{select_defaulted_comparison_kind}0"
"; found %1, expected %2%select{| or %4}3">;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Sema/DeclSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -2674,6 +2674,8 @@ class Declarator {
/// redeclaration time if the decl is static.
bool isStaticMember();

bool isExplicitObjectMemberFunction();

/// Returns true if this declares a constructor or a destructor.
bool isCtorOrDtor();

Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Sema/ScopeInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,8 @@ class LambdaScopeInfo final :
/// is known.
bool AfterParameterList = true;

ParmVarDecl *ExplicitObjectParameter = nullptr;

/// Source range covering the lambda introducer [...].
SourceRange IntroducerRange;

Expand Down Expand Up @@ -1042,6 +1044,8 @@ class LambdaScopeInfo final :

void visitPotentialCaptures(
llvm::function_ref<void(ValueDecl *, Expr *)> Callback) const;

bool lambdaCaptureShouldBeConst() const;
};

FunctionScopeInfo::WeakObjectProfileTy::WeakObjectProfileTy()
Expand Down
Loading

0 comments on commit af47517

Please sign in to comment.