llvm-project/clang/lib/Sema/SemaExprCXX.cpp
Oliver Hunt 1cd59264aa
[RFC] Initial implementation of P2719 (#113510)
This is a basic implementation of P2719: "Type-aware allocation and
deallocation functions" described at http://wg21.link/P2719

The proposal includes some more details but the basic change in
functionality is the addition of support for an additional implicit
parameter in operators `new` and `delete` to act as a type tag. Tag is
of type `std::type_identity<T>` where T is the concrete type being
allocated. So for example, a custom type specific allocator for `int`
say can be provided by the declaration of

  void *operator new(std::type_identity<int>, size_t, std::align_val_t);
  void  operator delete(std::type_identity<int>, void*, size_t, std::align_val_t);

However this becomes more powerful by specifying templated declarations,
for example

template <typename T> void *operator new(std::type_identity<T>, size_t, std::align_val_t);
template <typename T> void operator delete(std::type_identity<T>, void*, size_t, std::align_val_t););

Where the operators being resolved will be the concrete type being
operated over (NB. A completely unconstrained global definition as above
is not recommended as it triggers many problems similar to a general
override of the global operators).

These type aware operators can be declared as either free functions or
in class, and can be specified with or without the other implicit
parameters, with overload resolution performed according to the existing
standard parameter prioritisation, only with type parameterised
operators having higher precedence than non-type aware operators. The
only exception is destroying_delete which for reasons discussed in the
paper we do not support type-aware variants by default.
2025-04-10 17:13:10 -07:00

9980 lines
397 KiB
C++

//===--- SemaExprCXX.cpp - Semantic Analysis for Expressions --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Implements semantic analysis for C++ expressions.
///
//===----------------------------------------------------------------------===//
#include "TreeTransform.h"
#include "TypeLocBuilder.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTLambda.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DynamicRecursiveASTVisitor.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/AlignedAllocation.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TokenKinds.h"
#include "clang/Basic/TypeTraits.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaCUDA.h"
#include "clang/Sema/SemaHLSL.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaLambda.h"
#include "clang/Sema/SemaObjC.h"
#include "clang/Sema/SemaPPC.h"
#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateDeduction.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/TypeSize.h"
#include <optional>
using namespace clang;
using namespace sema;
ParsedType Sema::getInheritingConstructorName(CXXScopeSpec &SS,
SourceLocation NameLoc,
const IdentifierInfo &Name) {
NestedNameSpecifier *NNS = SS.getScopeRep();
if ([[maybe_unused]] const IdentifierInfo *II = NNS->getAsIdentifier())
assert(II == &Name && "not a constructor name");
QualType Type(NNS->translateToType(Context), 0);
// This reference to the type is located entirely at the location of the
// final identifier in the qualified-id.
return CreateParsedType(Type,
Context.getTrivialTypeSourceInfo(Type, NameLoc));
}
ParsedType Sema::getConstructorName(const IdentifierInfo &II,
SourceLocation NameLoc, Scope *S,
CXXScopeSpec &SS, bool EnteringContext) {
CXXRecordDecl *CurClass = getCurrentClass(S, &SS);
assert(CurClass && &II == CurClass->getIdentifier() &&
"not a constructor name");
// When naming a constructor as a member of a dependent context (eg, in a
// friend declaration or an inherited constructor declaration), form an
// unresolved "typename" type.
if (CurClass->isDependentContext() && !EnteringContext && SS.getScopeRep()) {
QualType T = Context.getDependentNameType(ElaboratedTypeKeyword::None,
SS.getScopeRep(), &II);
return ParsedType::make(T);
}
if (SS.isNotEmpty() && RequireCompleteDeclContext(SS, CurClass))
return ParsedType();
// Find the injected-class-name declaration. Note that we make no attempt to
// diagnose cases where the injected-class-name is shadowed: the only
// declaration that can validly shadow the injected-class-name is a
// non-static data member, and if the class contains both a non-static data
// member and a constructor then it is ill-formed (we check that in
// CheckCompletedCXXClass).
CXXRecordDecl *InjectedClassName = nullptr;
for (NamedDecl *ND : CurClass->lookup(&II)) {
auto *RD = dyn_cast<CXXRecordDecl>(ND);
if (RD && RD->isInjectedClassName()) {
InjectedClassName = RD;
break;
}
}
if (!InjectedClassName) {
if (!CurClass->isInvalidDecl()) {
// FIXME: RequireCompleteDeclContext doesn't check dependent contexts
// properly. Work around it here for now.
Diag(SS.getLastQualifierNameLoc(),
diag::err_incomplete_nested_name_spec) << CurClass << SS.getRange();
}
return ParsedType();
}
QualType T = Context.getTypeDeclType(InjectedClassName);
DiagnoseUseOfDecl(InjectedClassName, NameLoc);
MarkAnyDeclReferenced(NameLoc, InjectedClassName, /*OdrUse=*/false);
return ParsedType::make(T);
}
ParsedType Sema::getDestructorName(const IdentifierInfo &II,
SourceLocation NameLoc, Scope *S,
CXXScopeSpec &SS, ParsedType ObjectTypePtr,
bool EnteringContext) {
// Determine where to perform name lookup.
// FIXME: This area of the standard is very messy, and the current
// wording is rather unclear about which scopes we search for the
// destructor name; see core issues 399 and 555. Issue 399 in
// particular shows where the current description of destructor name
// lookup is completely out of line with existing practice, e.g.,
// this appears to be ill-formed:
//
// namespace N {
// template <typename T> struct S {
// ~S();
// };
// }
//
// void f(N::S<int>* s) {
// s->N::S<int>::~S();
// }
//
// See also PR6358 and PR6359.
//
// For now, we accept all the cases in which the name given could plausibly
// be interpreted as a correct destructor name, issuing off-by-default
// extension diagnostics on the cases that don't strictly conform to the
// C++20 rules. This basically means we always consider looking in the
// nested-name-specifier prefix, the complete nested-name-specifier, and
// the scope, and accept if we find the expected type in any of the three
// places.
if (SS.isInvalid())
return nullptr;
// Whether we've failed with a diagnostic already.
bool Failed = false;
llvm::SmallVector<NamedDecl*, 8> FoundDecls;
llvm::SmallPtrSet<CanonicalDeclPtr<Decl>, 8> FoundDeclSet;
// If we have an object type, it's because we are in a
// pseudo-destructor-expression or a member access expression, and
// we know what type we're looking for.
QualType SearchType =
ObjectTypePtr ? GetTypeFromParser(ObjectTypePtr) : QualType();
auto CheckLookupResult = [&](LookupResult &Found) -> ParsedType {
auto IsAcceptableResult = [&](NamedDecl *D) -> bool {
auto *Type = dyn_cast<TypeDecl>(D->getUnderlyingDecl());
if (!Type)
return false;
if (SearchType.isNull() || SearchType->isDependentType())
return true;
QualType T = Context.getTypeDeclType(Type);
return Context.hasSameUnqualifiedType(T, SearchType);
};
unsigned NumAcceptableResults = 0;
for (NamedDecl *D : Found) {
if (IsAcceptableResult(D))
++NumAcceptableResults;
// Don't list a class twice in the lookup failure diagnostic if it's
// found by both its injected-class-name and by the name in the enclosing
// scope.
if (auto *RD = dyn_cast<CXXRecordDecl>(D))
if (RD->isInjectedClassName())
D = cast<NamedDecl>(RD->getParent());
if (FoundDeclSet.insert(D).second)
FoundDecls.push_back(D);
}
// As an extension, attempt to "fix" an ambiguity by erasing all non-type
// results, and all non-matching results if we have a search type. It's not
// clear what the right behavior is if destructor lookup hits an ambiguity,
// but other compilers do generally accept at least some kinds of
// ambiguity.
if (Found.isAmbiguous() && NumAcceptableResults == 1) {
Diag(NameLoc, diag::ext_dtor_name_ambiguous);
LookupResult::Filter F = Found.makeFilter();
while (F.hasNext()) {
NamedDecl *D = F.next();
if (auto *TD = dyn_cast<TypeDecl>(D->getUnderlyingDecl()))
Diag(D->getLocation(), diag::note_destructor_type_here)
<< Context.getTypeDeclType(TD);
else
Diag(D->getLocation(), diag::note_destructor_nontype_here);
if (!IsAcceptableResult(D))
F.erase();
}
F.done();
}
if (Found.isAmbiguous())
Failed = true;
if (TypeDecl *Type = Found.getAsSingle<TypeDecl>()) {
if (IsAcceptableResult(Type)) {
QualType T = Context.getTypeDeclType(Type);
MarkAnyDeclReferenced(Type->getLocation(), Type, /*OdrUse=*/false);
return CreateParsedType(
Context.getElaboratedType(ElaboratedTypeKeyword::None, nullptr, T),
Context.getTrivialTypeSourceInfo(T, NameLoc));
}
}
return nullptr;
};
bool IsDependent = false;
auto LookupInObjectType = [&]() -> ParsedType {
if (Failed || SearchType.isNull())
return nullptr;
IsDependent |= SearchType->isDependentType();
LookupResult Found(*this, &II, NameLoc, LookupDestructorName);
DeclContext *LookupCtx = computeDeclContext(SearchType);
if (!LookupCtx)
return nullptr;
LookupQualifiedName(Found, LookupCtx);
return CheckLookupResult(Found);
};
auto LookupInNestedNameSpec = [&](CXXScopeSpec &LookupSS) -> ParsedType {
if (Failed)
return nullptr;
IsDependent |= isDependentScopeSpecifier(LookupSS);
DeclContext *LookupCtx = computeDeclContext(LookupSS, EnteringContext);
if (!LookupCtx)
return nullptr;
LookupResult Found(*this, &II, NameLoc, LookupDestructorName);
if (RequireCompleteDeclContext(LookupSS, LookupCtx)) {
Failed = true;
return nullptr;
}
LookupQualifiedName(Found, LookupCtx);
return CheckLookupResult(Found);
};
auto LookupInScope = [&]() -> ParsedType {
if (Failed || !S)
return nullptr;
LookupResult Found(*this, &II, NameLoc, LookupDestructorName);
LookupName(Found, S);
return CheckLookupResult(Found);
};
// C++2a [basic.lookup.qual]p6:
// In a qualified-id of the form
//
// nested-name-specifier[opt] type-name :: ~ type-name
//
// the second type-name is looked up in the same scope as the first.
//
// We interpret this as meaning that if you do a dual-scope lookup for the
// first name, you also do a dual-scope lookup for the second name, per
// C++ [basic.lookup.classref]p4:
//
// If the id-expression in a class member access is a qualified-id of the
// form
//
// class-name-or-namespace-name :: ...
//
// the class-name-or-namespace-name following the . or -> is first looked
// up in the class of the object expression and the name, if found, is used.
// Otherwise, it is looked up in the context of the entire
// postfix-expression.
//
// This looks in the same scopes as for an unqualified destructor name:
//
// C++ [basic.lookup.classref]p3:
// If the unqualified-id is ~ type-name, the type-name is looked up
// in the context of the entire postfix-expression. If the type T
// of the object expression is of a class type C, the type-name is
// also looked up in the scope of class C. At least one of the
// lookups shall find a name that refers to cv T.
//
// FIXME: The intent is unclear here. Should type-name::~type-name look in
// the scope anyway if it finds a non-matching name declared in the class?
// If both lookups succeed and find a dependent result, which result should
// we retain? (Same question for p->~type-name().)
if (NestedNameSpecifier *Prefix =
SS.isSet() ? SS.getScopeRep()->getPrefix() : nullptr) {
// This is
//
// nested-name-specifier type-name :: ~ type-name
//
// Look for the second type-name in the nested-name-specifier.
CXXScopeSpec PrefixSS;
PrefixSS.Adopt(NestedNameSpecifierLoc(Prefix, SS.location_data()));
if (ParsedType T = LookupInNestedNameSpec(PrefixSS))
return T;
} else {
// This is one of
//
// type-name :: ~ type-name
// ~ type-name
//
// Look in the scope and (if any) the object type.
if (ParsedType T = LookupInScope())
return T;
if (ParsedType T = LookupInObjectType())
return T;
}
if (Failed)
return nullptr;
if (IsDependent) {
// We didn't find our type, but that's OK: it's dependent anyway.
// FIXME: What if we have no nested-name-specifier?
QualType T =
CheckTypenameType(ElaboratedTypeKeyword::None, SourceLocation(),
SS.getWithLocInContext(Context), II, NameLoc);
return ParsedType::make(T);
}
// The remaining cases are all non-standard extensions imitating the behavior
// of various other compilers.
unsigned NumNonExtensionDecls = FoundDecls.size();
if (SS.isSet()) {
// For compatibility with older broken C++ rules and existing code,
//
// nested-name-specifier :: ~ type-name
//
// also looks for type-name within the nested-name-specifier.
if (ParsedType T = LookupInNestedNameSpec(SS)) {
Diag(SS.getEndLoc(), diag::ext_dtor_named_in_wrong_scope)
<< SS.getRange()
<< FixItHint::CreateInsertion(SS.getEndLoc(),
("::" + II.getName()).str());
return T;
}
// For compatibility with other compilers and older versions of Clang,
//
// nested-name-specifier type-name :: ~ type-name
//
// also looks for type-name in the scope. Unfortunately, we can't
// reasonably apply this fallback for dependent nested-name-specifiers.
if (SS.isValid() && SS.getScopeRep()->getPrefix()) {
if (ParsedType T = LookupInScope()) {
Diag(SS.getEndLoc(), diag::ext_qualified_dtor_named_in_lexical_scope)
<< FixItHint::CreateRemoval(SS.getRange());
Diag(FoundDecls.back()->getLocation(), diag::note_destructor_type_here)
<< GetTypeFromParser(T);
return T;
}
}
}
// We didn't find anything matching; tell the user what we did find (if
// anything).
// Don't tell the user about declarations we shouldn't have found.
FoundDecls.resize(NumNonExtensionDecls);
// List types before non-types.
std::stable_sort(FoundDecls.begin(), FoundDecls.end(),
[](NamedDecl *A, NamedDecl *B) {
return isa<TypeDecl>(A->getUnderlyingDecl()) >
isa<TypeDecl>(B->getUnderlyingDecl());
});
// Suggest a fixit to properly name the destroyed type.
auto MakeFixItHint = [&]{
const CXXRecordDecl *Destroyed = nullptr;
// FIXME: If we have a scope specifier, suggest its last component?
if (!SearchType.isNull())
Destroyed = SearchType->getAsCXXRecordDecl();
else if (S)
Destroyed = dyn_cast_or_null<CXXRecordDecl>(S->getEntity());
if (Destroyed)
return FixItHint::CreateReplacement(SourceRange(NameLoc),
Destroyed->getNameAsString());
return FixItHint();
};
if (FoundDecls.empty()) {
// FIXME: Attempt typo-correction?
Diag(NameLoc, diag::err_undeclared_destructor_name)
<< &II << MakeFixItHint();
} else if (!SearchType.isNull() && FoundDecls.size() == 1) {
if (auto *TD = dyn_cast<TypeDecl>(FoundDecls[0]->getUnderlyingDecl())) {
assert(!SearchType.isNull() &&
"should only reject a type result if we have a search type");
QualType T = Context.getTypeDeclType(TD);
Diag(NameLoc, diag::err_destructor_expr_type_mismatch)
<< T << SearchType << MakeFixItHint();
} else {
Diag(NameLoc, diag::err_destructor_expr_nontype)
<< &II << MakeFixItHint();
}
} else {
Diag(NameLoc, SearchType.isNull() ? diag::err_destructor_name_nontype
: diag::err_destructor_expr_mismatch)
<< &II << SearchType << MakeFixItHint();
}
for (NamedDecl *FoundD : FoundDecls) {
if (auto *TD = dyn_cast<TypeDecl>(FoundD->getUnderlyingDecl()))
Diag(FoundD->getLocation(), diag::note_destructor_type_here)
<< Context.getTypeDeclType(TD);
else
Diag(FoundD->getLocation(), diag::note_destructor_nontype_here)
<< FoundD;
}
return nullptr;
}
ParsedType Sema::getDestructorTypeForDecltype(const DeclSpec &DS,
ParsedType ObjectType) {
if (DS.getTypeSpecType() == DeclSpec::TST_error)
return nullptr;
if (DS.getTypeSpecType() == DeclSpec::TST_decltype_auto) {
Diag(DS.getTypeSpecTypeLoc(), diag::err_decltype_auto_invalid);
return nullptr;
}
assert(DS.getTypeSpecType() == DeclSpec::TST_decltype &&
"unexpected type in getDestructorType");
QualType T = BuildDecltypeType(DS.getRepAsExpr());
// If we know the type of the object, check that the correct destructor
// type was named now; we can give better diagnostics this way.
QualType SearchType = GetTypeFromParser(ObjectType);
if (!SearchType.isNull() && !SearchType->isDependentType() &&
!Context.hasSameUnqualifiedType(T, SearchType)) {
Diag(DS.getTypeSpecTypeLoc(), diag::err_destructor_expr_type_mismatch)
<< T << SearchType;
return nullptr;
}
return ParsedType::make(T);
}
bool Sema::checkLiteralOperatorId(const CXXScopeSpec &SS,
const UnqualifiedId &Name, bool IsUDSuffix) {
assert(Name.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId);
if (!IsUDSuffix) {
// [over.literal] p8
//
// double operator""_Bq(long double); // OK: not a reserved identifier
// double operator"" _Bq(long double); // ill-formed, no diagnostic required
const IdentifierInfo *II = Name.Identifier;
ReservedIdentifierStatus Status = II->isReserved(PP.getLangOpts());
SourceLocation Loc = Name.getEndLoc();
auto Hint = FixItHint::CreateReplacement(
Name.getSourceRange(),
(StringRef("operator\"\"") + II->getName()).str());
// Only emit this diagnostic if we start with an underscore, else the
// diagnostic for C++11 requiring a space between the quotes and the
// identifier conflicts with this and gets confusing. The diagnostic stating
// this is a reserved name should force the underscore, which gets this
// back.
if (II->isReservedLiteralSuffixId() !=
ReservedLiteralSuffixIdStatus::NotStartsWithUnderscore)
Diag(Loc, diag::warn_deprecated_literal_operator_id) << II << Hint;
if (isReservedInAllContexts(Status))
Diag(Loc, diag::warn_reserved_extern_symbol)
<< II << static_cast<int>(Status) << Hint;
}
if (!SS.isValid())
return false;
switch (SS.getScopeRep()->getKind()) {
case NestedNameSpecifier::Identifier:
case NestedNameSpecifier::TypeSpec:
// Per C++11 [over.literal]p2, literal operators can only be declared at
// namespace scope. Therefore, this unqualified-id cannot name anything.
// Reject it early, because we have no AST representation for this in the
// case where the scope is dependent.
Diag(Name.getBeginLoc(), diag::err_literal_operator_id_outside_namespace)
<< SS.getScopeRep();
return true;
case NestedNameSpecifier::Global:
case NestedNameSpecifier::Super:
case NestedNameSpecifier::Namespace:
case NestedNameSpecifier::NamespaceAlias:
return false;
}
llvm_unreachable("unknown nested name specifier kind");
}
ExprResult Sema::BuildCXXTypeId(QualType TypeInfoType,
SourceLocation TypeidLoc,
TypeSourceInfo *Operand,
SourceLocation RParenLoc) {
// C++ [expr.typeid]p4:
// The top-level cv-qualifiers of the lvalue expression or the type-id
// that is the operand of typeid are always ignored.
// If the type of the type-id is a class type or a reference to a class
// type, the class shall be completely-defined.
Qualifiers Quals;
QualType T
= Context.getUnqualifiedArrayType(Operand->getType().getNonReferenceType(),
Quals);
if (T->getAs<RecordType>() &&
RequireCompleteType(TypeidLoc, T, diag::err_incomplete_typeid))
return ExprError();
if (T->isVariablyModifiedType())
return ExprError(Diag(TypeidLoc, diag::err_variably_modified_typeid) << T);
if (CheckQualifiedFunctionForTypeId(T, TypeidLoc))
return ExprError();
return new (Context) CXXTypeidExpr(TypeInfoType.withConst(), Operand,
SourceRange(TypeidLoc, RParenLoc));
}
ExprResult Sema::BuildCXXTypeId(QualType TypeInfoType,
SourceLocation TypeidLoc,
Expr *E,
SourceLocation RParenLoc) {
bool WasEvaluated = false;
if (E && !E->isTypeDependent()) {
if (E->hasPlaceholderType()) {
ExprResult result = CheckPlaceholderExpr(E);
if (result.isInvalid()) return ExprError();
E = result.get();
}
QualType T = E->getType();
if (const RecordType *RecordT = T->getAs<RecordType>()) {
CXXRecordDecl *RecordD = cast<CXXRecordDecl>(RecordT->getDecl());
// C++ [expr.typeid]p3:
// [...] If the type of the expression is a class type, the class
// shall be completely-defined.
if (RequireCompleteType(TypeidLoc, T, diag::err_incomplete_typeid))
return ExprError();
// C++ [expr.typeid]p3:
// When typeid is applied to an expression other than an glvalue of a
// polymorphic class type [...] [the] expression is an unevaluated
// operand. [...]
if (RecordD->isPolymorphic() && E->isGLValue()) {
if (isUnevaluatedContext()) {
// The operand was processed in unevaluated context, switch the
// context and recheck the subexpression.
ExprResult Result = TransformToPotentiallyEvaluated(E);
if (Result.isInvalid())
return ExprError();
E = Result.get();
}
// We require a vtable to query the type at run time.
MarkVTableUsed(TypeidLoc, RecordD);
WasEvaluated = true;
}
}
ExprResult Result = CheckUnevaluatedOperand(E);
if (Result.isInvalid())
return ExprError();
E = Result.get();
// C++ [expr.typeid]p4:
// [...] If the type of the type-id is a reference to a possibly
// cv-qualified type, the result of the typeid expression refers to a
// std::type_info object representing the cv-unqualified referenced
// type.
Qualifiers Quals;
QualType UnqualT = Context.getUnqualifiedArrayType(T, Quals);
if (!Context.hasSameType(T, UnqualT)) {
T = UnqualT;
E = ImpCastExprToType(E, UnqualT, CK_NoOp, E->getValueKind()).get();
}
}
if (E->getType()->isVariablyModifiedType())
return ExprError(Diag(TypeidLoc, diag::err_variably_modified_typeid)
<< E->getType());
else if (!inTemplateInstantiation() &&
E->HasSideEffects(Context, WasEvaluated)) {
// The expression operand for typeid is in an unevaluated expression
// context, so side effects could result in unintended consequences.
Diag(E->getExprLoc(), WasEvaluated
? diag::warn_side_effects_typeid
: diag::warn_side_effects_unevaluated_context);
}
return new (Context) CXXTypeidExpr(TypeInfoType.withConst(), E,
SourceRange(TypeidLoc, RParenLoc));
}
/// ActOnCXXTypeidOfType - Parse typeid( type-id ) or typeid (expression);
ExprResult
Sema::ActOnCXXTypeid(SourceLocation OpLoc, SourceLocation LParenLoc,
bool isType, void *TyOrExpr, SourceLocation RParenLoc) {
// typeid is not supported in OpenCL.
if (getLangOpts().OpenCLCPlusPlus) {
return ExprError(Diag(OpLoc, diag::err_openclcxx_not_supported)
<< "typeid");
}
// Find the std::type_info type.
if (!getStdNamespace())
return ExprError(Diag(OpLoc, diag::err_need_header_before_typeid));
if (!CXXTypeInfoDecl) {
IdentifierInfo *TypeInfoII = &PP.getIdentifierTable().get("type_info");
LookupResult R(*this, TypeInfoII, SourceLocation(), LookupTagName);
LookupQualifiedName(R, getStdNamespace());
CXXTypeInfoDecl = R.getAsSingle<RecordDecl>();
// Microsoft's typeinfo doesn't have type_info in std but in the global
// namespace if _HAS_EXCEPTIONS is defined to 0. See PR13153.
if (!CXXTypeInfoDecl && LangOpts.MSVCCompat) {
LookupQualifiedName(R, Context.getTranslationUnitDecl());
CXXTypeInfoDecl = R.getAsSingle<RecordDecl>();
}
if (!CXXTypeInfoDecl)
return ExprError(Diag(OpLoc, diag::err_need_header_before_typeid));
}
if (!getLangOpts().RTTI) {
return ExprError(Diag(OpLoc, diag::err_no_typeid_with_fno_rtti));
}
QualType TypeInfoType = Context.getTypeDeclType(CXXTypeInfoDecl);
if (isType) {
// The operand is a type; handle it as such.
TypeSourceInfo *TInfo = nullptr;
QualType T = GetTypeFromParser(ParsedType::getFromOpaquePtr(TyOrExpr),
&TInfo);
if (T.isNull())
return ExprError();
if (!TInfo)
TInfo = Context.getTrivialTypeSourceInfo(T, OpLoc);
return BuildCXXTypeId(TypeInfoType, OpLoc, TInfo, RParenLoc);
}
// The operand is an expression.
ExprResult Result =
BuildCXXTypeId(TypeInfoType, OpLoc, (Expr *)TyOrExpr, RParenLoc);
if (!getLangOpts().RTTIData && !Result.isInvalid())
if (auto *CTE = dyn_cast<CXXTypeidExpr>(Result.get()))
if (CTE->isPotentiallyEvaluated() && !CTE->isMostDerived(Context))
Diag(OpLoc, diag::warn_no_typeid_with_rtti_disabled)
<< (getDiagnostics().getDiagnosticOptions().getFormat() ==
DiagnosticOptions::MSVC);
return Result;
}
/// Grabs __declspec(uuid()) off a type, or returns 0 if we cannot resolve to
/// a single GUID.
static void
getUuidAttrOfType(Sema &SemaRef, QualType QT,
llvm::SmallSetVector<const UuidAttr *, 1> &UuidAttrs) {
// Optionally remove one level of pointer, reference or array indirection.
const Type *Ty = QT.getTypePtr();
if (QT->isPointerOrReferenceType())
Ty = QT->getPointeeType().getTypePtr();
else if (QT->isArrayType())
Ty = Ty->getBaseElementTypeUnsafe();
const auto *TD = Ty->getAsTagDecl();
if (!TD)
return;
if (const auto *Uuid = TD->getMostRecentDecl()->getAttr<UuidAttr>()) {
UuidAttrs.insert(Uuid);
return;
}
// __uuidof can grab UUIDs from template arguments.
if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(TD)) {
const TemplateArgumentList &TAL = CTSD->getTemplateArgs();
for (const TemplateArgument &TA : TAL.asArray()) {
const UuidAttr *UuidForTA = nullptr;
if (TA.getKind() == TemplateArgument::Type)
getUuidAttrOfType(SemaRef, TA.getAsType(), UuidAttrs);
else if (TA.getKind() == TemplateArgument::Declaration)
getUuidAttrOfType(SemaRef, TA.getAsDecl()->getType(), UuidAttrs);
if (UuidForTA)
UuidAttrs.insert(UuidForTA);
}
}
}
ExprResult Sema::BuildCXXUuidof(QualType Type,
SourceLocation TypeidLoc,
TypeSourceInfo *Operand,
SourceLocation RParenLoc) {
MSGuidDecl *Guid = nullptr;
if (!Operand->getType()->isDependentType()) {
llvm::SmallSetVector<const UuidAttr *, 1> UuidAttrs;
getUuidAttrOfType(*this, Operand->getType(), UuidAttrs);
if (UuidAttrs.empty())
return ExprError(Diag(TypeidLoc, diag::err_uuidof_without_guid));
if (UuidAttrs.size() > 1)
return ExprError(Diag(TypeidLoc, diag::err_uuidof_with_multiple_guids));
Guid = UuidAttrs.back()->getGuidDecl();
}
return new (Context)
CXXUuidofExpr(Type, Operand, Guid, SourceRange(TypeidLoc, RParenLoc));
}
ExprResult Sema::BuildCXXUuidof(QualType Type, SourceLocation TypeidLoc,
Expr *E, SourceLocation RParenLoc) {
MSGuidDecl *Guid = nullptr;
if (!E->getType()->isDependentType()) {
if (E->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) {
// A null pointer results in {00000000-0000-0000-0000-000000000000}.
Guid = Context.getMSGuidDecl(MSGuidDecl::Parts{});
} else {
llvm::SmallSetVector<const UuidAttr *, 1> UuidAttrs;
getUuidAttrOfType(*this, E->getType(), UuidAttrs);
if (UuidAttrs.empty())
return ExprError(Diag(TypeidLoc, diag::err_uuidof_without_guid));
if (UuidAttrs.size() > 1)
return ExprError(Diag(TypeidLoc, diag::err_uuidof_with_multiple_guids));
Guid = UuidAttrs.back()->getGuidDecl();
}
}
return new (Context)
CXXUuidofExpr(Type, E, Guid, SourceRange(TypeidLoc, RParenLoc));
}
/// ActOnCXXUuidof - Parse __uuidof( type-id ) or __uuidof (expression);
ExprResult
Sema::ActOnCXXUuidof(SourceLocation OpLoc, SourceLocation LParenLoc,
bool isType, void *TyOrExpr, SourceLocation RParenLoc) {
QualType GuidType = Context.getMSGuidType();
GuidType.addConst();
if (isType) {
// The operand is a type; handle it as such.
TypeSourceInfo *TInfo = nullptr;
QualType T = GetTypeFromParser(ParsedType::getFromOpaquePtr(TyOrExpr),
&TInfo);
if (T.isNull())
return ExprError();
if (!TInfo)
TInfo = Context.getTrivialTypeSourceInfo(T, OpLoc);
return BuildCXXUuidof(GuidType, OpLoc, TInfo, RParenLoc);
}
// The operand is an expression.
return BuildCXXUuidof(GuidType, OpLoc, (Expr*)TyOrExpr, RParenLoc);
}
ExprResult
Sema::ActOnCXXBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind) {
assert((Kind == tok::kw_true || Kind == tok::kw_false) &&
"Unknown C++ Boolean value!");
return new (Context)
CXXBoolLiteralExpr(Kind == tok::kw_true, Context.BoolTy, OpLoc);
}
ExprResult
Sema::ActOnCXXNullPtrLiteral(SourceLocation Loc) {
return new (Context) CXXNullPtrLiteralExpr(Context.NullPtrTy, Loc);
}
ExprResult
Sema::ActOnCXXThrow(Scope *S, SourceLocation OpLoc, Expr *Ex) {
bool IsThrownVarInScope = false;
if (Ex) {
// C++0x [class.copymove]p31:
// When certain criteria are met, an implementation is allowed to omit the
// copy/move construction of a class object [...]
//
// - in a throw-expression, when the operand is the name of a
// non-volatile automatic object (other than a function or catch-
// clause parameter) whose scope does not extend beyond the end of the
// innermost enclosing try-block (if there is one), the copy/move
// operation from the operand to the exception object (15.1) can be
// omitted by constructing the automatic object directly into the
// exception object
if (const auto *DRE = dyn_cast<DeclRefExpr>(Ex->IgnoreParens()))
if (const auto *Var = dyn_cast<VarDecl>(DRE->getDecl());
Var && Var->hasLocalStorage() &&
!Var->getType().isVolatileQualified()) {
for (; S; S = S->getParent()) {
if (S->isDeclScope(Var)) {
IsThrownVarInScope = true;
break;
}
// FIXME: Many of the scope checks here seem incorrect.
if (S->getFlags() &
(Scope::FnScope | Scope::ClassScope | Scope::BlockScope |
Scope::ObjCMethodScope | Scope::TryScope))
break;
}
}
}
return BuildCXXThrow(OpLoc, Ex, IsThrownVarInScope);
}
ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex,
bool IsThrownVarInScope) {
const llvm::Triple &T = Context.getTargetInfo().getTriple();
const bool IsOpenMPGPUTarget =
getLangOpts().OpenMPIsTargetDevice && (T.isNVPTX() || T.isAMDGCN());
// Don't report an error if 'throw' is used in system headers or in an OpenMP
// target region compiled for a GPU architecture.
if (!IsOpenMPGPUTarget && !getLangOpts().CXXExceptions &&
!getSourceManager().isInSystemHeader(OpLoc) && !getLangOpts().CUDA) {
// Delay error emission for the OpenMP device code.
targetDiag(OpLoc, diag::err_exceptions_disabled) << "throw";
}
// In OpenMP target regions, we replace 'throw' with a trap on GPU targets.
if (IsOpenMPGPUTarget)
targetDiag(OpLoc, diag::warn_throw_not_valid_on_target) << T.str();
// Exceptions aren't allowed in CUDA device code.
if (getLangOpts().CUDA)
CUDA().DiagIfDeviceCode(OpLoc, diag::err_cuda_device_exceptions)
<< "throw" << llvm::to_underlying(CUDA().CurrentTarget());
if (getCurScope() && getCurScope()->isOpenMPSimdDirectiveScope())
Diag(OpLoc, diag::err_omp_simd_region_cannot_use_stmt) << "throw";
// Exceptions that escape a compute construct are ill-formed.
if (getLangOpts().OpenACC && getCurScope() &&
getCurScope()->isInOpenACCComputeConstructScope(Scope::TryScope))
Diag(OpLoc, diag::err_acc_branch_in_out_compute_construct)
<< /*throw*/ 2 << /*out of*/ 0;
if (Ex && !Ex->isTypeDependent()) {
// Initialize the exception result. This implicitly weeds out
// abstract types or types with inaccessible copy constructors.
// C++0x [class.copymove]p31:
// When certain criteria are met, an implementation is allowed to omit the
// copy/move construction of a class object [...]
//
// - in a throw-expression, when the operand is the name of a
// non-volatile automatic object (other than a function or
// catch-clause
// parameter) whose scope does not extend beyond the end of the
// innermost enclosing try-block (if there is one), the copy/move
// operation from the operand to the exception object (15.1) can be
// omitted by constructing the automatic object directly into the
// exception object
NamedReturnInfo NRInfo =
IsThrownVarInScope ? getNamedReturnInfo(Ex) : NamedReturnInfo();
QualType ExceptionObjectTy = Context.getExceptionObjectType(Ex->getType());
if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex))
return ExprError();
InitializedEntity Entity =
InitializedEntity::InitializeException(OpLoc, ExceptionObjectTy);
ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRInfo, Ex);
if (Res.isInvalid())
return ExprError();
Ex = Res.get();
}
// PPC MMA non-pointer types are not allowed as throw expr types.
if (Ex && Context.getTargetInfo().getTriple().isPPC64())
PPC().CheckPPCMMAType(Ex->getType(), Ex->getBeginLoc());
return new (Context)
CXXThrowExpr(Ex, Context.VoidTy, OpLoc, IsThrownVarInScope);
}
static void
collectPublicBases(CXXRecordDecl *RD,
llvm::DenseMap<CXXRecordDecl *, unsigned> &SubobjectsSeen,
llvm::SmallPtrSetImpl<CXXRecordDecl *> &VBases,
llvm::SetVector<CXXRecordDecl *> &PublicSubobjectsSeen,
bool ParentIsPublic) {
for (const CXXBaseSpecifier &BS : RD->bases()) {
CXXRecordDecl *BaseDecl = BS.getType()->getAsCXXRecordDecl();
bool NewSubobject;
// Virtual bases constitute the same subobject. Non-virtual bases are
// always distinct subobjects.
if (BS.isVirtual())
NewSubobject = VBases.insert(BaseDecl).second;
else
NewSubobject = true;
if (NewSubobject)
++SubobjectsSeen[BaseDecl];
// Only add subobjects which have public access throughout the entire chain.
bool PublicPath = ParentIsPublic && BS.getAccessSpecifier() == AS_public;
if (PublicPath)
PublicSubobjectsSeen.insert(BaseDecl);
// Recurse on to each base subobject.
collectPublicBases(BaseDecl, SubobjectsSeen, VBases, PublicSubobjectsSeen,
PublicPath);
}
}
static void getUnambiguousPublicSubobjects(
CXXRecordDecl *RD, llvm::SmallVectorImpl<CXXRecordDecl *> &Objects) {
llvm::DenseMap<CXXRecordDecl *, unsigned> SubobjectsSeen;
llvm::SmallSet<CXXRecordDecl *, 2> VBases;
llvm::SetVector<CXXRecordDecl *> PublicSubobjectsSeen;
SubobjectsSeen[RD] = 1;
PublicSubobjectsSeen.insert(RD);
collectPublicBases(RD, SubobjectsSeen, VBases, PublicSubobjectsSeen,
/*ParentIsPublic=*/true);
for (CXXRecordDecl *PublicSubobject : PublicSubobjectsSeen) {
// Skip ambiguous objects.
if (SubobjectsSeen[PublicSubobject] > 1)
continue;
Objects.push_back(PublicSubobject);
}
}
bool Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc,
QualType ExceptionObjectTy, Expr *E) {
// If the type of the exception would be an incomplete type or a pointer
// to an incomplete type other than (cv) void the program is ill-formed.
QualType Ty = ExceptionObjectTy;
bool isPointer = false;
if (const PointerType* Ptr = Ty->getAs<PointerType>()) {
Ty = Ptr->getPointeeType();
isPointer = true;
}
// Cannot throw WebAssembly reference type.
if (Ty.isWebAssemblyReferenceType()) {
Diag(ThrowLoc, diag::err_wasm_reftype_tc) << 0 << E->getSourceRange();
return true;
}
// Cannot throw WebAssembly table.
if (isPointer && Ty.isWebAssemblyReferenceType()) {
Diag(ThrowLoc, diag::err_wasm_table_art) << 2 << E->getSourceRange();
return true;
}
if (!isPointer || !Ty->isVoidType()) {
if (RequireCompleteType(ThrowLoc, Ty,
isPointer ? diag::err_throw_incomplete_ptr
: diag::err_throw_incomplete,
E->getSourceRange()))
return true;
if (!isPointer && Ty->isSizelessType()) {
Diag(ThrowLoc, diag::err_throw_sizeless) << Ty << E->getSourceRange();
return true;
}
if (RequireNonAbstractType(ThrowLoc, ExceptionObjectTy,
diag::err_throw_abstract_type, E))
return true;
}
// If the exception has class type, we need additional handling.
CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
if (!RD)
return false;
// If we are throwing a polymorphic class type or pointer thereof,
// exception handling will make use of the vtable.
MarkVTableUsed(ThrowLoc, RD);
// If a pointer is thrown, the referenced object will not be destroyed.
if (isPointer)
return false;
// If the class has a destructor, we must be able to call it.
if (!RD->hasIrrelevantDestructor()) {
if (CXXDestructorDecl *Destructor = LookupDestructor(RD)) {
MarkFunctionReferenced(E->getExprLoc(), Destructor);
CheckDestructorAccess(E->getExprLoc(), Destructor,
PDiag(diag::err_access_dtor_exception) << Ty);
if (DiagnoseUseOfDecl(Destructor, E->getExprLoc()))
return true;
}
}
// The MSVC ABI creates a list of all types which can catch the exception
// object. This list also references the appropriate copy constructor to call
// if the object is caught by value and has a non-trivial copy constructor.
if (Context.getTargetInfo().getCXXABI().isMicrosoft()) {
// We are only interested in the public, unambiguous bases contained within
// the exception object. Bases which are ambiguous or otherwise
// inaccessible are not catchable types.
llvm::SmallVector<CXXRecordDecl *, 2> UnambiguousPublicSubobjects;
getUnambiguousPublicSubobjects(RD, UnambiguousPublicSubobjects);
for (CXXRecordDecl *Subobject : UnambiguousPublicSubobjects) {
// Attempt to lookup the copy constructor. Various pieces of machinery
// will spring into action, like template instantiation, which means this
// cannot be a simple walk of the class's decls. Instead, we must perform
// lookup and overload resolution.
CXXConstructorDecl *CD = LookupCopyingConstructor(Subobject, 0);
if (!CD || CD->isDeleted())
continue;
// Mark the constructor referenced as it is used by this throw expression.
MarkFunctionReferenced(E->getExprLoc(), CD);
// Skip this copy constructor if it is trivial, we don't need to record it
// in the catchable type data.
if (CD->isTrivial())
continue;
// The copy constructor is non-trivial, create a mapping from this class
// type to this constructor.
// N.B. The selection of copy constructor is not sensitive to this
// particular throw-site. Lookup will be performed at the catch-site to
// ensure that the copy constructor is, in fact, accessible (via
// friendship or any other means).
Context.addCopyConstructorForExceptionObject(Subobject, CD);
// We don't keep the instantiated default argument expressions around so
// we must rebuild them here.
for (unsigned I = 1, E = CD->getNumParams(); I != E; ++I) {
if (CheckCXXDefaultArgExpr(ThrowLoc, CD, CD->getParamDecl(I)))
return true;
}
}
}
// Under the Itanium C++ ABI, memory for the exception object is allocated by
// the runtime with no ability for the compiler to request additional
// alignment. Warn if the exception type requires alignment beyond the minimum
// guaranteed by the target C++ runtime.
if (Context.getTargetInfo().getCXXABI().isItaniumFamily()) {
CharUnits TypeAlign = Context.getTypeAlignInChars(Ty);
CharUnits ExnObjAlign = Context.getExnObjectAlignment();
if (ExnObjAlign < TypeAlign) {
Diag(ThrowLoc, diag::warn_throw_underaligned_obj);
Diag(ThrowLoc, diag::note_throw_underaligned_obj)
<< Ty << (unsigned)TypeAlign.getQuantity()
<< (unsigned)ExnObjAlign.getQuantity();
}
}
if (!isPointer && getLangOpts().AssumeNothrowExceptionDtor) {
if (CXXDestructorDecl *Dtor = RD->getDestructor()) {
auto Ty = Dtor->getType();
if (auto *FT = Ty.getTypePtr()->getAs<FunctionProtoType>()) {
if (!isUnresolvedExceptionSpec(FT->getExceptionSpecType()) &&
!FT->isNothrow())
Diag(ThrowLoc, diag::err_throw_object_throwing_dtor) << RD;
}
}
}
return false;
}
static QualType adjustCVQualifiersForCXXThisWithinLambda(
ArrayRef<FunctionScopeInfo *> FunctionScopes, QualType ThisTy,
DeclContext *CurSemaContext, ASTContext &ASTCtx) {
QualType ClassType = ThisTy->getPointeeType();
LambdaScopeInfo *CurLSI = nullptr;
DeclContext *CurDC = CurSemaContext;
// Iterate through the stack of lambdas starting from the innermost lambda to
// the outermost lambda, checking if '*this' is ever captured by copy - since
// that could change the cv-qualifiers of the '*this' object.
// The object referred to by '*this' starts out with the cv-qualifiers of its
// member function. We then start with the innermost lambda and iterate
// outward checking to see if any lambda performs a by-copy capture of '*this'
// - and if so, any nested lambda must respect the 'constness' of that
// capturing lamdbda's call operator.
//
// Since the FunctionScopeInfo stack is representative of the lexical
// nesting of the lambda expressions during initial parsing (and is the best
// place for querying information about captures about lambdas that are
// partially processed) and perhaps during instantiation of function templates
// that contain lambda expressions that need to be transformed BUT not
// necessarily during instantiation of a nested generic lambda's function call
// operator (which might even be instantiated at the end of the TU) - at which
// time the DeclContext tree is mature enough to query capture information
// reliably - we use a two pronged approach to walk through all the lexically
// enclosing lambda expressions:
//
// 1) Climb down the FunctionScopeInfo stack as long as each item represents
// a Lambda (i.e. LambdaScopeInfo) AND each LSI's 'closure-type' is lexically
// enclosed by the call-operator of the LSI below it on the stack (while
// tracking the enclosing DC for step 2 if needed). Note the topmost LSI on
// the stack represents the innermost lambda.
//
// 2) If we run out of enclosing LSI's, check if the enclosing DeclContext
// represents a lambda's call operator. If it does, we must be instantiating
// a generic lambda's call operator (represented by the Current LSI, and
// should be the only scenario where an inconsistency between the LSI and the
// DeclContext should occur), so climb out the DeclContexts if they
// represent lambdas, while querying the corresponding closure types
// regarding capture information.
// 1) Climb down the function scope info stack.
for (int I = FunctionScopes.size();
I-- && isa<LambdaScopeInfo>(FunctionScopes[I]) &&
(!CurLSI || !CurLSI->Lambda || CurLSI->Lambda->getDeclContext() ==
cast<LambdaScopeInfo>(FunctionScopes[I])->CallOperator);
CurDC = getLambdaAwareParentOfDeclContext(CurDC)) {
CurLSI = cast<LambdaScopeInfo>(FunctionScopes[I]);
if (!CurLSI->isCXXThisCaptured())
continue;
auto C = CurLSI->getCXXThisCapture();
if (C.isCopyCapture()) {
if (CurLSI->lambdaCaptureShouldBeConst())
ClassType.addConst();
return ASTCtx.getPointerType(ClassType);
}
}
// 2) We've run out of ScopeInfos but check 1. if CurDC is a lambda (which
// can happen during instantiation of its nested generic lambda call
// operator); 2. if we're in a lambda scope (lambda body).
if (CurLSI && isLambdaCallOperator(CurDC)) {
assert(isGenericLambdaCallOperatorSpecialization(CurLSI->CallOperator) &&
"While computing 'this' capture-type for a generic lambda, when we "
"run out of enclosing LSI's, yet the enclosing DC is a "
"lambda-call-operator we must be (i.e. Current LSI) in a generic "
"lambda call oeprator");
assert(CurDC == getLambdaAwareParentOfDeclContext(CurLSI->CallOperator));
auto IsThisCaptured =
[](CXXRecordDecl *Closure, bool &IsByCopy, bool &IsConst) {
IsConst = false;
IsByCopy = false;
for (auto &&C : Closure->captures()) {
if (C.capturesThis()) {
if (C.getCaptureKind() == LCK_StarThis)
IsByCopy = true;
if (Closure->getLambdaCallOperator()->isConst())
IsConst = true;
return true;
}
}
return false;
};
bool IsByCopyCapture = false;
bool IsConstCapture = false;
CXXRecordDecl *Closure = cast<CXXRecordDecl>(CurDC->getParent());
while (Closure &&
IsThisCaptured(Closure, IsByCopyCapture, IsConstCapture)) {
if (IsByCopyCapture) {
if (IsConstCapture)
ClassType.addConst();
return ASTCtx.getPointerType(ClassType);
}
Closure = isLambdaCallOperator(Closure->getParent())
? cast<CXXRecordDecl>(Closure->getParent()->getParent())
: nullptr;
}
}
return ThisTy;
}
QualType Sema::getCurrentThisType() {
DeclContext *DC = getFunctionLevelDeclContext();
QualType ThisTy = CXXThisTypeOverride;
if (CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(DC)) {
if (method && method->isImplicitObjectMemberFunction())
ThisTy = method->getThisType().getNonReferenceType();
}
if (ThisTy.isNull() && isLambdaCallWithImplicitObjectParameter(CurContext) &&
inTemplateInstantiation() && isa<CXXRecordDecl>(DC)) {
// This is a lambda call operator that is being instantiated as a default
// initializer. DC must point to the enclosing class type, so we can recover
// the 'this' type from it.
QualType ClassTy = Context.getTypeDeclType(cast<CXXRecordDecl>(DC));
// There are no cv-qualifiers for 'this' within default initializers,
// per [expr.prim.general]p4.
ThisTy = Context.getPointerType(ClassTy);
}
// If we are within a lambda's call operator, the cv-qualifiers of 'this'
// might need to be adjusted if the lambda or any of its enclosing lambda's
// captures '*this' by copy.
if (!ThisTy.isNull() && isLambdaCallOperator(CurContext))
return adjustCVQualifiersForCXXThisWithinLambda(FunctionScopes, ThisTy,
CurContext, Context);
return ThisTy;
}
Sema::CXXThisScopeRAII::CXXThisScopeRAII(Sema &S,
Decl *ContextDecl,
Qualifiers CXXThisTypeQuals,
bool Enabled)
: S(S), OldCXXThisTypeOverride(S.CXXThisTypeOverride), Enabled(false)
{
if (!Enabled || !ContextDecl)
return;
CXXRecordDecl *Record = nullptr;
if (ClassTemplateDecl *Template = dyn_cast<ClassTemplateDecl>(ContextDecl))
Record = Template->getTemplatedDecl();
else
Record = cast<CXXRecordDecl>(ContextDecl);
QualType T = S.Context.getRecordType(Record);
T = S.getASTContext().getQualifiedType(T, CXXThisTypeQuals);
S.CXXThisTypeOverride =
S.Context.getLangOpts().HLSL ? T : S.Context.getPointerType(T);
this->Enabled = true;
}
Sema::CXXThisScopeRAII::~CXXThisScopeRAII() {
if (Enabled) {
S.CXXThisTypeOverride = OldCXXThisTypeOverride;
}
}
static void buildLambdaThisCaptureFixit(Sema &Sema, LambdaScopeInfo *LSI) {
SourceLocation DiagLoc = LSI->IntroducerRange.getEnd();
assert(!LSI->isCXXThisCaptured());
// [=, this] {}; // until C++20: Error: this when = is the default
if (LSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval &&
!Sema.getLangOpts().CPlusPlus20)
return;
Sema.Diag(DiagLoc, diag::note_lambda_this_capture_fixit)
<< FixItHint::CreateInsertion(
DiagLoc, LSI->NumExplicitCaptures > 0 ? ", this" : "this");
}
bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit,
bool BuildAndDiagnose, const unsigned *const FunctionScopeIndexToStopAt,
const bool ByCopy) {
// We don't need to capture this in an unevaluated context.
if (isUnevaluatedContext() && !Explicit)
return true;
assert((!ByCopy || Explicit) && "cannot implicitly capture *this by value");
const int MaxFunctionScopesIndex = FunctionScopeIndexToStopAt
? *FunctionScopeIndexToStopAt
: FunctionScopes.size() - 1;
// Check that we can capture the *enclosing object* (referred to by '*this')
// by the capturing-entity/closure (lambda/block/etc) at
// MaxFunctionScopesIndex-deep on the FunctionScopes stack.
// Note: The *enclosing object* can only be captured by-value by a
// closure that is a lambda, using the explicit notation:
// [*this] { ... }.
// Every other capture of the *enclosing object* results in its by-reference
// capture.
// For a closure 'L' (at MaxFunctionScopesIndex in the FunctionScopes
// stack), we can capture the *enclosing object* only if:
// - 'L' has an explicit byref or byval capture of the *enclosing object*
// - or, 'L' has an implicit capture.
// AND
// -- there is no enclosing closure
// -- or, there is some enclosing closure 'E' that has already captured the
// *enclosing object*, and every intervening closure (if any) between 'E'
// and 'L' can implicitly capture the *enclosing object*.
// -- or, every enclosing closure can implicitly capture the
// *enclosing object*
unsigned NumCapturingClosures = 0;
for (int idx = MaxFunctionScopesIndex; idx >= 0; idx--) {
if (CapturingScopeInfo *CSI =
dyn_cast<CapturingScopeInfo>(FunctionScopes[idx])) {
if (CSI->CXXThisCaptureIndex != 0) {
// 'this' is already being captured; there isn't anything more to do.
CSI->Captures[CSI->CXXThisCaptureIndex - 1].markUsed(BuildAndDiagnose);
break;
}
LambdaScopeInfo *LSI = dyn_cast<LambdaScopeInfo>(CSI);
if (LSI && isGenericLambdaCallOperatorSpecialization(LSI->CallOperator)) {
// This context can't implicitly capture 'this'; fail out.
if (BuildAndDiagnose) {
LSI->CallOperator->setInvalidDecl();
Diag(Loc, diag::err_this_capture)
<< (Explicit && idx == MaxFunctionScopesIndex);
if (!Explicit)
buildLambdaThisCaptureFixit(*this, LSI);
}
return true;
}
if (CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByref ||
CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_LambdaByval ||
CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_Block ||
CSI->ImpCaptureStyle == CapturingScopeInfo::ImpCap_CapturedRegion ||
(Explicit && idx == MaxFunctionScopesIndex)) {
// Regarding (Explicit && idx == MaxFunctionScopesIndex): only the first
// iteration through can be an explicit capture, all enclosing closures,
// if any, must perform implicit captures.
// This closure can capture 'this'; continue looking upwards.
NumCapturingClosures++;
continue;
}
// This context can't implicitly capture 'this'; fail out.
if (BuildAndDiagnose) {
LSI->CallOperator->setInvalidDecl();
Diag(Loc, diag::err_this_capture)
<< (Explicit && idx == MaxFunctionScopesIndex);
}
if (!Explicit)
buildLambdaThisCaptureFixit(*this, LSI);
return true;
}
break;
}
if (!BuildAndDiagnose) return false;
// If we got here, then the closure at MaxFunctionScopesIndex on the
// FunctionScopes stack, can capture the *enclosing object*, so capture it
// (including implicit by-reference captures in any enclosing closures).
// In the loop below, respect the ByCopy flag only for the closure requesting
// the capture (i.e. first iteration through the loop below). Ignore it for
// all enclosing closure's up to NumCapturingClosures (since they must be
// implicitly capturing the *enclosing object* by reference (see loop
// above)).
assert((!ByCopy ||
isa<LambdaScopeInfo>(FunctionScopes[MaxFunctionScopesIndex])) &&
"Only a lambda can capture the enclosing object (referred to by "
"*this) by copy");
QualType ThisTy = getCurrentThisType();
for (int idx = MaxFunctionScopesIndex; NumCapturingClosures;
--idx, --NumCapturingClosures) {
CapturingScopeInfo *CSI = cast<CapturingScopeInfo>(FunctionScopes[idx]);
// The type of the corresponding data member (not a 'this' pointer if 'by
// copy').
QualType CaptureType = ByCopy ? ThisTy->getPointeeType() : ThisTy;
bool isNested = NumCapturingClosures > 1;
CSI->addThisCapture(isNested, Loc, CaptureType, ByCopy);
}
return false;
}
ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
// C++20 [expr.prim.this]p1:
// The keyword this names a pointer to the object for which an
// implicit object member function is invoked or a non-static
// data member's initializer is evaluated.
QualType ThisTy = getCurrentThisType();
if (CheckCXXThisType(Loc, ThisTy))
return ExprError();
return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false);
}
bool Sema::CheckCXXThisType(SourceLocation Loc, QualType Type) {
if (!Type.isNull())
return false;
// C++20 [expr.prim.this]p3:
// If a declaration declares a member function or member function template
// of a class X, the expression this is a prvalue of type
// "pointer to cv-qualifier-seq X" wherever X is the current class between
// the optional cv-qualifier-seq and the end of the function-definition,
// member-declarator, or declarator. It shall not appear within the
// declaration of either a static member function or an explicit object
// member function of the current class (although its type and value
// category are defined within such member functions as they are within
// an implicit object member function).
DeclContext *DC = getFunctionLevelDeclContext();
const auto *Method = dyn_cast<CXXMethodDecl>(DC);
if (Method && Method->isExplicitObjectMemberFunction()) {
Diag(Loc, diag::err_invalid_this_use) << 1;
} else if (Method && isLambdaCallWithExplicitObjectParameter(CurContext)) {
Diag(Loc, diag::err_invalid_this_use) << 1;
} else {
Diag(Loc, diag::err_invalid_this_use) << 0;
}
return true;
}
Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type,
bool IsImplicit) {
auto *This = CXXThisExpr::Create(Context, Loc, Type, IsImplicit);
MarkThisReferenced(This);
return This;
}
void Sema::MarkThisReferenced(CXXThisExpr *This) {
CheckCXXThisCapture(This->getExprLoc());
if (This->isTypeDependent())
return;
// Check if 'this' is captured by value in a lambda with a dependent explicit
// object parameter, and mark it as type-dependent as well if so.
auto IsDependent = [&]() {
for (auto *Scope : llvm::reverse(FunctionScopes)) {
auto *LSI = dyn_cast<sema::LambdaScopeInfo>(Scope);
if (!LSI)
continue;
if (LSI->Lambda && !LSI->Lambda->Encloses(CurContext) &&
LSI->AfterParameterList)
return false;
// If this lambda captures 'this' by value, then 'this' is dependent iff
// this lambda has a dependent explicit object parameter. If we can't
// determine whether it does (e.g. because the CXXMethodDecl's type is
// null), assume it doesn't.
if (LSI->isCXXThisCaptured()) {
if (!LSI->getCXXThisCapture().isCopyCapture())
continue;
const auto *MD = LSI->CallOperator;
if (MD->getType().isNull())
return false;
const auto *Ty = MD->getType()->getAs<FunctionProtoType>();
return Ty && MD->isExplicitObjectMemberFunction() &&
Ty->getParamType(0)->isDependentType();
}
}
return false;
}();
This->setCapturedByCopyInLambdaWithExplicitObjectParameter(IsDependent);
}
bool Sema::isThisOutsideMemberFunctionBody(QualType BaseType) {
// If we're outside the body of a member function, then we'll have a specified
// type for 'this'.
if (CXXThisTypeOverride.isNull())
return false;
// Determine whether we're looking into a class that's currently being
// defined.
CXXRecordDecl *Class = BaseType->getAsCXXRecordDecl();
return Class && Class->isBeingDefined();
}
ExprResult
Sema::ActOnCXXTypeConstructExpr(ParsedType TypeRep,
SourceLocation LParenOrBraceLoc,
MultiExprArg exprs,
SourceLocation RParenOrBraceLoc,
bool ListInitialization) {
if (!TypeRep)
return ExprError();
TypeSourceInfo *TInfo;
QualType Ty = GetTypeFromParser(TypeRep, &TInfo);
if (!TInfo)
TInfo = Context.getTrivialTypeSourceInfo(Ty, SourceLocation());
auto Result = BuildCXXTypeConstructExpr(TInfo, LParenOrBraceLoc, exprs,
RParenOrBraceLoc, ListInitialization);
// Avoid creating a non-type-dependent expression that contains typos.
// Non-type-dependent expressions are liable to be discarded without
// checking for embedded typos.
if (!Result.isInvalid() && Result.get()->isInstantiationDependent() &&
!Result.get()->isTypeDependent())
Result = CorrectDelayedTyposInExpr(Result.get());
else if (Result.isInvalid())
Result = CreateRecoveryExpr(TInfo->getTypeLoc().getBeginLoc(),
RParenOrBraceLoc, exprs, Ty);
return Result;
}
ExprResult
Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
SourceLocation LParenOrBraceLoc,
MultiExprArg Exprs,
SourceLocation RParenOrBraceLoc,
bool ListInitialization) {
QualType Ty = TInfo->getType();
SourceLocation TyBeginLoc = TInfo->getTypeLoc().getBeginLoc();
SourceRange FullRange = SourceRange(TyBeginLoc, RParenOrBraceLoc);
InitializedEntity Entity =
InitializedEntity::InitializeTemporary(Context, TInfo);
InitializationKind Kind =
Exprs.size()
? ListInitialization
? InitializationKind::CreateDirectList(
TyBeginLoc, LParenOrBraceLoc, RParenOrBraceLoc)
: InitializationKind::CreateDirect(TyBeginLoc, LParenOrBraceLoc,
RParenOrBraceLoc)
: InitializationKind::CreateValue(TyBeginLoc, LParenOrBraceLoc,
RParenOrBraceLoc);
// C++17 [expr.type.conv]p1:
// If the type is a placeholder for a deduced class type, [...perform class
// template argument deduction...]
// C++23:
// Otherwise, if the type contains a placeholder type, it is replaced by the
// type determined by placeholder type deduction.
DeducedType *Deduced = Ty->getContainedDeducedType();
if (Deduced && !Deduced->isDeduced() &&
isa<DeducedTemplateSpecializationType>(Deduced)) {
Ty = DeduceTemplateSpecializationFromInitializer(TInfo, Entity,
Kind, Exprs);
if (Ty.isNull())
return ExprError();
Entity = InitializedEntity::InitializeTemporary(TInfo, Ty);
} else if (Deduced && !Deduced->isDeduced()) {
MultiExprArg Inits = Exprs;
if (ListInitialization) {
auto *ILE = cast<InitListExpr>(Exprs[0]);
Inits = MultiExprArg(ILE->getInits(), ILE->getNumInits());
}
if (Inits.empty())
return ExprError(Diag(TyBeginLoc, diag::err_auto_expr_init_no_expression)
<< Ty << FullRange);
if (Inits.size() > 1) {
Expr *FirstBad = Inits[1];
return ExprError(Diag(FirstBad->getBeginLoc(),
diag::err_auto_expr_init_multiple_expressions)
<< Ty << FullRange);
}
if (getLangOpts().CPlusPlus23) {
if (Ty->getAs<AutoType>())
Diag(TyBeginLoc, diag::warn_cxx20_compat_auto_expr) << FullRange;
}
Expr *Deduce = Inits[0];
if (isa<InitListExpr>(Deduce))
return ExprError(
Diag(Deduce->getBeginLoc(), diag::err_auto_expr_init_paren_braces)
<< ListInitialization << Ty << FullRange);
QualType DeducedType;
TemplateDeductionInfo Info(Deduce->getExprLoc());
TemplateDeductionResult Result =
DeduceAutoType(TInfo->getTypeLoc(), Deduce, DeducedType, Info);
if (Result != TemplateDeductionResult::Success &&
Result != TemplateDeductionResult::AlreadyDiagnosed)
return ExprError(Diag(TyBeginLoc, diag::err_auto_expr_deduction_failure)
<< Ty << Deduce->getType() << FullRange
<< Deduce->getSourceRange());
if (DeducedType.isNull()) {
assert(Result == TemplateDeductionResult::AlreadyDiagnosed);
return ExprError();
}
Ty = DeducedType;
Entity = InitializedEntity::InitializeTemporary(TInfo, Ty);
}
if (Ty->isDependentType() || CallExpr::hasAnyTypeDependentArguments(Exprs))
return CXXUnresolvedConstructExpr::Create(
Context, Ty.getNonReferenceType(), TInfo, LParenOrBraceLoc, Exprs,
RParenOrBraceLoc, ListInitialization);
// C++ [expr.type.conv]p1:
// If the expression list is a parenthesized single expression, the type
// conversion expression is equivalent (in definedness, and if defined in
// meaning) to the corresponding cast expression.
if (Exprs.size() == 1 && !ListInitialization &&
!isa<InitListExpr>(Exprs[0])) {
Expr *Arg = Exprs[0];
return BuildCXXFunctionalCastExpr(TInfo, Ty, LParenOrBraceLoc, Arg,
RParenOrBraceLoc);
}
// For an expression of the form T(), T shall not be an array type.
QualType ElemTy = Ty;
if (Ty->isArrayType()) {
if (!ListInitialization)
return ExprError(Diag(TyBeginLoc, diag::err_value_init_for_array_type)
<< FullRange);
ElemTy = Context.getBaseElementType(Ty);
}
// Only construct objects with object types.
// The standard doesn't explicitly forbid function types here, but that's an
// obvious oversight, as there's no way to dynamically construct a function
// in general.
if (Ty->isFunctionType())
return ExprError(Diag(TyBeginLoc, diag::err_init_for_function_type)
<< Ty << FullRange);
// C++17 [expr.type.conv]p2, per DR2351:
// If the type is cv void and the initializer is () or {}, the expression is
// a prvalue of the specified type that performs no initialization.
if (Ty->isVoidType()) {
if (Exprs.empty())
return new (Context) CXXScalarValueInitExpr(
Ty.getUnqualifiedType(), TInfo, Kind.getRange().getEnd());
if (ListInitialization &&
cast<InitListExpr>(Exprs[0])->getNumInits() == 0) {
return CXXFunctionalCastExpr::Create(
Context, Ty.getUnqualifiedType(), VK_PRValue, TInfo, CK_ToVoid,
Exprs[0], /*Path=*/nullptr, CurFPFeatureOverrides(),
Exprs[0]->getBeginLoc(), Exprs[0]->getEndLoc());
}
} else if (RequireCompleteType(TyBeginLoc, ElemTy,
diag::err_invalid_incomplete_type_use,
FullRange))
return ExprError();
// Otherwise, the expression is a prvalue of the specified type whose
// result object is direct-initialized (11.6) with the initializer.
InitializationSequence InitSeq(*this, Entity, Kind, Exprs);
ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Exprs);
if (Result.isInvalid())
return Result;
Expr *Inner = Result.get();
if (CXXBindTemporaryExpr *BTE = dyn_cast_or_null<CXXBindTemporaryExpr>(Inner))
Inner = BTE->getSubExpr();
if (auto *CE = dyn_cast<ConstantExpr>(Inner);
CE && CE->isImmediateInvocation())
Inner = CE->getSubExpr();
if (!isa<CXXTemporaryObjectExpr>(Inner) &&
!isa<CXXScalarValueInitExpr>(Inner)) {
// If we created a CXXTemporaryObjectExpr, that node also represents the
// functional cast. Otherwise, create an explicit cast to represent
// the syntactic form of a functional-style cast that was used here.
//
// FIXME: Creating a CXXFunctionalCastExpr around a CXXConstructExpr
// would give a more consistent AST representation than using a
// CXXTemporaryObjectExpr. It's also weird that the functional cast
// is sometimes handled by initialization and sometimes not.
QualType ResultType = Result.get()->getType();
SourceRange Locs = ListInitialization
? SourceRange()
: SourceRange(LParenOrBraceLoc, RParenOrBraceLoc);
Result = CXXFunctionalCastExpr::Create(
Context, ResultType, Expr::getValueKindForType(Ty), TInfo, CK_NoOp,
Result.get(), /*Path=*/nullptr, CurFPFeatureOverrides(),
Locs.getBegin(), Locs.getEnd());
}
return Result;
}
bool Sema::isUsualDeallocationFunction(const CXXMethodDecl *Method) {
// [CUDA] Ignore this function, if we can't call it.
const FunctionDecl *Caller = getCurFunctionDecl(/*AllowLambda=*/true);
if (getLangOpts().CUDA) {
auto CallPreference = CUDA().IdentifyPreference(Caller, Method);
// If it's not callable at all, it's not the right function.
if (CallPreference < SemaCUDA::CFP_WrongSide)
return false;
if (CallPreference == SemaCUDA::CFP_WrongSide) {
// Maybe. We have to check if there are better alternatives.
DeclContext::lookup_result R =
Method->getDeclContext()->lookup(Method->getDeclName());
for (const auto *D : R) {
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
if (CUDA().IdentifyPreference(Caller, FD) > SemaCUDA::CFP_WrongSide)
return false;
}
}
// We've found no better variants.
}
}
SmallVector<const FunctionDecl*, 4> PreventedBy;
bool Result = Method->isUsualDeallocationFunction(PreventedBy);
if (Result || !getLangOpts().CUDA || PreventedBy.empty())
return Result;
// In case of CUDA, return true if none of the 1-argument deallocator
// functions are actually callable.
return llvm::none_of(PreventedBy, [&](const FunctionDecl *FD) {
assert(FD->getNumParams() == 1 &&
"Only single-operand functions should be in PreventedBy");
return CUDA().IdentifyPreference(Caller, FD) >= SemaCUDA::CFP_HostDevice;
});
}
/// Determine whether the given function is a non-placement
/// deallocation function.
static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) {
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD))
return S.isUsualDeallocationFunction(Method);
if (!FD->getDeclName().isAnyOperatorDelete())
return false;
if (FD->isTypeAwareOperatorNewOrDelete())
return FunctionDecl::RequiredTypeAwareDeleteParameterCount ==
FD->getNumParams();
unsigned UsualParams = 1;
if (S.getLangOpts().SizedDeallocation && UsualParams < FD->getNumParams() &&
S.Context.hasSameUnqualifiedType(
FD->getParamDecl(UsualParams)->getType(),
S.Context.getSizeType()))
++UsualParams;
if (S.getLangOpts().AlignedAllocation && UsualParams < FD->getNumParams() &&
S.Context.hasSameUnqualifiedType(
FD->getParamDecl(UsualParams)->getType(),
S.Context.getTypeDeclType(S.getStdAlignValT())))
++UsualParams;
return UsualParams == FD->getNumParams();
}
namespace {
struct UsualDeallocFnInfo {
UsualDeallocFnInfo()
: Found(), FD(nullptr),
IDP(AlignedAllocationMode::No, SizedDeallocationMode::No) {}
UsualDeallocFnInfo(Sema &S, DeclAccessPair Found, QualType AllocType,
SourceLocation Loc)
: Found(Found), FD(dyn_cast<FunctionDecl>(Found->getUnderlyingDecl())),
Destroying(false),
IDP({AllocType, TypeAwareAllocationMode::No,
AlignedAllocationMode::No, SizedDeallocationMode::No}),
CUDAPref(SemaCUDA::CFP_Native) {
// A function template declaration is only a usual deallocation function
// if it is a typed delete.
if (!FD) {
if (AllocType.isNull())
return;
auto *FTD = dyn_cast<FunctionTemplateDecl>(Found->getUnderlyingDecl());
if (!FTD)
return;
FunctionDecl *InstantiatedDecl =
S.BuildTypeAwareUsualDelete(FTD, AllocType, Loc);
if (!InstantiatedDecl)
return;
FD = InstantiatedDecl;
}
unsigned NumBaseParams = 1;
if (FD->isTypeAwareOperatorNewOrDelete()) {
// If this is a type aware operator delete we instantiate an appropriate
// specialization of std::type_identity<>. If we do not know the
// type being deallocated, or if the type-identity parameter of the
// deallocation function does not match the constructed type_identity
// specialization we reject the declaration.
if (AllocType.isNull()) {
FD = nullptr;
return;
}
QualType TypeIdentityTag = FD->getParamDecl(0)->getType();
QualType ExpectedTypeIdentityTag =
S.tryBuildStdTypeIdentity(AllocType, Loc);
if (ExpectedTypeIdentityTag.isNull()) {
FD = nullptr;
return;
}
if (!S.Context.hasSameType(TypeIdentityTag, ExpectedTypeIdentityTag)) {
FD = nullptr;
return;
}
IDP.PassTypeIdentity = TypeAwareAllocationMode::Yes;
++NumBaseParams;
}
if (FD->isDestroyingOperatorDelete()) {
Destroying = true;
++NumBaseParams;
}
if (NumBaseParams < FD->getNumParams() &&
S.Context.hasSameUnqualifiedType(
FD->getParamDecl(NumBaseParams)->getType(),
S.Context.getSizeType())) {
++NumBaseParams;
IDP.PassSize = SizedDeallocationMode::Yes;
}
if (NumBaseParams < FD->getNumParams() &&
FD->getParamDecl(NumBaseParams)->getType()->isAlignValT()) {
++NumBaseParams;
IDP.PassAlignment = AlignedAllocationMode::Yes;
}
// In CUDA, determine how much we'd like / dislike to call this.
if (S.getLangOpts().CUDA)
CUDAPref = S.CUDA().IdentifyPreference(
S.getCurFunctionDecl(/*AllowLambda=*/true), FD);
}
explicit operator bool() const { return FD; }
int Compare(Sema &S, const UsualDeallocFnInfo &Other,
ImplicitDeallocationParameters TargetIDP) const {
assert(!TargetIDP.Type.isNull() ||
!isTypeAwareAllocation(Other.IDP.PassTypeIdentity));
// C++ P0722:
// A destroying operator delete is preferred over a non-destroying
// operator delete.
if (Destroying != Other.Destroying)
return Destroying ? 1 : -1;
const ImplicitDeallocationParameters &OtherIDP = Other.IDP;
// Selection for type awareness has priority over alignment and size
if (IDP.PassTypeIdentity != OtherIDP.PassTypeIdentity)
return IDP.PassTypeIdentity == TargetIDP.PassTypeIdentity ? 1 : -1;
// C++17 [expr.delete]p10:
// If the type has new-extended alignment, a function with a parameter
// of type std::align_val_t is preferred; otherwise a function without
// such a parameter is preferred
if (IDP.PassAlignment != OtherIDP.PassAlignment)
return IDP.PassAlignment == TargetIDP.PassAlignment ? 1 : -1;
if (IDP.PassSize != OtherIDP.PassSize)
return IDP.PassSize == TargetIDP.PassSize ? 1 : -1;
if (isTypeAwareAllocation(IDP.PassTypeIdentity)) {
// Type aware allocation involves templates so we need to choose
// the best type
FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
FunctionTemplateDecl *OtherPrimaryTemplate =
Other.FD->getPrimaryTemplate();
if ((!PrimaryTemplate) != (!OtherPrimaryTemplate))
return OtherPrimaryTemplate ? 1 : -1;
if (PrimaryTemplate && OtherPrimaryTemplate) {
const auto *DC = dyn_cast<CXXRecordDecl>(Found->getDeclContext());
const auto *OtherDC =
dyn_cast<CXXRecordDecl>(Other.Found->getDeclContext());
unsigned ImplicitArgCount = Destroying + IDP.getNumImplicitArgs();
if (FunctionTemplateDecl *Best = S.getMoreSpecializedTemplate(
PrimaryTemplate, OtherPrimaryTemplate, SourceLocation(),
TPOC_Call, ImplicitArgCount,
DC ? QualType(DC->getTypeForDecl(), 0) : QualType{},
OtherDC ? QualType(OtherDC->getTypeForDecl(), 0) : QualType{},
false)) {
return Best == PrimaryTemplate ? 1 : -1;
}
}
}
// Use CUDA call preference as a tiebreaker.
if (CUDAPref > Other.CUDAPref)
return 1;
if (CUDAPref == Other.CUDAPref)
return 0;
return -1;
}
DeclAccessPair Found;
FunctionDecl *FD;
bool Destroying;
ImplicitDeallocationParameters IDP;
SemaCUDA::CUDAFunctionPreference CUDAPref;
};
}
/// Determine whether a type has new-extended alignment. This may be called when
/// the type is incomplete (for a delete-expression with an incomplete pointee
/// type), in which case it will conservatively return false if the alignment is
/// not known.
static bool hasNewExtendedAlignment(Sema &S, QualType AllocType) {
return S.getLangOpts().AlignedAllocation &&
S.getASTContext().getTypeAlignIfKnown(AllocType) >
S.getASTContext().getTargetInfo().getNewAlign();
}
static bool CheckDeleteOperator(Sema &S, SourceLocation StartLoc,
SourceRange Range, bool Diagnose,
CXXRecordDecl *NamingClass, DeclAccessPair Decl,
FunctionDecl *Operator) {
if (Operator->isTypeAwareOperatorNewOrDelete()) {
QualType SelectedTypeIdentityParameter =
Operator->getParamDecl(0)->getType();
if (S.RequireCompleteType(StartLoc, SelectedTypeIdentityParameter,
diag::err_incomplete_type))
return true;
}
// FIXME: DiagnoseUseOfDecl?
if (Operator->isDeleted()) {
if (Diagnose) {
StringLiteral *Msg = Operator->getDeletedMessage();
S.Diag(StartLoc, diag::err_deleted_function_use)
<< (Msg != nullptr) << (Msg ? Msg->getString() : StringRef());
S.NoteDeletedFunction(Operator);
}
return true;
}
return S.CheckAllocationAccess(StartLoc, Range, NamingClass, Decl, Diagnose);
}
/// Select the correct "usual" deallocation function to use from a selection of
/// deallocation functions (either global or class-scope).
static UsualDeallocFnInfo resolveDeallocationOverload(
Sema &S, LookupResult &R, const ImplicitDeallocationParameters &IDP,
SourceLocation Loc,
llvm::SmallVectorImpl<UsualDeallocFnInfo> *BestFns = nullptr) {
UsualDeallocFnInfo Best;
for (auto I = R.begin(), E = R.end(); I != E; ++I) {
UsualDeallocFnInfo Info(S, I.getPair(), IDP.Type, Loc);
if (!Info || !isNonPlacementDeallocationFunction(S, Info.FD) ||
Info.CUDAPref == SemaCUDA::CFP_Never)
continue;
if (!isTypeAwareAllocation(IDP.PassTypeIdentity) &&
isTypeAwareAllocation(Info.IDP.PassTypeIdentity))
continue;
if (!Best) {
Best = Info;
if (BestFns)
BestFns->push_back(Info);
continue;
}
int ComparisonResult = Best.Compare(S, Info, IDP);
if (ComparisonResult > 0)
continue;
// If more than one preferred function is found, all non-preferred
// functions are eliminated from further consideration.
if (BestFns && ComparisonResult < 0)
BestFns->clear();
Best = Info;
if (BestFns)
BestFns->push_back(Info);
}
return Best;
}
/// Determine whether a given type is a class for which 'delete[]' would call
/// a member 'operator delete[]' with a 'size_t' parameter. This implies that
/// we need to store the array size (even if the type is
/// trivially-destructible).
static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc,
TypeAwareAllocationMode PassType,
QualType allocType) {
const RecordType *record =
allocType->getBaseElementTypeUnsafe()->getAs<RecordType>();
if (!record) return false;
// Try to find an operator delete[] in class scope.
DeclarationName deleteName =
S.Context.DeclarationNames.getCXXOperatorName(OO_Array_Delete);
LookupResult ops(S, deleteName, loc, Sema::LookupOrdinaryName);
S.LookupQualifiedName(ops, record->getDecl());
// We're just doing this for information.
ops.suppressDiagnostics();
// Very likely: there's no operator delete[].
if (ops.empty()) return false;
// If it's ambiguous, it should be illegal to call operator delete[]
// on this thing, so it doesn't matter if we allocate extra space or not.
if (ops.isAmbiguous()) return false;
// C++17 [expr.delete]p10:
// If the deallocation functions have class scope, the one without a
// parameter of type std::size_t is selected.
ImplicitDeallocationParameters IDP = {
allocType, PassType,
alignedAllocationModeFromBool(hasNewExtendedAlignment(S, allocType)),
SizedDeallocationMode::No};
auto Best = resolveDeallocationOverload(S, ops, IDP, loc);
return Best && isSizedDeallocation(Best.IDP.PassSize);
}
ExprResult
Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
SourceLocation PlacementLParen, MultiExprArg PlacementArgs,
SourceLocation PlacementRParen, SourceRange TypeIdParens,
Declarator &D, Expr *Initializer) {
std::optional<Expr *> ArraySize;
// If the specified type is an array, unwrap it and save the expression.
if (D.getNumTypeObjects() > 0 &&
D.getTypeObject(0).Kind == DeclaratorChunk::Array) {
DeclaratorChunk &Chunk = D.getTypeObject(0);
if (D.getDeclSpec().hasAutoTypeSpec())
return ExprError(Diag(Chunk.Loc, diag::err_new_array_of_auto)
<< D.getSourceRange());
if (Chunk.Arr.hasStatic)
return ExprError(Diag(Chunk.Loc, diag::err_static_illegal_in_new)
<< D.getSourceRange());
if (!Chunk.Arr.NumElts && !Initializer)
return ExprError(Diag(Chunk.Loc, diag::err_array_new_needs_size)
<< D.getSourceRange());
ArraySize = static_cast<Expr*>(Chunk.Arr.NumElts);
D.DropFirstTypeObject();
}
// Every dimension shall be of constant size.
if (ArraySize) {
for (unsigned I = 0, N = D.getNumTypeObjects(); I < N; ++I) {
if (D.getTypeObject(I).Kind != DeclaratorChunk::Array)
break;
DeclaratorChunk::ArrayTypeInfo &Array = D.getTypeObject(I).Arr;
if (Expr *NumElts = (Expr *)Array.NumElts) {
if (!NumElts->isTypeDependent() && !NumElts->isValueDependent()) {
// FIXME: GCC permits constant folding here. We should either do so consistently
// or not do so at all, rather than changing behavior in C++14 onwards.
if (getLangOpts().CPlusPlus14) {
// C++1y [expr.new]p6: Every constant-expression in a noptr-new-declarator
// shall be a converted constant expression (5.19) of type std::size_t
// and shall evaluate to a strictly positive value.
llvm::APSInt Value(Context.getIntWidth(Context.getSizeType()));
Array.NumElts
= CheckConvertedConstantExpression(NumElts, Context.getSizeType(), Value,
CCEK_ArrayBound)
.get();
} else {
Array.NumElts =
VerifyIntegerConstantExpression(
NumElts, nullptr, diag::err_new_array_nonconst, AllowFold)
.get();
}
if (!Array.NumElts)
return ExprError();
}
}
}
}
TypeSourceInfo *TInfo = GetTypeForDeclarator(D);
QualType AllocType = TInfo->getType();
if (D.isInvalidType())
return ExprError();
SourceRange DirectInitRange;
if (ParenListExpr *List = dyn_cast_or_null<ParenListExpr>(Initializer))
DirectInitRange = List->getSourceRange();
return BuildCXXNew(SourceRange(StartLoc, D.getEndLoc()), UseGlobal,
PlacementLParen, PlacementArgs, PlacementRParen,
TypeIdParens, AllocType, TInfo, ArraySize, DirectInitRange,
Initializer);
}
static bool isLegalArrayNewInitializer(CXXNewInitializationStyle Style,
Expr *Init, bool IsCPlusPlus20) {
if (!Init)
return true;
if (ParenListExpr *PLE = dyn_cast<ParenListExpr>(Init))
return IsCPlusPlus20 || PLE->getNumExprs() == 0;
if (isa<ImplicitValueInitExpr>(Init))
return true;
else if (CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(Init))
return !CCE->isListInitialization() &&
CCE->getConstructor()->isDefaultConstructor();
else if (Style == CXXNewInitializationStyle::Braces) {
assert(isa<InitListExpr>(Init) &&
"Shouldn't create list CXXConstructExprs for arrays.");
return true;
}
return false;
}
bool
Sema::isUnavailableAlignedAllocationFunction(const FunctionDecl &FD) const {
if (!getLangOpts().AlignedAllocationUnavailable)
return false;
if (FD.isDefined())
return false;
UnsignedOrNone AlignmentParam = std::nullopt;
if (FD.isReplaceableGlobalAllocationFunction(&AlignmentParam) &&
AlignmentParam)
return true;
return false;
}
// Emit a diagnostic if an aligned allocation/deallocation function that is not
// implemented in the standard library is selected.
void Sema::diagnoseUnavailableAlignedAllocation(const FunctionDecl &FD,
SourceLocation Loc) {
if (isUnavailableAlignedAllocationFunction(FD)) {
const llvm::Triple &T = getASTContext().getTargetInfo().getTriple();
StringRef OSName = AvailabilityAttr::getPlatformNameSourceSpelling(
getASTContext().getTargetInfo().getPlatformName());
VersionTuple OSVersion = alignedAllocMinVersion(T.getOS());
bool IsDelete = FD.getDeclName().isAnyOperatorDelete();
Diag(Loc, diag::err_aligned_allocation_unavailable)
<< IsDelete << FD.getType().getAsString() << OSName
<< OSVersion.getAsString() << OSVersion.empty();
Diag(Loc, diag::note_silence_aligned_allocation_unavailable);
}
}
ExprResult Sema::BuildCXXNew(SourceRange Range, bool UseGlobal,
SourceLocation PlacementLParen,
MultiExprArg PlacementArgs,
SourceLocation PlacementRParen,
SourceRange TypeIdParens, QualType AllocType,
TypeSourceInfo *AllocTypeInfo,
std::optional<Expr *> ArraySize,
SourceRange DirectInitRange, Expr *Initializer) {
SourceRange TypeRange = AllocTypeInfo->getTypeLoc().getSourceRange();
SourceLocation StartLoc = Range.getBegin();
CXXNewInitializationStyle InitStyle;
if (DirectInitRange.isValid()) {
assert(Initializer && "Have parens but no initializer.");
InitStyle = CXXNewInitializationStyle::Parens;
} else if (isa_and_nonnull<InitListExpr>(Initializer))
InitStyle = CXXNewInitializationStyle::Braces;
else {
assert((!Initializer || isa<ImplicitValueInitExpr>(Initializer) ||
isa<CXXConstructExpr>(Initializer)) &&
"Initializer expression that cannot have been implicitly created.");
InitStyle = CXXNewInitializationStyle::None;
}
MultiExprArg Exprs(&Initializer, Initializer ? 1 : 0);
if (ParenListExpr *List = dyn_cast_or_null<ParenListExpr>(Initializer)) {
assert(InitStyle == CXXNewInitializationStyle::Parens &&
"paren init for non-call init");
Exprs = MultiExprArg(List->getExprs(), List->getNumExprs());
}
// C++11 [expr.new]p15:
// A new-expression that creates an object of type T initializes that
// object as follows:
InitializationKind Kind = [&] {
switch (InitStyle) {
// - If the new-initializer is omitted, the object is default-
// initialized (8.5); if no initialization is performed,
// the object has indeterminate value
case CXXNewInitializationStyle::None:
return InitializationKind::CreateDefault(TypeRange.getBegin());
// - Otherwise, the new-initializer is interpreted according to the
// initialization rules of 8.5 for direct-initialization.
case CXXNewInitializationStyle::Parens:
return InitializationKind::CreateDirect(TypeRange.getBegin(),
DirectInitRange.getBegin(),
DirectInitRange.getEnd());
case CXXNewInitializationStyle::Braces:
return InitializationKind::CreateDirectList(TypeRange.getBegin(),
Initializer->getBeginLoc(),
Initializer->getEndLoc());
}
llvm_unreachable("Unknown initialization kind");
}();
// C++11 [dcl.spec.auto]p6. Deduce the type which 'auto' stands in for.
auto *Deduced = AllocType->getContainedDeducedType();
if (Deduced && !Deduced->isDeduced() &&
isa<DeducedTemplateSpecializationType>(Deduced)) {
if (ArraySize)
return ExprError(
Diag(*ArraySize ? (*ArraySize)->getExprLoc() : TypeRange.getBegin(),
diag::err_deduced_class_template_compound_type)
<< /*array*/ 2
<< (*ArraySize ? (*ArraySize)->getSourceRange() : TypeRange));
InitializedEntity Entity
= InitializedEntity::InitializeNew(StartLoc, AllocType);
AllocType = DeduceTemplateSpecializationFromInitializer(
AllocTypeInfo, Entity, Kind, Exprs);
if (AllocType.isNull())
return ExprError();
} else if (Deduced && !Deduced->isDeduced()) {
MultiExprArg Inits = Exprs;
bool Braced = (InitStyle == CXXNewInitializationStyle::Braces);
if (Braced) {
auto *ILE = cast<InitListExpr>(Exprs[0]);
Inits = MultiExprArg(ILE->getInits(), ILE->getNumInits());
}
if (InitStyle == CXXNewInitializationStyle::None || Inits.empty())
return ExprError(Diag(StartLoc, diag::err_auto_new_requires_ctor_arg)
<< AllocType << TypeRange);
if (Inits.size() > 1) {
Expr *FirstBad = Inits[1];
return ExprError(Diag(FirstBad->getBeginLoc(),
diag::err_auto_new_ctor_multiple_expressions)
<< AllocType << TypeRange);
}
if (Braced && !getLangOpts().CPlusPlus17)
Diag(Initializer->getBeginLoc(), diag::ext_auto_new_list_init)
<< AllocType << TypeRange;
Expr *Deduce = Inits[0];
if (isa<InitListExpr>(Deduce))
return ExprError(
Diag(Deduce->getBeginLoc(), diag::err_auto_expr_init_paren_braces)
<< Braced << AllocType << TypeRange);
QualType DeducedType;
TemplateDeductionInfo Info(Deduce->getExprLoc());
TemplateDeductionResult Result =
DeduceAutoType(AllocTypeInfo->getTypeLoc(), Deduce, DeducedType, Info);
if (Result != TemplateDeductionResult::Success &&
Result != TemplateDeductionResult::AlreadyDiagnosed)
return ExprError(Diag(StartLoc, diag::err_auto_new_deduction_failure)
<< AllocType << Deduce->getType() << TypeRange
<< Deduce->getSourceRange());
if (DeducedType.isNull()) {
assert(Result == TemplateDeductionResult::AlreadyDiagnosed);
return ExprError();
}
AllocType = DeducedType;
}
// Per C++0x [expr.new]p5, the type being constructed may be a
// typedef of an array type.
// Dependent case will be handled separately.
if (!ArraySize && !AllocType->isDependentType()) {
if (const ConstantArrayType *Array
= Context.getAsConstantArrayType(AllocType)) {
ArraySize = IntegerLiteral::Create(Context, Array->getSize(),
Context.getSizeType(),
TypeRange.getEnd());
AllocType = Array->getElementType();
}
}
if (CheckAllocatedType(AllocType, TypeRange.getBegin(), TypeRange))
return ExprError();
if (ArraySize && !checkArrayElementAlignment(AllocType, TypeRange.getBegin()))
return ExprError();
// In ARC, infer 'retaining' for the allocated
if (getLangOpts().ObjCAutoRefCount &&
AllocType.getObjCLifetime() == Qualifiers::OCL_None &&
AllocType->isObjCLifetimeType()) {
AllocType = Context.getLifetimeQualifiedType(AllocType,
AllocType->getObjCARCImplicitLifetime());
}
QualType ResultType = Context.getPointerType(AllocType);
if (ArraySize && *ArraySize &&
(*ArraySize)->getType()->isNonOverloadPlaceholderType()) {
ExprResult result = CheckPlaceholderExpr(*ArraySize);
if (result.isInvalid()) return ExprError();
ArraySize = result.get();
}
// C++98 5.3.4p6: "The expression in a direct-new-declarator shall have
// integral or enumeration type with a non-negative value."
// C++11 [expr.new]p6: The expression [...] shall be of integral or unscoped
// enumeration type, or a class type for which a single non-explicit
// conversion function to integral or unscoped enumeration type exists.
// C++1y [expr.new]p6: The expression [...] is implicitly converted to
// std::size_t.
std::optional<uint64_t> KnownArraySize;
if (ArraySize && *ArraySize && !(*ArraySize)->isTypeDependent()) {
ExprResult ConvertedSize;
if (getLangOpts().CPlusPlus14) {
assert(Context.getTargetInfo().getIntWidth() && "Builtin type of size 0?");
ConvertedSize = PerformImplicitConversion(
*ArraySize, Context.getSizeType(), AssignmentAction::Converting);
if (!ConvertedSize.isInvalid() &&
(*ArraySize)->getType()->getAs<RecordType>())
// Diagnose the compatibility of this conversion.
Diag(StartLoc, diag::warn_cxx98_compat_array_size_conversion)
<< (*ArraySize)->getType() << 0 << "'size_t'";
} else {
class SizeConvertDiagnoser : public ICEConvertDiagnoser {
protected:
Expr *ArraySize;
public:
SizeConvertDiagnoser(Expr *ArraySize)
: ICEConvertDiagnoser(/*AllowScopedEnumerations*/false, false, false),
ArraySize(ArraySize) {}
SemaDiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc,
QualType T) override {
return S.Diag(Loc, diag::err_array_size_not_integral)
<< S.getLangOpts().CPlusPlus11 << T;
}
SemaDiagnosticBuilder diagnoseIncomplete(
Sema &S, SourceLocation Loc, QualType T) override {
return S.Diag(Loc, diag::err_array_size_incomplete_type)
<< T << ArraySize->getSourceRange();
}
SemaDiagnosticBuilder diagnoseExplicitConv(
Sema &S, SourceLocation Loc, QualType T, QualType ConvTy) override {
return S.Diag(Loc, diag::err_array_size_explicit_conversion) << T << ConvTy;
}
SemaDiagnosticBuilder noteExplicitConv(
Sema &S, CXXConversionDecl *Conv, QualType ConvTy) override {
return S.Diag(Conv->getLocation(), diag::note_array_size_conversion)
<< ConvTy->isEnumeralType() << ConvTy;
}
SemaDiagnosticBuilder diagnoseAmbiguous(
Sema &S, SourceLocation Loc, QualType T) override {
return S.Diag(Loc, diag::err_array_size_ambiguous_conversion) << T;
}
SemaDiagnosticBuilder noteAmbiguous(
Sema &S, CXXConversionDecl *Conv, QualType ConvTy) override {
return S.Diag(Conv->getLocation(), diag::note_array_size_conversion)
<< ConvTy->isEnumeralType() << ConvTy;
}
SemaDiagnosticBuilder diagnoseConversion(Sema &S, SourceLocation Loc,
QualType T,
QualType ConvTy) override {
return S.Diag(Loc,
S.getLangOpts().CPlusPlus11
? diag::warn_cxx98_compat_array_size_conversion
: diag::ext_array_size_conversion)
<< T << ConvTy->isEnumeralType() << ConvTy;
}
} SizeDiagnoser(*ArraySize);
ConvertedSize = PerformContextualImplicitConversion(StartLoc, *ArraySize,
SizeDiagnoser);
}
if (ConvertedSize.isInvalid())
return ExprError();
ArraySize = ConvertedSize.get();
QualType SizeType = (*ArraySize)->getType();
if (!SizeType->isIntegralOrUnscopedEnumerationType())
return ExprError();
// C++98 [expr.new]p7:
// The expression in a direct-new-declarator shall have integral type
// with a non-negative value.
//
// Let's see if this is a constant < 0. If so, we reject it out of hand,
// per CWG1464. Otherwise, if it's not a constant, we must have an
// unparenthesized array type.
// We've already performed any required implicit conversion to integer or
// unscoped enumeration type.
// FIXME: Per CWG1464, we are required to check the value prior to
// converting to size_t. This will never find a negative array size in
// C++14 onwards, because Value is always unsigned here!
if (std::optional<llvm::APSInt> Value =
(*ArraySize)->getIntegerConstantExpr(Context)) {
if (Value->isSigned() && Value->isNegative()) {
return ExprError(Diag((*ArraySize)->getBeginLoc(),
diag::err_typecheck_negative_array_size)
<< (*ArraySize)->getSourceRange());
}
if (!AllocType->isDependentType()) {
unsigned ActiveSizeBits =
ConstantArrayType::getNumAddressingBits(Context, AllocType, *Value);
if (ActiveSizeBits > ConstantArrayType::getMaxSizeBits(Context))
return ExprError(
Diag((*ArraySize)->getBeginLoc(), diag::err_array_too_large)
<< toString(*Value, 10) << (*ArraySize)->getSourceRange());
}
KnownArraySize = Value->getZExtValue();
} else if (TypeIdParens.isValid()) {
// Can't have dynamic array size when the type-id is in parentheses.
Diag((*ArraySize)->getBeginLoc(), diag::ext_new_paren_array_nonconst)
<< (*ArraySize)->getSourceRange()
<< FixItHint::CreateRemoval(TypeIdParens.getBegin())
<< FixItHint::CreateRemoval(TypeIdParens.getEnd());
TypeIdParens = SourceRange();
}
// Note that we do *not* convert the argument in any way. It can
// be signed, larger than size_t, whatever.
}
FunctionDecl *OperatorNew = nullptr;
FunctionDecl *OperatorDelete = nullptr;
unsigned Alignment =
AllocType->isDependentType() ? 0 : Context.getTypeAlign(AllocType);
unsigned NewAlignment = Context.getTargetInfo().getNewAlign();
ImplicitAllocationParameters IAP = {
AllocType, ShouldUseTypeAwareOperatorNewOrDelete(),
alignedAllocationModeFromBool(getLangOpts().AlignedAllocation &&
Alignment > NewAlignment)};
if (CheckArgsForPlaceholders(PlacementArgs))
return ExprError();
AllocationFunctionScope Scope = UseGlobal ? AFS_Global : AFS_Both;
SourceRange AllocationParameterRange = Range;
if (PlacementLParen.isValid() && PlacementRParen.isValid())
AllocationParameterRange = SourceRange(PlacementLParen, PlacementRParen);
if (!AllocType->isDependentType() &&
!Expr::hasAnyTypeDependentArguments(PlacementArgs) &&
FindAllocationFunctions(StartLoc, AllocationParameterRange, Scope, Scope,
AllocType, ArraySize.has_value(), IAP,
PlacementArgs, OperatorNew, OperatorDelete))
return ExprError();
// If this is an array allocation, compute whether the usual array
// deallocation function for the type has a size_t parameter.
bool UsualArrayDeleteWantsSize = false;
if (ArraySize && !AllocType->isDependentType())
UsualArrayDeleteWantsSize = doesUsualArrayDeleteWantSize(
*this, StartLoc, IAP.PassTypeIdentity, AllocType);
SmallVector<Expr *, 8> AllPlaceArgs;
if (OperatorNew) {
auto *Proto = OperatorNew->getType()->castAs<FunctionProtoType>();
VariadicCallType CallType = Proto->isVariadic() ? VariadicFunction
: VariadicDoesNotApply;
// We've already converted the placement args, just fill in any default
// arguments. Skip the first parameter because we don't have a corresponding
// argument. Skip the second parameter too if we're passing in the
// alignment; we've already filled it in.
unsigned NumImplicitArgs = 1;
if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
assert(OperatorNew->isTypeAwareOperatorNewOrDelete());
NumImplicitArgs++;
}
if (isAlignedAllocation(IAP.PassAlignment))
NumImplicitArgs++;
if (GatherArgumentsForCall(AllocationParameterRange.getBegin(), OperatorNew,
Proto, NumImplicitArgs, PlacementArgs,
AllPlaceArgs, CallType))
return ExprError();
if (!AllPlaceArgs.empty())
PlacementArgs = AllPlaceArgs;
// We would like to perform some checking on the given `operator new` call,
// but the PlacementArgs does not contain the implicit arguments,
// namely allocation size and maybe allocation alignment,
// so we need to conjure them.
QualType SizeTy = Context.getSizeType();
unsigned SizeTyWidth = Context.getTypeSize(SizeTy);
llvm::APInt SingleEltSize(
SizeTyWidth, Context.getTypeSizeInChars(AllocType).getQuantity());
// How many bytes do we want to allocate here?
std::optional<llvm::APInt> AllocationSize;
if (!ArraySize && !AllocType->isDependentType()) {
// For non-array operator new, we only want to allocate one element.
AllocationSize = SingleEltSize;
} else if (KnownArraySize && !AllocType->isDependentType()) {
// For array operator new, only deal with static array size case.
bool Overflow;
AllocationSize = llvm::APInt(SizeTyWidth, *KnownArraySize)
.umul_ov(SingleEltSize, Overflow);
(void)Overflow;
assert(
!Overflow &&
"Expected that all the overflows would have been handled already.");
}
IntegerLiteral AllocationSizeLiteral(
Context, AllocationSize.value_or(llvm::APInt::getZero(SizeTyWidth)),
SizeTy, StartLoc);
// Otherwise, if we failed to constant-fold the allocation size, we'll
// just give up and pass-in something opaque, that isn't a null pointer.
OpaqueValueExpr OpaqueAllocationSize(StartLoc, SizeTy, VK_PRValue,
OK_Ordinary, /*SourceExpr=*/nullptr);
// Let's synthesize the alignment argument in case we will need it.
// Since we *really* want to allocate these on stack, this is slightly ugly
// because there might not be a `std::align_val_t` type.
EnumDecl *StdAlignValT = getStdAlignValT();
QualType AlignValT =
StdAlignValT ? Context.getTypeDeclType(StdAlignValT) : SizeTy;
IntegerLiteral AlignmentLiteral(
Context,
llvm::APInt(Context.getTypeSize(SizeTy),
Alignment / Context.getCharWidth()),
SizeTy, StartLoc);
ImplicitCastExpr DesiredAlignment(ImplicitCastExpr::OnStack, AlignValT,
CK_IntegralCast, &AlignmentLiteral,
VK_PRValue, FPOptionsOverride());
// Adjust placement args by prepending conjured size and alignment exprs.
llvm::SmallVector<Expr *, 8> CallArgs;
CallArgs.reserve(NumImplicitArgs + PlacementArgs.size());
CallArgs.emplace_back(AllocationSize
? static_cast<Expr *>(&AllocationSizeLiteral)
: &OpaqueAllocationSize);
if (isAlignedAllocation(IAP.PassAlignment))
CallArgs.emplace_back(&DesiredAlignment);
CallArgs.insert(CallArgs.end(), PlacementArgs.begin(), PlacementArgs.end());
DiagnoseSentinelCalls(OperatorNew, PlacementLParen, CallArgs);
checkCall(OperatorNew, Proto, /*ThisArg=*/nullptr, CallArgs,
/*IsMemberFunction=*/false, StartLoc, Range, CallType);
// Warn if the type is over-aligned and is being allocated by (unaligned)
// global operator new.
if (PlacementArgs.empty() && !isAlignedAllocation(IAP.PassAlignment) &&
(OperatorNew->isImplicit() ||
(OperatorNew->getBeginLoc().isValid() &&
getSourceManager().isInSystemHeader(OperatorNew->getBeginLoc())))) {
if (Alignment > NewAlignment)
Diag(StartLoc, diag::warn_overaligned_type)
<< AllocType
<< unsigned(Alignment / Context.getCharWidth())
<< unsigned(NewAlignment / Context.getCharWidth());
}
}
// Array 'new' can't have any initializers except empty parentheses.
// Initializer lists are also allowed, in C++11. Rely on the parser for the
// dialect distinction.
if (ArraySize && !isLegalArrayNewInitializer(InitStyle, Initializer,
getLangOpts().CPlusPlus20)) {
SourceRange InitRange(Exprs.front()->getBeginLoc(),
Exprs.back()->getEndLoc());
Diag(StartLoc, diag::err_new_array_init_args) << InitRange;
return ExprError();
}
// If we can perform the initialization, and we've not already done so,
// do it now.
if (!AllocType->isDependentType() &&
!Expr::hasAnyTypeDependentArguments(Exprs)) {
// The type we initialize is the complete type, including the array bound.
QualType InitType;
if (KnownArraySize)
InitType = Context.getConstantArrayType(
AllocType,
llvm::APInt(Context.getTypeSize(Context.getSizeType()),
*KnownArraySize),
*ArraySize, ArraySizeModifier::Normal, 0);
else if (ArraySize)
InitType = Context.getIncompleteArrayType(AllocType,
ArraySizeModifier::Normal, 0);
else
InitType = AllocType;
InitializedEntity Entity
= InitializedEntity::InitializeNew(StartLoc, InitType);
InitializationSequence InitSeq(*this, Entity, Kind, Exprs);
ExprResult FullInit = InitSeq.Perform(*this, Entity, Kind, Exprs);
if (FullInit.isInvalid())
return ExprError();
// FullInit is our initializer; strip off CXXBindTemporaryExprs, because
// we don't want the initialized object to be destructed.
// FIXME: We should not create these in the first place.
if (CXXBindTemporaryExpr *Binder =
dyn_cast_or_null<CXXBindTemporaryExpr>(FullInit.get()))
FullInit = Binder->getSubExpr();
Initializer = FullInit.get();
// FIXME: If we have a KnownArraySize, check that the array bound of the
// initializer is no greater than that constant value.
if (ArraySize && !*ArraySize) {
auto *CAT = Context.getAsConstantArrayType(Initializer->getType());
if (CAT) {
// FIXME: Track that the array size was inferred rather than explicitly
// specified.
ArraySize = IntegerLiteral::Create(
Context, CAT->getSize(), Context.getSizeType(), TypeRange.getEnd());
} else {
Diag(TypeRange.getEnd(), diag::err_new_array_size_unknown_from_init)
<< Initializer->getSourceRange();
}
}
}
// Mark the new and delete operators as referenced.
if (OperatorNew) {
if (DiagnoseUseOfDecl(OperatorNew, StartLoc))
return ExprError();
MarkFunctionReferenced(StartLoc, OperatorNew);
}
if (OperatorDelete) {
if (DiagnoseUseOfDecl(OperatorDelete, StartLoc))
return ExprError();
MarkFunctionReferenced(StartLoc, OperatorDelete);
}
return CXXNewExpr::Create(Context, UseGlobal, OperatorNew, OperatorDelete,
IAP, UsualArrayDeleteWantsSize, PlacementArgs,
TypeIdParens, ArraySize, InitStyle, Initializer,
ResultType, AllocTypeInfo, Range, DirectInitRange);
}
bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc,
SourceRange R) {
// C++ 5.3.4p1: "[The] type shall be a complete object type, but not an
// abstract class type or array thereof.
if (AllocType->isFunctionType())
return Diag(Loc, diag::err_bad_new_type)
<< AllocType << 0 << R;
else if (AllocType->isReferenceType())
return Diag(Loc, diag::err_bad_new_type)
<< AllocType << 1 << R;
else if (!AllocType->isDependentType() &&
RequireCompleteSizedType(
Loc, AllocType, diag::err_new_incomplete_or_sizeless_type, R))
return true;
else if (RequireNonAbstractType(Loc, AllocType,
diag::err_allocation_of_abstract_type))
return true;
else if (AllocType->isVariablyModifiedType())
return Diag(Loc, diag::err_variably_modified_new_type)
<< AllocType;
else if (AllocType.getAddressSpace() != LangAS::Default &&
!getLangOpts().OpenCLCPlusPlus)
return Diag(Loc, diag::err_address_space_qualified_new)
<< AllocType.getUnqualifiedType()
<< AllocType.getQualifiers().getAddressSpaceAttributePrintValue();
else if (getLangOpts().ObjCAutoRefCount) {
if (const ArrayType *AT = Context.getAsArrayType(AllocType)) {
QualType BaseAllocType = Context.getBaseElementType(AT);
if (BaseAllocType.getObjCLifetime() == Qualifiers::OCL_None &&
BaseAllocType->isObjCLifetimeType())
return Diag(Loc, diag::err_arc_new_array_without_ownership)
<< BaseAllocType;
}
}
return false;
}
enum class ResolveMode { Typed, Untyped };
static bool resolveAllocationOverloadInterior(
Sema &S, LookupResult &R, SourceRange Range, ResolveMode Mode,
SmallVectorImpl<Expr *> &Args, AlignedAllocationMode &PassAlignment,
FunctionDecl *&Operator, OverloadCandidateSet *AlignedCandidates,
Expr *AlignArg, bool Diagnose) {
unsigned NonTypeArgumentOffset = 0;
if (Mode == ResolveMode::Typed) {
++NonTypeArgumentOffset;
}
OverloadCandidateSet Candidates(R.getNameLoc(),
OverloadCandidateSet::CSK_Normal);
for (LookupResult::iterator Alloc = R.begin(), AllocEnd = R.end();
Alloc != AllocEnd; ++Alloc) {
// Even member operator new/delete are implicitly treated as
// static, so don't use AddMemberCandidate.
NamedDecl *D = (*Alloc)->getUnderlyingDecl();
bool IsTypeAware = D->getAsFunction()->isTypeAwareOperatorNewOrDelete();
if (IsTypeAware == (Mode != ResolveMode::Typed))
continue;
if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) {
S.AddTemplateOverloadCandidate(FnTemplate, Alloc.getPair(),
/*ExplicitTemplateArgs=*/nullptr, Args,
Candidates,
/*SuppressUserConversions=*/false);
continue;
}
FunctionDecl *Fn = cast<FunctionDecl>(D);
S.AddOverloadCandidate(Fn, Alloc.getPair(), Args, Candidates,
/*SuppressUserConversions=*/false);
}
// Do the resolution.
OverloadCandidateSet::iterator Best;
switch (Candidates.BestViableFunction(S, R.getNameLoc(), Best)) {
case OR_Success: {
// Got one!
FunctionDecl *FnDecl = Best->Function;
if (S.CheckAllocationAccess(R.getNameLoc(), Range, R.getNamingClass(),
Best->FoundDecl) == Sema::AR_inaccessible)
return true;
Operator = FnDecl;
return false;
}
case OR_No_Viable_Function:
// C++17 [expr.new]p13:
// If no matching function is found and the allocated object type has
// new-extended alignment, the alignment argument is removed from the
// argument list, and overload resolution is performed again.
if (isAlignedAllocation(PassAlignment)) {
PassAlignment = AlignedAllocationMode::No;
AlignArg = Args[NonTypeArgumentOffset + 1];
Args.erase(Args.begin() + NonTypeArgumentOffset + 1);
return resolveAllocationOverloadInterior(S, R, Range, Mode, Args,
PassAlignment, Operator,
&Candidates, AlignArg, Diagnose);
}
// MSVC will fall back on trying to find a matching global operator new
// if operator new[] cannot be found. Also, MSVC will leak by not
// generating a call to operator delete or operator delete[], but we
// will not replicate that bug.
// FIXME: Find out how this interacts with the std::align_val_t fallback
// once MSVC implements it.
if (R.getLookupName().getCXXOverloadedOperator() == OO_Array_New &&
S.Context.getLangOpts().MSVCCompat && Mode != ResolveMode::Typed) {
R.clear();
R.setLookupName(S.Context.DeclarationNames.getCXXOperatorName(OO_New));
S.LookupQualifiedName(R, S.Context.getTranslationUnitDecl());
// FIXME: This will give bad diagnostics pointing at the wrong functions.
return resolveAllocationOverloadInterior(S, R, Range, Mode, Args,
PassAlignment, Operator,
/*Candidates=*/nullptr,
/*AlignArg=*/nullptr, Diagnose);
}
if (Mode == ResolveMode::Typed) {
// If we can't find a matching type aware operator we don't consider this
// a failure.
Operator = nullptr;
return false;
}
if (Diagnose) {
// If this is an allocation of the form 'new (p) X' for some object
// pointer p (or an expression that will decay to such a pointer),
// diagnose the missing inclusion of <new>.
if (!R.isClassLookup() && Args.size() == 2 &&
(Args[1]->getType()->isObjectPointerType() ||
Args[1]->getType()->isArrayType())) {
S.Diag(R.getNameLoc(), diag::err_need_header_before_placement_new)
<< R.getLookupName() << Range;
// Listing the candidates is unlikely to be useful; skip it.
return true;
}
// Finish checking all candidates before we note any. This checking can
// produce additional diagnostics so can't be interleaved with our
// emission of notes.
//
// For an aligned allocation, separately check the aligned and unaligned
// candidates with their respective argument lists.
SmallVector<OverloadCandidate*, 32> Cands;
SmallVector<OverloadCandidate*, 32> AlignedCands;
llvm::SmallVector<Expr*, 4> AlignedArgs;
if (AlignedCandidates) {
auto IsAligned = [NonTypeArgumentOffset](OverloadCandidate &C) {
auto AlignArgOffset = NonTypeArgumentOffset + 1;
return C.Function->getNumParams() > AlignArgOffset &&
C.Function->getParamDecl(AlignArgOffset)
->getType()
->isAlignValT();
};
auto IsUnaligned = [&](OverloadCandidate &C) { return !IsAligned(C); };
AlignedArgs.reserve(Args.size() + NonTypeArgumentOffset + 1);
for (unsigned Idx = 0; Idx < NonTypeArgumentOffset + 1; ++Idx)
AlignedArgs.push_back(Args[Idx]);
AlignedArgs.push_back(AlignArg);
AlignedArgs.append(Args.begin() + NonTypeArgumentOffset + 1,
Args.end());
AlignedCands = AlignedCandidates->CompleteCandidates(
S, OCD_AllCandidates, AlignedArgs, R.getNameLoc(), IsAligned);
Cands = Candidates.CompleteCandidates(S, OCD_AllCandidates, Args,
R.getNameLoc(), IsUnaligned);
} else {
Cands = Candidates.CompleteCandidates(S, OCD_AllCandidates, Args,
R.getNameLoc());
}
S.Diag(R.getNameLoc(), diag::err_ovl_no_viable_function_in_call)
<< R.getLookupName() << Range;
if (AlignedCandidates)
AlignedCandidates->NoteCandidates(S, AlignedArgs, AlignedCands, "",
R.getNameLoc());
Candidates.NoteCandidates(S, Args, Cands, "", R.getNameLoc());
}
return true;
case OR_Ambiguous:
if (Diagnose) {
Candidates.NoteCandidates(
PartialDiagnosticAt(R.getNameLoc(),
S.PDiag(diag::err_ovl_ambiguous_call)
<< R.getLookupName() << Range),
S, OCD_AmbiguousCandidates, Args);
}
return true;
case OR_Deleted: {
if (Diagnose)
S.DiagnoseUseOfDeletedFunction(R.getNameLoc(), Range, R.getLookupName(),
Candidates, Best->Function, Args);
return true;
}
}
llvm_unreachable("Unreachable, bad result from BestViableFunction");
}
enum class DeallocLookupMode { Untyped, OptionallyTyped };
static void LookupGlobalDeallocationFunctions(Sema &S, SourceLocation Loc,
LookupResult &FoundDelete,
DeallocLookupMode Mode,
DeclarationName Name) {
S.LookupQualifiedName(FoundDelete, S.Context.getTranslationUnitDecl());
if (Mode != DeallocLookupMode::OptionallyTyped) {
// We're going to remove either the typed or the non-typed
bool RemoveTypedDecl = Mode == DeallocLookupMode::Untyped;
LookupResult::Filter Filter = FoundDelete.makeFilter();
while (Filter.hasNext()) {
FunctionDecl *FD = Filter.next()->getUnderlyingDecl()->getAsFunction();
if (FD->isTypeAwareOperatorNewOrDelete() == RemoveTypedDecl)
Filter.erase();
}
Filter.done();
}
}
static bool resolveAllocationOverload(
Sema &S, LookupResult &R, SourceRange Range, SmallVectorImpl<Expr *> &Args,
ImplicitAllocationParameters &IAP, FunctionDecl *&Operator,
OverloadCandidateSet *AlignedCandidates, Expr *AlignArg, bool Diagnose) {
Operator = nullptr;
if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
assert(S.isStdTypeIdentity(Args[0]->getType(), nullptr));
// The internal overload resolution work mutates the argument list
// in accordance with the spec. We may want to change that in future,
// but for now we deal with this by making a copy of the non-type-identity
// arguments.
SmallVector<Expr *> UntypedParameters;
UntypedParameters.reserve(Args.size() - 1);
UntypedParameters.push_back(Args[1]);
// Type aware allocation implicitly includes the alignment parameter so
// only include it in the untyped parameter list if alignment was explicitly
// requested
if (isAlignedAllocation(IAP.PassAlignment))
UntypedParameters.push_back(Args[2]);
UntypedParameters.append(Args.begin() + 3, Args.end());
AlignedAllocationMode InitialAlignmentMode = IAP.PassAlignment;
IAP.PassAlignment = AlignedAllocationMode::Yes;
if (resolveAllocationOverloadInterior(
S, R, Range, ResolveMode::Typed, Args, IAP.PassAlignment, Operator,
AlignedCandidates, AlignArg, Diagnose))
return true;
if (Operator)
return false;
// If we got to this point we could not find a matching typed operator
// so we update the IAP flags, and revert to our stored copy of the
// type-identity-less argument list.
IAP.PassTypeIdentity = TypeAwareAllocationMode::No;
IAP.PassAlignment = InitialAlignmentMode;
Args = UntypedParameters;
}
assert(!S.isStdTypeIdentity(Args[0]->getType(), nullptr));
return resolveAllocationOverloadInterior(
S, R, Range, ResolveMode::Untyped, Args, IAP.PassAlignment, Operator,
AlignedCandidates, AlignArg, Diagnose);
}
bool Sema::FindAllocationFunctions(
SourceLocation StartLoc, SourceRange Range,
AllocationFunctionScope NewScope, AllocationFunctionScope DeleteScope,
QualType AllocType, bool IsArray, ImplicitAllocationParameters &IAP,
MultiExprArg PlaceArgs, FunctionDecl *&OperatorNew,
FunctionDecl *&OperatorDelete, bool Diagnose) {
// --- Choosing an allocation function ---
// C++ 5.3.4p8 - 14 & 18
// 1) If looking in AFS_Global scope for allocation functions, only look in
// the global scope. Else, if AFS_Class, only look in the scope of the
// allocated class. If AFS_Both, look in both.
// 2) If an array size is given, look for operator new[], else look for
// operator new.
// 3) The first argument is always size_t. Append the arguments from the
// placement form.
SmallVector<Expr*, 8> AllocArgs;
AllocArgs.reserve(IAP.getNumImplicitArgs() + PlaceArgs.size());
// C++ [expr.new]p8:
// If the allocated type is a non-array type, the allocation
// function's name is operator new and the deallocation function's
// name is operator delete. If the allocated type is an array
// type, the allocation function's name is operator new[] and the
// deallocation function's name is operator delete[].
DeclarationName NewName = Context.DeclarationNames.getCXXOperatorName(
IsArray ? OO_Array_New : OO_New);
QualType AllocElemType = Context.getBaseElementType(AllocType);
// We don't care about the actual value of these arguments.
// FIXME: Should the Sema create the expression and embed it in the syntax
// tree? Or should the consumer just recalculate the value?
// FIXME: Using a dummy value will interact poorly with attribute enable_if.
// We use size_t as a stand in so that we can construct the init
// expr on the stack
QualType TypeIdentity = Context.getSizeType();
if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
QualType SpecializedTypeIdentity =
tryBuildStdTypeIdentity(IAP.Type, StartLoc);
if (!SpecializedTypeIdentity.isNull()) {
TypeIdentity = SpecializedTypeIdentity;
if (RequireCompleteType(StartLoc, TypeIdentity,
diag::err_incomplete_type))
return true;
} else
IAP.PassTypeIdentity = TypeAwareAllocationMode::No;
}
TypeAwareAllocationMode OriginalTypeAwareState = IAP.PassTypeIdentity;
CXXScalarValueInitExpr TypeIdentityParam(TypeIdentity, nullptr, StartLoc);
if (isTypeAwareAllocation(IAP.PassTypeIdentity))
AllocArgs.push_back(&TypeIdentityParam);
QualType SizeTy = Context.getSizeType();
unsigned SizeTyWidth = Context.getTypeSize(SizeTy);
IntegerLiteral Size(Context, llvm::APInt::getZero(SizeTyWidth), SizeTy,
SourceLocation());
AllocArgs.push_back(&Size);
QualType AlignValT = Context.VoidTy;
bool IncludeAlignParam = isAlignedAllocation(IAP.PassAlignment) ||
isTypeAwareAllocation(IAP.PassTypeIdentity);
if (IncludeAlignParam) {
DeclareGlobalNewDelete();
AlignValT = Context.getTypeDeclType(getStdAlignValT());
}
CXXScalarValueInitExpr Align(AlignValT, nullptr, SourceLocation());
if (IncludeAlignParam)
AllocArgs.push_back(&Align);
AllocArgs.insert(AllocArgs.end(), PlaceArgs.begin(), PlaceArgs.end());
// Find the allocation function.
{
LookupResult R(*this, NewName, StartLoc, LookupOrdinaryName);
// C++1z [expr.new]p9:
// If the new-expression begins with a unary :: operator, the allocation
// function's name is looked up in the global scope. Otherwise, if the
// allocated type is a class type T or array thereof, the allocation
// function's name is looked up in the scope of T.
if (AllocElemType->isRecordType() && NewScope != AFS_Global)
LookupQualifiedName(R, AllocElemType->getAsCXXRecordDecl());
// We can see ambiguity here if the allocation function is found in
// multiple base classes.
if (R.isAmbiguous())
return true;
// If this lookup fails to find the name, or if the allocated type is not
// a class type, the allocation function's name is looked up in the
// global scope.
if (R.empty()) {
if (NewScope == AFS_Class)
return true;
LookupQualifiedName(R, Context.getTranslationUnitDecl());
}
if (getLangOpts().OpenCLCPlusPlus && R.empty()) {
if (PlaceArgs.empty()) {
Diag(StartLoc, diag::err_openclcxx_not_supported) << "default new";
} else {
Diag(StartLoc, diag::err_openclcxx_placement_new);
}
return true;
}
assert(!R.empty() && "implicitly declared allocation functions not found");
assert(!R.isAmbiguous() && "global allocation functions are ambiguous");
// We do our own custom access checks below.
R.suppressDiagnostics();
if (resolveAllocationOverload(*this, R, Range, AllocArgs, IAP, OperatorNew,
/*Candidates=*/nullptr,
/*AlignArg=*/nullptr, Diagnose))
return true;
}
// new[] will force emission of vector deleting dtor which needs delete[].
bool MaybeVectorDeletingDtor = false;
if (Context.getTargetInfo().getCXXABI().isMicrosoft()) {
if (AllocElemType->isRecordType() && IsArray) {
auto *RD =
cast<CXXRecordDecl>(AllocElemType->castAs<RecordType>()->getDecl());
CXXDestructorDecl *DD = RD->getDestructor();
MaybeVectorDeletingDtor = DD && DD->isVirtual() && !DD->isDeleted();
}
}
// We don't need an operator delete if we're running under -fno-exceptions.
if (!getLangOpts().Exceptions && !MaybeVectorDeletingDtor) {
OperatorDelete = nullptr;
return false;
}
// Note, the name of OperatorNew might have been changed from array to
// non-array by resolveAllocationOverload.
DeclarationName DeleteName = Context.DeclarationNames.getCXXOperatorName(
OperatorNew->getDeclName().getCXXOverloadedOperator() == OO_Array_New
? OO_Array_Delete
: OO_Delete);
// C++ [expr.new]p19:
//
// If the new-expression begins with a unary :: operator, the
// deallocation function's name is looked up in the global
// scope. Otherwise, if the allocated type is a class type T or an
// array thereof, the deallocation function's name is looked up in
// the scope of T. If this lookup fails to find the name, or if
// the allocated type is not a class type or array thereof, the
// deallocation function's name is looked up in the global scope.
LookupResult FoundDelete(*this, DeleteName, StartLoc, LookupOrdinaryName);
if (AllocElemType->isRecordType() && DeleteScope != AFS_Global) {
auto *RD =
cast<CXXRecordDecl>(AllocElemType->castAs<RecordType>()->getDecl());
LookupQualifiedName(FoundDelete, RD);
}
if (FoundDelete.isAmbiguous())
return true; // FIXME: clean up expressions?
// Filter out any destroying operator deletes. We can't possibly call such a
// function in this context, because we're handling the case where the object
// was not successfully constructed.
// FIXME: This is not covered by the language rules yet.
{
LookupResult::Filter Filter = FoundDelete.makeFilter();
while (Filter.hasNext()) {
auto *FD = dyn_cast<FunctionDecl>(Filter.next()->getUnderlyingDecl());
if (FD && FD->isDestroyingOperatorDelete())
Filter.erase();
}
Filter.done();
}
bool FoundGlobalDelete = FoundDelete.empty();
bool IsClassScopedTypeAwareNew =
isTypeAwareAllocation(IAP.PassTypeIdentity) &&
OperatorNew->getDeclContext()->isRecord();
auto DiagnoseMissingTypeAwareCleanupOperator = [&](bool IsPlacementOperator) {
assert(isTypeAwareAllocation(IAP.PassTypeIdentity));
if (Diagnose) {
Diag(StartLoc, diag::err_mismatching_type_aware_cleanup_deallocator)
<< OperatorNew->getDeclName() << IsPlacementOperator << DeleteName;
Diag(OperatorNew->getLocation(), diag::note_type_aware_operator_declared)
<< OperatorNew->isTypeAwareOperatorNewOrDelete()
<< OperatorNew->getDeclName() << OperatorNew->getDeclContext();
}
};
if (IsClassScopedTypeAwareNew && FoundDelete.empty()) {
DiagnoseMissingTypeAwareCleanupOperator(/*isPlacementNew=*/false);
return true;
}
if (FoundDelete.empty()) {
FoundDelete.clear(LookupOrdinaryName);
if (DeleteScope == AFS_Class)
return true;
DeclareGlobalNewDelete();
DeallocLookupMode LookupMode = isTypeAwareAllocation(OriginalTypeAwareState)
? DeallocLookupMode::OptionallyTyped
: DeallocLookupMode::Untyped;
LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete, LookupMode,
DeleteName);
}
FoundDelete.suppressDiagnostics();
SmallVector<std::pair<DeclAccessPair,FunctionDecl*>, 2> Matches;
// Whether we're looking for a placement operator delete is dictated
// by whether we selected a placement operator new, not by whether
// we had explicit placement arguments. This matters for things like
// struct A { void *operator new(size_t, int = 0); ... };
// A *a = new A()
//
// We don't have any definition for what a "placement allocation function"
// is, but we assume it's any allocation function whose
// parameter-declaration-clause is anything other than (size_t).
//
// FIXME: Should (size_t, std::align_val_t) also be considered non-placement?
// This affects whether an exception from the constructor of an overaligned
// type uses the sized or non-sized form of aligned operator delete.
unsigned NonPlacementNewArgCount = 1; // size parameter
if (isTypeAwareAllocation(IAP.PassTypeIdentity))
NonPlacementNewArgCount =
/* type-identity */ 1 + /* size */ 1 + /* alignment */ 1;
bool isPlacementNew = !PlaceArgs.empty() ||
OperatorNew->param_size() != NonPlacementNewArgCount ||
OperatorNew->isVariadic();
if (isPlacementNew) {
// C++ [expr.new]p20:
// A declaration of a placement deallocation function matches the
// declaration of a placement allocation function if it has the
// same number of parameters and, after parameter transformations
// (8.3.5), all parameter types except the first are
// identical. [...]
//
// To perform this comparison, we compute the function type that
// the deallocation function should have, and use that type both
// for template argument deduction and for comparison purposes.
QualType ExpectedFunctionType;
{
auto *Proto = OperatorNew->getType()->castAs<FunctionProtoType>();
SmallVector<QualType, 6> ArgTypes;
int InitialParamOffset = 0;
if (isTypeAwareAllocation(IAP.PassTypeIdentity)) {
ArgTypes.push_back(TypeIdentity);
InitialParamOffset = 1;
}
ArgTypes.push_back(Context.VoidPtrTy);
for (unsigned I = ArgTypes.size() - InitialParamOffset,
N = Proto->getNumParams();
I < N; ++I)
ArgTypes.push_back(Proto->getParamType(I));
FunctionProtoType::ExtProtoInfo EPI;
// FIXME: This is not part of the standard's rule.
EPI.Variadic = Proto->isVariadic();
ExpectedFunctionType
= Context.getFunctionType(Context.VoidTy, ArgTypes, EPI);
}
for (LookupResult::iterator D = FoundDelete.begin(),
DEnd = FoundDelete.end();
D != DEnd; ++D) {
FunctionDecl *Fn = nullptr;
if (FunctionTemplateDecl *FnTmpl =
dyn_cast<FunctionTemplateDecl>((*D)->getUnderlyingDecl())) {
// Perform template argument deduction to try to match the
// expected function type.
TemplateDeductionInfo Info(StartLoc);
if (DeduceTemplateArguments(FnTmpl, nullptr, ExpectedFunctionType, Fn,
Info) != TemplateDeductionResult::Success)
continue;
} else
Fn = cast<FunctionDecl>((*D)->getUnderlyingDecl());
if (Context.hasSameType(adjustCCAndNoReturn(Fn->getType(),
ExpectedFunctionType,
/*AdjustExcpetionSpec*/true),
ExpectedFunctionType))
Matches.push_back(std::make_pair(D.getPair(), Fn));
}
if (getLangOpts().CUDA)
CUDA().EraseUnwantedMatches(getCurFunctionDecl(/*AllowLambda=*/true),
Matches);
if (Matches.empty() && isTypeAwareAllocation(IAP.PassTypeIdentity)) {
DiagnoseMissingTypeAwareCleanupOperator(isPlacementNew);
return true;
}
} else {
// C++1y [expr.new]p22:
// For a non-placement allocation function, the normal deallocation
// function lookup is used
//
// Per [expr.delete]p10, this lookup prefers a member operator delete
// without a size_t argument, but prefers a non-member operator delete
// with a size_t where possible (which it always is in this case).
llvm::SmallVector<UsualDeallocFnInfo, 4> BestDeallocFns;
ImplicitDeallocationParameters IDP = {
AllocElemType, OriginalTypeAwareState,
alignedAllocationModeFromBool(
hasNewExtendedAlignment(*this, AllocElemType)),
sizedDeallocationModeFromBool(FoundGlobalDelete)};
UsualDeallocFnInfo Selected = resolveDeallocationOverload(
*this, FoundDelete, IDP, StartLoc, &BestDeallocFns);
if (Selected && BestDeallocFns.empty())
Matches.push_back(std::make_pair(Selected.Found, Selected.FD));
else {
// If we failed to select an operator, all remaining functions are viable
// but ambiguous.
for (auto Fn : BestDeallocFns)
Matches.push_back(std::make_pair(Fn.Found, Fn.FD));
}
}
// C++ [expr.new]p20:
// [...] If the lookup finds a single matching deallocation
// function, that function will be called; otherwise, no
// deallocation function will be called.
if (Matches.size() == 1) {
OperatorDelete = Matches[0].second;
bool FoundTypeAwareOperator =
OperatorDelete->isTypeAwareOperatorNewOrDelete() ||
OperatorNew->isTypeAwareOperatorNewOrDelete();
if (Diagnose && FoundTypeAwareOperator) {
bool MismatchedTypeAwareness =
OperatorDelete->isTypeAwareOperatorNewOrDelete() !=
OperatorNew->isTypeAwareOperatorNewOrDelete();
bool MismatchedContext =
OperatorDelete->getDeclContext() != OperatorNew->getDeclContext();
if (MismatchedTypeAwareness || MismatchedContext) {
FunctionDecl *Operators[] = {OperatorDelete, OperatorNew};
bool TypeAwareOperatorIndex =
OperatorNew->isTypeAwareOperatorNewOrDelete();
Diag(StartLoc, diag::err_mismatching_type_aware_cleanup_deallocator)
<< Operators[TypeAwareOperatorIndex]->getDeclName()
<< isPlacementNew
<< Operators[!TypeAwareOperatorIndex]->getDeclName()
<< Operators[TypeAwareOperatorIndex]->getDeclContext();
Diag(OperatorNew->getLocation(),
diag::note_type_aware_operator_declared)
<< OperatorNew->isTypeAwareOperatorNewOrDelete()
<< OperatorNew->getDeclName() << OperatorNew->getDeclContext();
Diag(OperatorDelete->getLocation(),
diag::note_type_aware_operator_declared)
<< OperatorDelete->isTypeAwareOperatorNewOrDelete()
<< OperatorDelete->getDeclName()
<< OperatorDelete->getDeclContext();
}
}
// C++1z [expr.new]p23:
// If the lookup finds a usual deallocation function (3.7.4.2)
// with a parameter of type std::size_t and that function, considered
// as a placement deallocation function, would have been
// selected as a match for the allocation function, the program
// is ill-formed.
if (getLangOpts().CPlusPlus11 && isPlacementNew &&
isNonPlacementDeallocationFunction(*this, OperatorDelete)) {
UsualDeallocFnInfo Info(*this,
DeclAccessPair::make(OperatorDelete, AS_public),
AllocElemType, StartLoc);
// Core issue, per mail to core reflector, 2016-10-09:
// If this is a member operator delete, and there is a corresponding
// non-sized member operator delete, this isn't /really/ a sized
// deallocation function, it just happens to have a size_t parameter.
bool IsSizedDelete = isSizedDeallocation(Info.IDP.PassSize);
if (IsSizedDelete && !FoundGlobalDelete) {
ImplicitDeallocationParameters SizeTestingIDP = {
AllocElemType, Info.IDP.PassTypeIdentity, Info.IDP.PassAlignment,
SizedDeallocationMode::No};
auto NonSizedDelete = resolveDeallocationOverload(
*this, FoundDelete, SizeTestingIDP, StartLoc);
if (NonSizedDelete &&
!isSizedDeallocation(NonSizedDelete.IDP.PassSize) &&
NonSizedDelete.IDP.PassAlignment == Info.IDP.PassAlignment)
IsSizedDelete = false;
}
if (IsSizedDelete && !isTypeAwareAllocation(IAP.PassTypeIdentity)) {
SourceRange R = PlaceArgs.empty()
? SourceRange()
: SourceRange(PlaceArgs.front()->getBeginLoc(),
PlaceArgs.back()->getEndLoc());
Diag(StartLoc, diag::err_placement_new_non_placement_delete) << R;
if (!OperatorDelete->isImplicit())
Diag(OperatorDelete->getLocation(), diag::note_previous_decl)
<< DeleteName;
}
}
if (CheckDeleteOperator(*this, StartLoc, Range, Diagnose,
FoundDelete.getNamingClass(), Matches[0].first,
Matches[0].second))
return true;
} else if (!Matches.empty()) {
// We found multiple suitable operators. Per [expr.new]p20, that means we
// call no 'operator delete' function, but we should at least warn the user.
// FIXME: Suppress this warning if the construction cannot throw.
Diag(StartLoc, diag::warn_ambiguous_suitable_delete_function_found)
<< DeleteName << AllocElemType;
for (auto &Match : Matches)
Diag(Match.second->getLocation(),
diag::note_member_declared_here) << DeleteName;
}
return false;
}
void Sema::DeclareGlobalNewDelete() {
if (GlobalNewDeleteDeclared)
return;
// The implicitly declared new and delete operators
// are not supported in OpenCL.
if (getLangOpts().OpenCLCPlusPlus)
return;
// C++ [basic.stc.dynamic.general]p2:
// The library provides default definitions for the global allocation
// and deallocation functions. Some global allocation and deallocation
// functions are replaceable ([new.delete]); these are attached to the
// global module ([module.unit]).
if (getLangOpts().CPlusPlusModules && getCurrentModule())
PushGlobalModuleFragment(SourceLocation());
// C++ [basic.std.dynamic]p2:
// [...] The following allocation and deallocation functions (18.4) are
// implicitly declared in global scope in each translation unit of a
// program
//
// C++03:
// void* operator new(std::size_t) throw(std::bad_alloc);
// void* operator new[](std::size_t) throw(std::bad_alloc);
// void operator delete(void*) throw();
// void operator delete[](void*) throw();
// C++11:
// void* operator new(std::size_t);
// void* operator new[](std::size_t);
// void operator delete(void*) noexcept;
// void operator delete[](void*) noexcept;
// C++1y:
// void* operator new(std::size_t);
// void* operator new[](std::size_t);
// void operator delete(void*) noexcept;
// void operator delete[](void*) noexcept;
// void operator delete(void*, std::size_t) noexcept;
// void operator delete[](void*, std::size_t) noexcept;
//
// These implicit declarations introduce only the function names operator
// new, operator new[], operator delete, operator delete[].
//
// Here, we need to refer to std::bad_alloc, so we will implicitly declare
// "std" or "bad_alloc" as necessary to form the exception specification.
// However, we do not make these implicit declarations visible to name
// lookup.
if (!StdBadAlloc && !getLangOpts().CPlusPlus11) {
// The "std::bad_alloc" class has not yet been declared, so build it
// implicitly.
StdBadAlloc = CXXRecordDecl::Create(
Context, TagTypeKind::Class, getOrCreateStdNamespace(),
SourceLocation(), SourceLocation(),
&PP.getIdentifierTable().get("bad_alloc"), nullptr);
getStdBadAlloc()->setImplicit(true);
// The implicitly declared "std::bad_alloc" should live in global module
// fragment.
if (TheGlobalModuleFragment) {
getStdBadAlloc()->setModuleOwnershipKind(
Decl::ModuleOwnershipKind::ReachableWhenImported);
getStdBadAlloc()->setLocalOwningModule(TheGlobalModuleFragment);
}
}
if (!StdAlignValT && getLangOpts().AlignedAllocation) {
// The "std::align_val_t" enum class has not yet been declared, so build it
// implicitly.
auto *AlignValT = EnumDecl::Create(
Context, getOrCreateStdNamespace(), SourceLocation(), SourceLocation(),
&PP.getIdentifierTable().get("align_val_t"), nullptr, true, true, true);
// The implicitly declared "std::align_val_t" should live in global module
// fragment.
if (TheGlobalModuleFragment) {
AlignValT->setModuleOwnershipKind(
Decl::ModuleOwnershipKind::ReachableWhenImported);
AlignValT->setLocalOwningModule(TheGlobalModuleFragment);
}
AlignValT->setIntegerType(Context.getSizeType());
AlignValT->setPromotionType(Context.getSizeType());
AlignValT->setImplicit(true);
StdAlignValT = AlignValT;
}
GlobalNewDeleteDeclared = true;
QualType VoidPtr = Context.getPointerType(Context.VoidTy);
QualType SizeT = Context.getSizeType();
auto DeclareGlobalAllocationFunctions = [&](OverloadedOperatorKind Kind,
QualType Return, QualType Param) {
llvm::SmallVector<QualType, 3> Params;
Params.push_back(Param);
// Create up to four variants of the function (sized/aligned).
bool HasSizedVariant = getLangOpts().SizedDeallocation &&
(Kind == OO_Delete || Kind == OO_Array_Delete);
bool HasAlignedVariant = getLangOpts().AlignedAllocation;
int NumSizeVariants = (HasSizedVariant ? 2 : 1);
int NumAlignVariants = (HasAlignedVariant ? 2 : 1);
for (int Sized = 0; Sized < NumSizeVariants; ++Sized) {
if (Sized)
Params.push_back(SizeT);
for (int Aligned = 0; Aligned < NumAlignVariants; ++Aligned) {
if (Aligned)
Params.push_back(Context.getTypeDeclType(getStdAlignValT()));
DeclareGlobalAllocationFunction(
Context.DeclarationNames.getCXXOperatorName(Kind), Return, Params);
if (Aligned)
Params.pop_back();
}
}
};
DeclareGlobalAllocationFunctions(OO_New, VoidPtr, SizeT);
DeclareGlobalAllocationFunctions(OO_Array_New, VoidPtr, SizeT);
DeclareGlobalAllocationFunctions(OO_Delete, Context.VoidTy, VoidPtr);
DeclareGlobalAllocationFunctions(OO_Array_Delete, Context.VoidTy, VoidPtr);
if (getLangOpts().CPlusPlusModules && getCurrentModule())
PopGlobalModuleFragment();
}
/// DeclareGlobalAllocationFunction - Declares a single implicit global
/// allocation function if it doesn't already exist.
void Sema::DeclareGlobalAllocationFunction(DeclarationName Name,
QualType Return,
ArrayRef<QualType> Params) {
DeclContext *GlobalCtx = Context.getTranslationUnitDecl();
// Check if this function is already declared.
DeclContext::lookup_result R = GlobalCtx->lookup(Name);
for (DeclContext::lookup_iterator Alloc = R.begin(), AllocEnd = R.end();
Alloc != AllocEnd; ++Alloc) {
// Only look at non-template functions, as it is the predefined,
// non-templated allocation function we are trying to declare here.
if (FunctionDecl *Func = dyn_cast<FunctionDecl>(*Alloc)) {
if (Func->getNumParams() == Params.size()) {
llvm::SmallVector<QualType, 3> FuncParams;
for (auto *P : Func->parameters())
FuncParams.push_back(
Context.getCanonicalType(P->getType().getUnqualifiedType()));
if (llvm::ArrayRef(FuncParams) == Params) {
// Make the function visible to name lookup, even if we found it in
// an unimported module. It either is an implicitly-declared global
// allocation function, or is suppressing that function.
Func->setVisibleDespiteOwningModule();
return;
}
}
}
}
FunctionProtoType::ExtProtoInfo EPI(Context.getDefaultCallingConvention(
/*IsVariadic=*/false, /*IsCXXMethod=*/false, /*IsBuiltin=*/true));
QualType BadAllocType;
bool HasBadAllocExceptionSpec = Name.isAnyOperatorNew();
if (HasBadAllocExceptionSpec) {
if (!getLangOpts().CPlusPlus11) {
BadAllocType = Context.getTypeDeclType(getStdBadAlloc());
assert(StdBadAlloc && "Must have std::bad_alloc declared");
EPI.ExceptionSpec.Type = EST_Dynamic;
EPI.ExceptionSpec.Exceptions = llvm::ArrayRef(BadAllocType);
}
if (getLangOpts().NewInfallible) {
EPI.ExceptionSpec.Type = EST_DynamicNone;
}
} else {
EPI.ExceptionSpec =
getLangOpts().CPlusPlus11 ? EST_BasicNoexcept : EST_DynamicNone;
}
auto CreateAllocationFunctionDecl = [&](Attr *ExtraAttr) {
QualType FnType = Context.getFunctionType(Return, Params, EPI);
FunctionDecl *Alloc = FunctionDecl::Create(
Context, GlobalCtx, SourceLocation(), SourceLocation(), Name, FnType,
/*TInfo=*/nullptr, SC_None, getCurFPFeatures().isFPConstrained(), false,
true);
Alloc->setImplicit();
// Global allocation functions should always be visible.
Alloc->setVisibleDespiteOwningModule();
if (HasBadAllocExceptionSpec && getLangOpts().NewInfallible &&
!getLangOpts().CheckNew)
Alloc->addAttr(
ReturnsNonNullAttr::CreateImplicit(Context, Alloc->getLocation()));
// C++ [basic.stc.dynamic.general]p2:
// The library provides default definitions for the global allocation
// and deallocation functions. Some global allocation and deallocation
// functions are replaceable ([new.delete]); these are attached to the
// global module ([module.unit]).
//
// In the language wording, these functions are attched to the global
// module all the time. But in the implementation, the global module
// is only meaningful when we're in a module unit. So here we attach
// these allocation functions to global module conditionally.
if (TheGlobalModuleFragment) {
Alloc->setModuleOwnershipKind(
Decl::ModuleOwnershipKind::ReachableWhenImported);
Alloc->setLocalOwningModule(TheGlobalModuleFragment);
}
if (LangOpts.hasGlobalAllocationFunctionVisibility())
Alloc->addAttr(VisibilityAttr::CreateImplicit(
Context, LangOpts.hasHiddenGlobalAllocationFunctionVisibility()
? VisibilityAttr::Hidden
: LangOpts.hasProtectedGlobalAllocationFunctionVisibility()
? VisibilityAttr::Protected
: VisibilityAttr::Default));
llvm::SmallVector<ParmVarDecl *, 3> ParamDecls;
for (QualType T : Params) {
ParamDecls.push_back(ParmVarDecl::Create(
Context, Alloc, SourceLocation(), SourceLocation(), nullptr, T,
/*TInfo=*/nullptr, SC_None, nullptr));
ParamDecls.back()->setImplicit();
}
Alloc->setParams(ParamDecls);
if (ExtraAttr)
Alloc->addAttr(ExtraAttr);
AddKnownFunctionAttributesForReplaceableGlobalAllocationFunction(Alloc);
Context.getTranslationUnitDecl()->addDecl(Alloc);
IdResolver.tryAddTopLevelDecl(Alloc, Name);
};
if (!LangOpts.CUDA)
CreateAllocationFunctionDecl(nullptr);
else {
// Host and device get their own declaration so each can be
// defined or re-declared independently.
CreateAllocationFunctionDecl(CUDAHostAttr::CreateImplicit(Context));
CreateAllocationFunctionDecl(CUDADeviceAttr::CreateImplicit(Context));
}
}
FunctionDecl *
Sema::FindUsualDeallocationFunction(SourceLocation StartLoc,
ImplicitDeallocationParameters IDP,
DeclarationName Name) {
DeclareGlobalNewDelete();
LookupResult FoundDelete(*this, Name, StartLoc, LookupOrdinaryName);
LookupGlobalDeallocationFunctions(*this, StartLoc, FoundDelete,
DeallocLookupMode::OptionallyTyped, Name);
// FIXME: It's possible for this to result in ambiguity, through a
// user-declared variadic operator delete or the enable_if attribute. We
// should probably not consider those cases to be usual deallocation
// functions. But for now we just make an arbitrary choice in that case.
auto Result = resolveDeallocationOverload(*this, FoundDelete, IDP, StartLoc);
if (!Result)
return nullptr;
if (CheckDeleteOperator(*this, StartLoc, StartLoc, /*Diagnose=*/true,
FoundDelete.getNamingClass(), Result.Found,
Result.FD))
return nullptr;
assert(Result.FD && "operator delete missing from global scope?");
return Result.FD;
}
FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
CXXRecordDecl *RD,
DeclarationName Name,
bool Diagnose) {
FunctionDecl *OperatorDelete = nullptr;
QualType DeallocType = Context.getRecordType(RD);
ImplicitDeallocationParameters IDP = {
DeallocType, ShouldUseTypeAwareOperatorNewOrDelete(),
AlignedAllocationMode::No, SizedDeallocationMode::No};
if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete, IDP, Diagnose))
return nullptr;
if (OperatorDelete)
return OperatorDelete;
// If there's no class-specific operator delete, look up the global delete.
QualType RecordType = Context.getRecordType(RD);
IDP.PassAlignment =
alignedAllocationModeFromBool(hasNewExtendedAlignment(*this, RecordType));
IDP.PassSize = SizedDeallocationMode::Yes;
return FindUsualDeallocationFunction(Loc, IDP, Name);
}
bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
DeclarationName Name,
FunctionDecl *&Operator,
ImplicitDeallocationParameters IDP,
bool Diagnose) {
LookupResult Found(*this, Name, StartLoc, LookupOrdinaryName);
// Try to find operator delete/operator delete[] in class scope.
LookupQualifiedName(Found, RD);
if (Found.isAmbiguous()) {
if (!Diagnose)
Found.suppressDiagnostics();
return true;
}
Found.suppressDiagnostics();
if (!isAlignedAllocation(IDP.PassAlignment) &&
hasNewExtendedAlignment(*this, Context.getRecordType(RD)))
IDP.PassAlignment = AlignedAllocationMode::Yes;
// C++17 [expr.delete]p10:
// If the deallocation functions have class scope, the one without a
// parameter of type std::size_t is selected.
llvm::SmallVector<UsualDeallocFnInfo, 4> Matches;
resolveDeallocationOverload(*this, Found, IDP, StartLoc, &Matches);
// If we could find an overload, use it.
if (Matches.size() == 1) {
Operator = cast<CXXMethodDecl>(Matches[0].FD);
return CheckDeleteOperator(*this, StartLoc, StartLoc, Diagnose,
Found.getNamingClass(), Matches[0].Found,
Operator);
}
// We found multiple suitable operators; complain about the ambiguity.
// FIXME: The standard doesn't say to do this; it appears that the intent
// is that this should never happen.
if (!Matches.empty()) {
if (Diagnose) {
Diag(StartLoc, diag::err_ambiguous_suitable_delete_member_function_found)
<< Name << RD;
for (auto &Match : Matches)
Diag(Match.FD->getLocation(), diag::note_member_declared_here) << Name;
}
return true;
}
// We did find operator delete/operator delete[] declarations, but
// none of them were suitable.
if (!Found.empty()) {
if (Diagnose) {
Diag(StartLoc, diag::err_no_suitable_delete_member_function_found)
<< Name << RD;
for (NamedDecl *D : Found)
Diag(D->getUnderlyingDecl()->getLocation(),
diag::note_member_declared_here) << Name;
}
return true;
}
Operator = nullptr;
return false;
}
namespace {
/// Checks whether delete-expression, and new-expression used for
/// initializing deletee have the same array form.
class MismatchingNewDeleteDetector {
public:
enum MismatchResult {
/// Indicates that there is no mismatch or a mismatch cannot be proven.
NoMismatch,
/// Indicates that variable is initialized with mismatching form of \a new.
VarInitMismatches,
/// Indicates that member is initialized with mismatching form of \a new.
MemberInitMismatches,
/// Indicates that 1 or more constructors' definitions could not been
/// analyzed, and they will be checked again at the end of translation unit.
AnalyzeLater
};
/// \param EndOfTU True, if this is the final analysis at the end of
/// translation unit. False, if this is the initial analysis at the point
/// delete-expression was encountered.
explicit MismatchingNewDeleteDetector(bool EndOfTU)
: Field(nullptr), IsArrayForm(false), EndOfTU(EndOfTU),
HasUndefinedConstructors(false) {}
/// Checks whether pointee of a delete-expression is initialized with
/// matching form of new-expression.
///
/// If return value is \c VarInitMismatches or \c MemberInitMismatches at the
/// point where delete-expression is encountered, then a warning will be
/// issued immediately. If return value is \c AnalyzeLater at the point where
/// delete-expression is seen, then member will be analyzed at the end of
/// translation unit. \c AnalyzeLater is returned iff at least one constructor
/// couldn't be analyzed. If at least one constructor initializes the member
/// with matching type of new, the return value is \c NoMismatch.
MismatchResult analyzeDeleteExpr(const CXXDeleteExpr *DE);
/// Analyzes a class member.
/// \param Field Class member to analyze.
/// \param DeleteWasArrayForm Array form-ness of the delete-expression used
/// for deleting the \p Field.
MismatchResult analyzeField(FieldDecl *Field, bool DeleteWasArrayForm);
FieldDecl *Field;
/// List of mismatching new-expressions used for initialization of the pointee
llvm::SmallVector<const CXXNewExpr *, 4> NewExprs;
/// Indicates whether delete-expression was in array form.
bool IsArrayForm;
private:
const bool EndOfTU;
/// Indicates that there is at least one constructor without body.
bool HasUndefinedConstructors;
/// Returns \c CXXNewExpr from given initialization expression.
/// \param E Expression used for initializing pointee in delete-expression.
/// E can be a single-element \c InitListExpr consisting of new-expression.
const CXXNewExpr *getNewExprFromInitListOrExpr(const Expr *E);
/// Returns whether member is initialized with mismatching form of
/// \c new either by the member initializer or in-class initialization.
///
/// If bodies of all constructors are not visible at the end of translation
/// unit or at least one constructor initializes member with the matching
/// form of \c new, mismatch cannot be proven, and this function will return
/// \c NoMismatch.
MismatchResult analyzeMemberExpr(const MemberExpr *ME);
/// Returns whether variable is initialized with mismatching form of
/// \c new.
///
/// If variable is initialized with matching form of \c new or variable is not
/// initialized with a \c new expression, this function will return true.
/// If variable is initialized with mismatching form of \c new, returns false.
/// \param D Variable to analyze.
bool hasMatchingVarInit(const DeclRefExpr *D);
/// Checks whether the constructor initializes pointee with mismatching
/// form of \c new.
///
/// Returns true, if member is initialized with matching form of \c new in
/// member initializer list. Returns false, if member is initialized with the
/// matching form of \c new in this constructor's initializer or given
/// constructor isn't defined at the point where delete-expression is seen, or
/// member isn't initialized by the constructor.
bool hasMatchingNewInCtor(const CXXConstructorDecl *CD);
/// Checks whether member is initialized with matching form of
/// \c new in member initializer list.
bool hasMatchingNewInCtorInit(const CXXCtorInitializer *CI);
/// Checks whether member is initialized with mismatching form of \c new by
/// in-class initializer.
MismatchResult analyzeInClassInitializer();
};
}
MismatchingNewDeleteDetector::MismatchResult
MismatchingNewDeleteDetector::analyzeDeleteExpr(const CXXDeleteExpr *DE) {
NewExprs.clear();
assert(DE && "Expected delete-expression");
IsArrayForm = DE->isArrayForm();
const Expr *E = DE->getArgument()->IgnoreParenImpCasts();
if (const MemberExpr *ME = dyn_cast<const MemberExpr>(E)) {
return analyzeMemberExpr(ME);
} else if (const DeclRefExpr *D = dyn_cast<const DeclRefExpr>(E)) {
if (!hasMatchingVarInit(D))
return VarInitMismatches;
}
return NoMismatch;
}
const CXXNewExpr *
MismatchingNewDeleteDetector::getNewExprFromInitListOrExpr(const Expr *E) {
assert(E != nullptr && "Expected a valid initializer expression");
E = E->IgnoreParenImpCasts();
if (const InitListExpr *ILE = dyn_cast<const InitListExpr>(E)) {
if (ILE->getNumInits() == 1)
E = dyn_cast<const CXXNewExpr>(ILE->getInit(0)->IgnoreParenImpCasts());
}
return dyn_cast_or_null<const CXXNewExpr>(E);
}
bool MismatchingNewDeleteDetector::hasMatchingNewInCtorInit(
const CXXCtorInitializer *CI) {
const CXXNewExpr *NE = nullptr;
if (Field == CI->getMember() &&
(NE = getNewExprFromInitListOrExpr(CI->getInit()))) {
if (NE->isArray() == IsArrayForm)
return true;
else
NewExprs.push_back(NE);
}
return false;
}
bool MismatchingNewDeleteDetector::hasMatchingNewInCtor(
const CXXConstructorDecl *CD) {
if (CD->isImplicit())
return false;
const FunctionDecl *Definition = CD;
if (!CD->isThisDeclarationADefinition() && !CD->isDefined(Definition)) {
HasUndefinedConstructors = true;
return EndOfTU;
}
for (const auto *CI : cast<const CXXConstructorDecl>(Definition)->inits()) {
if (hasMatchingNewInCtorInit(CI))
return true;
}
return false;
}
MismatchingNewDeleteDetector::MismatchResult
MismatchingNewDeleteDetector::analyzeInClassInitializer() {
assert(Field != nullptr && "This should be called only for members");
const Expr *InitExpr = Field->getInClassInitializer();
if (!InitExpr)
return EndOfTU ? NoMismatch : AnalyzeLater;
if (const CXXNewExpr *NE = getNewExprFromInitListOrExpr(InitExpr)) {
if (NE->isArray() != IsArrayForm) {
NewExprs.push_back(NE);
return MemberInitMismatches;
}
}
return NoMismatch;
}
MismatchingNewDeleteDetector::MismatchResult
MismatchingNewDeleteDetector::analyzeField(FieldDecl *Field,
bool DeleteWasArrayForm) {
assert(Field != nullptr && "Analysis requires a valid class member.");
this->Field = Field;
IsArrayForm = DeleteWasArrayForm;
const CXXRecordDecl *RD = cast<const CXXRecordDecl>(Field->getParent());
for (const auto *CD : RD->ctors()) {
if (hasMatchingNewInCtor(CD))
return NoMismatch;
}
if (HasUndefinedConstructors)
return EndOfTU ? NoMismatch : AnalyzeLater;
if (!NewExprs.empty())
return MemberInitMismatches;
return Field->hasInClassInitializer() ? analyzeInClassInitializer()
: NoMismatch;
}
MismatchingNewDeleteDetector::MismatchResult
MismatchingNewDeleteDetector::analyzeMemberExpr(const MemberExpr *ME) {
assert(ME != nullptr && "Expected a member expression");
if (FieldDecl *F = dyn_cast<FieldDecl>(ME->getMemberDecl()))
return analyzeField(F, IsArrayForm);
return NoMismatch;
}
bool MismatchingNewDeleteDetector::hasMatchingVarInit(const DeclRefExpr *D) {
const CXXNewExpr *NE = nullptr;
if (const VarDecl *VD = dyn_cast<const VarDecl>(D->getDecl())) {
if (VD->hasInit() && (NE = getNewExprFromInitListOrExpr(VD->getInit())) &&
NE->isArray() != IsArrayForm) {
NewExprs.push_back(NE);
}
}
return NewExprs.empty();
}
static void
DiagnoseMismatchedNewDelete(Sema &SemaRef, SourceLocation DeleteLoc,
const MismatchingNewDeleteDetector &Detector) {
SourceLocation EndOfDelete = SemaRef.getLocForEndOfToken(DeleteLoc);
FixItHint H;
if (!Detector.IsArrayForm)
H = FixItHint::CreateInsertion(EndOfDelete, "[]");
else {
SourceLocation RSquare = Lexer::findLocationAfterToken(
DeleteLoc, tok::l_square, SemaRef.getSourceManager(),
SemaRef.getLangOpts(), true);
if (RSquare.isValid())
H = FixItHint::CreateRemoval(SourceRange(EndOfDelete, RSquare));
}
SemaRef.Diag(DeleteLoc, diag::warn_mismatched_delete_new)
<< Detector.IsArrayForm << H;
for (const auto *NE : Detector.NewExprs)
SemaRef.Diag(NE->getExprLoc(), diag::note_allocated_here)
<< Detector.IsArrayForm;
}
void Sema::AnalyzeDeleteExprMismatch(const CXXDeleteExpr *DE) {
if (Diags.isIgnored(diag::warn_mismatched_delete_new, SourceLocation()))
return;
MismatchingNewDeleteDetector Detector(/*EndOfTU=*/false);
switch (Detector.analyzeDeleteExpr(DE)) {
case MismatchingNewDeleteDetector::VarInitMismatches:
case MismatchingNewDeleteDetector::MemberInitMismatches: {
DiagnoseMismatchedNewDelete(*this, DE->getBeginLoc(), Detector);
break;
}
case MismatchingNewDeleteDetector::AnalyzeLater: {
DeleteExprs[Detector.Field].push_back(
std::make_pair(DE->getBeginLoc(), DE->isArrayForm()));
break;
}
case MismatchingNewDeleteDetector::NoMismatch:
break;
}
}
void Sema::AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation DeleteLoc,
bool DeleteWasArrayForm) {
MismatchingNewDeleteDetector Detector(/*EndOfTU=*/true);
switch (Detector.analyzeField(Field, DeleteWasArrayForm)) {
case MismatchingNewDeleteDetector::VarInitMismatches:
llvm_unreachable("This analysis should have been done for class members.");
case MismatchingNewDeleteDetector::AnalyzeLater:
llvm_unreachable("Analysis cannot be postponed any point beyond end of "
"translation unit.");
case MismatchingNewDeleteDetector::MemberInitMismatches:
DiagnoseMismatchedNewDelete(*this, DeleteLoc, Detector);
break;
case MismatchingNewDeleteDetector::NoMismatch:
break;
}
}
ExprResult
Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
bool ArrayForm, Expr *ExE) {
// C++ [expr.delete]p1:
// The operand shall have a pointer type, or a class type having a single
// non-explicit conversion function to a pointer type. The result has type
// void.
//
// DR599 amends "pointer type" to "pointer to object type" in both cases.
ExprResult Ex = ExE;
FunctionDecl *OperatorDelete = nullptr;
bool ArrayFormAsWritten = ArrayForm;
bool UsualArrayDeleteWantsSize = false;
if (!Ex.get()->isTypeDependent()) {
// Perform lvalue-to-rvalue cast, if needed.
Ex = DefaultLvalueConversion(Ex.get());
if (Ex.isInvalid())
return ExprError();
QualType Type = Ex.get()->getType();
class DeleteConverter : public ContextualImplicitConverter {
public:
DeleteConverter() : ContextualImplicitConverter(false, true) {}
bool match(QualType ConvType) override {
// FIXME: If we have an operator T* and an operator void*, we must pick
// the operator T*.
if (const PointerType *ConvPtrType = ConvType->getAs<PointerType>())
if (ConvPtrType->getPointeeType()->isIncompleteOrObjectType())
return true;
return false;
}
SemaDiagnosticBuilder diagnoseNoMatch(Sema &S, SourceLocation Loc,
QualType T) override {
return S.Diag(Loc, diag::err_delete_operand) << T;
}
SemaDiagnosticBuilder diagnoseIncomplete(Sema &S, SourceLocation Loc,
QualType T) override {
return S.Diag(Loc, diag::err_delete_incomplete_class_type) << T;
}
SemaDiagnosticBuilder diagnoseExplicitConv(Sema &S, SourceLocation Loc,
QualType T,
QualType ConvTy) override {
return S.Diag(Loc, diag::err_delete_explicit_conversion) << T << ConvTy;
}
SemaDiagnosticBuilder noteExplicitConv(Sema &S, CXXConversionDecl *Conv,
QualType ConvTy) override {
return S.Diag(Conv->getLocation(), diag::note_delete_conversion)
<< ConvTy;
}
SemaDiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc,
QualType T) override {
return S.Diag(Loc, diag::err_ambiguous_delete_operand) << T;
}
SemaDiagnosticBuilder noteAmbiguous(Sema &S, CXXConversionDecl *Conv,
QualType ConvTy) override {
return S.Diag(Conv->getLocation(), diag::note_delete_conversion)
<< ConvTy;
}
SemaDiagnosticBuilder diagnoseConversion(Sema &S, SourceLocation Loc,
QualType T,
QualType ConvTy) override {
llvm_unreachable("conversion functions are permitted");
}
} Converter;
Ex = PerformContextualImplicitConversion(StartLoc, Ex.get(), Converter);
if (Ex.isInvalid())
return ExprError();
Type = Ex.get()->getType();
if (!Converter.match(Type))
// FIXME: PerformContextualImplicitConversion should return ExprError
// itself in this case.
return ExprError();
QualType Pointee = Type->castAs<PointerType>()->getPointeeType();
QualType PointeeElem = Context.getBaseElementType(Pointee);
if (Pointee.getAddressSpace() != LangAS::Default &&
!getLangOpts().OpenCLCPlusPlus)
return Diag(Ex.get()->getBeginLoc(),
diag::err_address_space_qualified_delete)
<< Pointee.getUnqualifiedType()
<< Pointee.getQualifiers().getAddressSpaceAttributePrintValue();
CXXRecordDecl *PointeeRD = nullptr;
if (Pointee->isVoidType() && !isSFINAEContext()) {
// The C++ standard bans deleting a pointer to a non-object type, which
// effectively bans deletion of "void*". However, most compilers support
// this, so we treat it as a warning unless we're in a SFINAE context.
// But we still prohibit this since C++26.
Diag(StartLoc, LangOpts.CPlusPlus26 ? diag::err_delete_incomplete
: diag::ext_delete_void_ptr_operand)
<< (LangOpts.CPlusPlus26 ? Pointee : Type)
<< Ex.get()->getSourceRange();
} else if (Pointee->isFunctionType() || Pointee->isVoidType() ||
Pointee->isSizelessType()) {
return ExprError(Diag(StartLoc, diag::err_delete_operand)
<< Type << Ex.get()->getSourceRange());
} else if (!Pointee->isDependentType()) {
// FIXME: This can result in errors if the definition was imported from a
// module but is hidden.
if (Pointee->isEnumeralType() ||
!RequireCompleteType(StartLoc, Pointee,
LangOpts.CPlusPlus26
? diag::err_delete_incomplete
: diag::warn_delete_incomplete,
Ex.get())) {
if (const RecordType *RT = PointeeElem->getAs<RecordType>())
PointeeRD = cast<CXXRecordDecl>(RT->getDecl());
}
}
if (Pointee->isArrayType() && !ArrayForm) {
Diag(StartLoc, diag::warn_delete_array_type)
<< Type << Ex.get()->getSourceRange()
<< FixItHint::CreateInsertion(getLocForEndOfToken(StartLoc), "[]");
ArrayForm = true;
}
DeclarationName DeleteName = Context.DeclarationNames.getCXXOperatorName(
ArrayForm ? OO_Array_Delete : OO_Delete);
if (PointeeRD) {
ImplicitDeallocationParameters IDP = {
Pointee, ShouldUseTypeAwareOperatorNewOrDelete(),
AlignedAllocationMode::No, SizedDeallocationMode::No};
if (!UseGlobal &&
FindDeallocationFunction(StartLoc, PointeeRD, DeleteName,
OperatorDelete, IDP))
return ExprError();
// If we're allocating an array of records, check whether the
// usual operator delete[] has a size_t parameter.
if (ArrayForm) {
// If the user specifically asked to use the global allocator,
// we'll need to do the lookup into the class.
if (UseGlobal)
UsualArrayDeleteWantsSize = doesUsualArrayDeleteWantSize(
*this, StartLoc, IDP.PassTypeIdentity, PointeeElem);
// Otherwise, the usual operator delete[] should be the
// function we just found.
else if (isa_and_nonnull<CXXMethodDecl>(OperatorDelete)) {
UsualDeallocFnInfo UDFI(
*this, DeclAccessPair::make(OperatorDelete, AS_public), Pointee,
StartLoc);
UsualArrayDeleteWantsSize = isSizedDeallocation(UDFI.IDP.PassSize);
}
}
if (!PointeeRD->hasIrrelevantDestructor()) {
if (CXXDestructorDecl *Dtor = LookupDestructor(PointeeRD)) {
if (Dtor->isCalledByDelete(OperatorDelete)) {
MarkFunctionReferenced(StartLoc,
const_cast<CXXDestructorDecl *>(Dtor));
if (DiagnoseUseOfDecl(Dtor, StartLoc))
return ExprError();
}
}
}
CheckVirtualDtorCall(PointeeRD->getDestructor(), StartLoc,
/*IsDelete=*/true, /*CallCanBeVirtual=*/true,
/*WarnOnNonAbstractTypes=*/!ArrayForm,
SourceLocation());
}
if (!OperatorDelete) {
if (getLangOpts().OpenCLCPlusPlus) {
Diag(StartLoc, diag::err_openclcxx_not_supported) << "default delete";
return ExprError();
}
bool IsComplete = isCompleteType(StartLoc, Pointee);
bool CanProvideSize =
IsComplete && (!ArrayForm || UsualArrayDeleteWantsSize ||
Pointee.isDestructedType());
bool Overaligned = hasNewExtendedAlignment(*this, Pointee);
// Look for a global declaration.
ImplicitDeallocationParameters IDP = {
Pointee, ShouldUseTypeAwareOperatorNewOrDelete(),
alignedAllocationModeFromBool(Overaligned),
sizedDeallocationModeFromBool(CanProvideSize)};
OperatorDelete = FindUsualDeallocationFunction(StartLoc, IDP, DeleteName);
if (!OperatorDelete)
return ExprError();
}
if (OperatorDelete->isInvalidDecl())
return ExprError();
MarkFunctionReferenced(StartLoc, OperatorDelete);
// Check access and ambiguity of destructor if we're going to call it.
// Note that this is required even for a virtual delete.
bool IsVirtualDelete = false;
if (PointeeRD) {
if (CXXDestructorDecl *Dtor = LookupDestructor(PointeeRD)) {
if (Dtor->isCalledByDelete(OperatorDelete))
CheckDestructorAccess(Ex.get()->getExprLoc(), Dtor,
PDiag(diag::err_access_dtor) << PointeeElem);
IsVirtualDelete = Dtor->isVirtual();
}
}
DiagnoseUseOfDecl(OperatorDelete, StartLoc);
unsigned AddressParamIdx = 0;
if (OperatorDelete->isTypeAwareOperatorNewOrDelete()) {
QualType TypeIdentity = OperatorDelete->getParamDecl(0)->getType();
if (RequireCompleteType(StartLoc, TypeIdentity,
diag::err_incomplete_type))
return ExprError();
AddressParamIdx = 1;
}
// Convert the operand to the type of the first parameter of operator
// delete. This is only necessary if we selected a destroying operator
// delete that we are going to call (non-virtually); converting to void*
// is trivial and left to AST consumers to handle.
QualType ParamType =
OperatorDelete->getParamDecl(AddressParamIdx)->getType();
if (!IsVirtualDelete && !ParamType->getPointeeType()->isVoidType()) {
Qualifiers Qs = Pointee.getQualifiers();
if (Qs.hasCVRQualifiers()) {
// Qualifiers are irrelevant to this conversion; we're only looking
// for access and ambiguity.
Qs.removeCVRQualifiers();
QualType Unqual = Context.getPointerType(
Context.getQualifiedType(Pointee.getUnqualifiedType(), Qs));
Ex = ImpCastExprToType(Ex.get(), Unqual, CK_NoOp);
}
Ex = PerformImplicitConversion(Ex.get(), ParamType,
AssignmentAction::Passing);
if (Ex.isInvalid())
return ExprError();
}
}
CXXDeleteExpr *Result = new (Context) CXXDeleteExpr(
Context.VoidTy, UseGlobal, ArrayForm, ArrayFormAsWritten,
UsualArrayDeleteWantsSize, OperatorDelete, Ex.get(), StartLoc);
AnalyzeDeleteExprMismatch(Result);
return Result;
}
static bool resolveBuiltinNewDeleteOverload(Sema &S, CallExpr *TheCall,
bool IsDelete,
FunctionDecl *&Operator) {
DeclarationName NewName = S.Context.DeclarationNames.getCXXOperatorName(
IsDelete ? OO_Delete : OO_New);
LookupResult R(S, NewName, TheCall->getBeginLoc(), Sema::LookupOrdinaryName);
S.LookupQualifiedName(R, S.Context.getTranslationUnitDecl());
assert(!R.empty() && "implicitly declared allocation functions not found");
assert(!R.isAmbiguous() && "global allocation functions are ambiguous");
// We do our own custom access checks below.
R.suppressDiagnostics();
SmallVector<Expr *, 8> Args(TheCall->arguments());
OverloadCandidateSet Candidates(R.getNameLoc(),
OverloadCandidateSet::CSK_Normal);
for (LookupResult::iterator FnOvl = R.begin(), FnOvlEnd = R.end();
FnOvl != FnOvlEnd; ++FnOvl) {
// Even member operator new/delete are implicitly treated as
// static, so don't use AddMemberCandidate.
NamedDecl *D = (*FnOvl)->getUnderlyingDecl();
if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) {
S.AddTemplateOverloadCandidate(FnTemplate, FnOvl.getPair(),
/*ExplicitTemplateArgs=*/nullptr, Args,
Candidates,
/*SuppressUserConversions=*/false);
continue;
}
FunctionDecl *Fn = cast<FunctionDecl>(D);
S.AddOverloadCandidate(Fn, FnOvl.getPair(), Args, Candidates,
/*SuppressUserConversions=*/false);
}
SourceRange Range = TheCall->getSourceRange();
// Do the resolution.
OverloadCandidateSet::iterator Best;
switch (Candidates.BestViableFunction(S, R.getNameLoc(), Best)) {
case OR_Success: {
// Got one!
FunctionDecl *FnDecl = Best->Function;
assert(R.getNamingClass() == nullptr &&
"class members should not be considered");
if (!FnDecl->isReplaceableGlobalAllocationFunction()) {
S.Diag(R.getNameLoc(), diag::err_builtin_operator_new_delete_not_usual)
<< (IsDelete ? 1 : 0) << Range;
S.Diag(FnDecl->getLocation(), diag::note_non_usual_function_declared_here)
<< R.getLookupName() << FnDecl->getSourceRange();
return true;
}
Operator = FnDecl;
return false;
}
case OR_No_Viable_Function:
Candidates.NoteCandidates(
PartialDiagnosticAt(R.getNameLoc(),
S.PDiag(diag::err_ovl_no_viable_function_in_call)
<< R.getLookupName() << Range),
S, OCD_AllCandidates, Args);
return true;
case OR_Ambiguous:
Candidates.NoteCandidates(
PartialDiagnosticAt(R.getNameLoc(),
S.PDiag(diag::err_ovl_ambiguous_call)
<< R.getLookupName() << Range),
S, OCD_AmbiguousCandidates, Args);
return true;
case OR_Deleted:
S.DiagnoseUseOfDeletedFunction(R.getNameLoc(), Range, R.getLookupName(),
Candidates, Best->Function, Args);
return true;
}
llvm_unreachable("Unreachable, bad result from BestViableFunction");
}
ExprResult Sema::BuiltinOperatorNewDeleteOverloaded(ExprResult TheCallResult,
bool IsDelete) {
CallExpr *TheCall = cast<CallExpr>(TheCallResult.get());
if (!getLangOpts().CPlusPlus) {
Diag(TheCall->getExprLoc(), diag::err_builtin_requires_language)
<< (IsDelete ? "__builtin_operator_delete" : "__builtin_operator_new")
<< "C++";
return ExprError();
}
// CodeGen assumes it can find the global new and delete to call,
// so ensure that they are declared.
DeclareGlobalNewDelete();
FunctionDecl *OperatorNewOrDelete = nullptr;
if (resolveBuiltinNewDeleteOverload(*this, TheCall, IsDelete,
OperatorNewOrDelete))
return ExprError();
assert(OperatorNewOrDelete && "should be found");
DiagnoseUseOfDecl(OperatorNewOrDelete, TheCall->getExprLoc());
MarkFunctionReferenced(TheCall->getExprLoc(), OperatorNewOrDelete);
TheCall->setType(OperatorNewOrDelete->getReturnType());
for (unsigned i = 0; i != TheCall->getNumArgs(); ++i) {
QualType ParamTy = OperatorNewOrDelete->getParamDecl(i)->getType();
InitializedEntity Entity =
InitializedEntity::InitializeParameter(Context, ParamTy, false);
ExprResult Arg = PerformCopyInitialization(
Entity, TheCall->getArg(i)->getBeginLoc(), TheCall->getArg(i));
if (Arg.isInvalid())
return ExprError();
TheCall->setArg(i, Arg.get());
}
auto Callee = dyn_cast<ImplicitCastExpr>(TheCall->getCallee());
assert(Callee && Callee->getCastKind() == CK_BuiltinFnToFnPtr &&
"Callee expected to be implicit cast to a builtin function pointer");
Callee->setType(OperatorNewOrDelete->getType());
return TheCallResult;
}
void Sema::CheckVirtualDtorCall(CXXDestructorDecl *dtor, SourceLocation Loc,
bool IsDelete, bool CallCanBeVirtual,
bool WarnOnNonAbstractTypes,
SourceLocation DtorLoc) {
if (!dtor || dtor->isVirtual() || !CallCanBeVirtual || isUnevaluatedContext())
return;
// C++ [expr.delete]p3:
// In the first alternative (delete object), if the static type of the
// object to be deleted is different from its dynamic type, the static
// type shall be a base class of the dynamic type of the object to be
// deleted and the static type shall have a virtual destructor or the
// behavior is undefined.
//
const CXXRecordDecl *PointeeRD = dtor->getParent();
// Note: a final class cannot be derived from, no issue there
if (!PointeeRD->isPolymorphic() || PointeeRD->hasAttr<FinalAttr>())
return;
// If the superclass is in a system header, there's nothing that can be done.
// The `delete` (where we emit the warning) can be in a system header,
// what matters for this warning is where the deleted type is defined.
if (getSourceManager().isInSystemHeader(PointeeRD->getLocation()))
return;
QualType ClassType = dtor->getFunctionObjectParameterType();
if (PointeeRD->isAbstract()) {
// If the class is abstract, we warn by default, because we're
// sure the code has undefined behavior.
Diag(Loc, diag::warn_delete_abstract_non_virtual_dtor) << (IsDelete ? 0 : 1)
<< ClassType;
} else if (WarnOnNonAbstractTypes) {
// Otherwise, if this is not an array delete, it's a bit suspect,
// but not necessarily wrong.
Diag(Loc, diag::warn_delete_non_virtual_dtor) << (IsDelete ? 0 : 1)
<< ClassType;
}
if (!IsDelete) {
std::string TypeStr;
ClassType.getAsStringInternal(TypeStr, getPrintingPolicy());
Diag(DtorLoc, diag::note_delete_non_virtual)
<< FixItHint::CreateInsertion(DtorLoc, TypeStr + "::");
}
}
Sema::ConditionResult Sema::ActOnConditionVariable(Decl *ConditionVar,
SourceLocation StmtLoc,
ConditionKind CK) {
ExprResult E =
CheckConditionVariable(cast<VarDecl>(ConditionVar), StmtLoc, CK);
if (E.isInvalid())
return ConditionError();
return ConditionResult(*this, ConditionVar, MakeFullExpr(E.get(), StmtLoc),
CK == ConditionKind::ConstexprIf);
}
ExprResult Sema::CheckConditionVariable(VarDecl *ConditionVar,
SourceLocation StmtLoc,
ConditionKind CK) {
if (ConditionVar->isInvalidDecl())
return ExprError();
QualType T = ConditionVar->getType();
// C++ [stmt.select]p2:
// The declarator shall not specify a function or an array.
if (T->isFunctionType())
return ExprError(Diag(ConditionVar->getLocation(),
diag::err_invalid_use_of_function_type)
<< ConditionVar->getSourceRange());
else if (T->isArrayType())
return ExprError(Diag(ConditionVar->getLocation(),
diag::err_invalid_use_of_array_type)
<< ConditionVar->getSourceRange());
ExprResult Condition = BuildDeclRefExpr(
ConditionVar, ConditionVar->getType().getNonReferenceType(), VK_LValue,
ConditionVar->getLocation());
switch (CK) {
case ConditionKind::Boolean:
return CheckBooleanCondition(StmtLoc, Condition.get());
case ConditionKind::ConstexprIf:
return CheckBooleanCondition(StmtLoc, Condition.get(), true);
case ConditionKind::Switch:
return CheckSwitchCondition(StmtLoc, Condition.get());
}
llvm_unreachable("unexpected condition kind");
}
ExprResult Sema::CheckCXXBooleanCondition(Expr *CondExpr, bool IsConstexpr) {
// C++11 6.4p4:
// The value of a condition that is an initialized declaration in a statement
// other than a switch statement is the value of the declared variable
// implicitly converted to type bool. If that conversion is ill-formed, the
// program is ill-formed.
// The value of a condition that is an expression is the value of the
// expression, implicitly converted to bool.
//
// C++23 8.5.2p2
// If the if statement is of the form if constexpr, the value of the condition
// is contextually converted to bool and the converted expression shall be
// a constant expression.
//
ExprResult E = PerformContextuallyConvertToBool(CondExpr);
if (!IsConstexpr || E.isInvalid() || E.get()->isValueDependent())
return E;
// FIXME: Return this value to the caller so they don't need to recompute it.
llvm::APSInt Cond;
E = VerifyIntegerConstantExpression(
E.get(), &Cond,
diag::err_constexpr_if_condition_expression_is_not_constant);
return E;
}
bool
Sema::IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType) {
// Look inside the implicit cast, if it exists.
if (ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(From))
From = Cast->getSubExpr();
// A string literal (2.13.4) that is not a wide string literal can
// be converted to an rvalue of type "pointer to char"; a wide
// string literal can be converted to an rvalue of type "pointer
// to wchar_t" (C++ 4.2p2).
if (StringLiteral *StrLit = dyn_cast<StringLiteral>(From->IgnoreParens()))
if (const PointerType *ToPtrType = ToType->getAs<PointerType>())
if (const BuiltinType *ToPointeeType
= ToPtrType->getPointeeType()->getAs<BuiltinType>()) {
// This conversion is considered only when there is an
// explicit appropriate pointer target type (C++ 4.2p2).
if (!ToPtrType->getPointeeType().hasQualifiers()) {
switch (StrLit->getKind()) {
case StringLiteralKind::UTF8:
case StringLiteralKind::UTF16:
case StringLiteralKind::UTF32:
// We don't allow UTF literals to be implicitly converted
break;
case StringLiteralKind::Ordinary:
case StringLiteralKind::Binary:
return (ToPointeeType->getKind() == BuiltinType::Char_U ||
ToPointeeType->getKind() == BuiltinType::Char_S);
case StringLiteralKind::Wide:
return Context.typesAreCompatible(Context.getWideCharType(),
QualType(ToPointeeType, 0));
case StringLiteralKind::Unevaluated:
assert(false && "Unevaluated string literal in expression");
break;
}
}
}
return false;
}
static ExprResult BuildCXXCastArgument(Sema &S,
SourceLocation CastLoc,
QualType Ty,
CastKind Kind,
CXXMethodDecl *Method,
DeclAccessPair FoundDecl,
bool HadMultipleCandidates,
Expr *From) {
switch (Kind) {
default: llvm_unreachable("Unhandled cast kind!");
case CK_ConstructorConversion: {
CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(Method);
SmallVector<Expr*, 8> ConstructorArgs;
if (S.RequireNonAbstractType(CastLoc, Ty,
diag::err_allocation_of_abstract_type))
return ExprError();
if (S.CompleteConstructorCall(Constructor, Ty, From, CastLoc,
ConstructorArgs))
return ExprError();
S.CheckConstructorAccess(CastLoc, Constructor, FoundDecl,
InitializedEntity::InitializeTemporary(Ty));
if (S.DiagnoseUseOfDecl(Method, CastLoc))
return ExprError();
ExprResult Result = S.BuildCXXConstructExpr(
CastLoc, Ty, FoundDecl, cast<CXXConstructorDecl>(Method),
ConstructorArgs, HadMultipleCandidates,
/*ListInit*/ false, /*StdInitListInit*/ false, /*ZeroInit*/ false,
CXXConstructionKind::Complete, SourceRange());
if (Result.isInvalid())
return ExprError();
return S.MaybeBindToTemporary(Result.getAs<Expr>());
}
case CK_UserDefinedConversion: {
assert(!From->getType()->isPointerType() && "Arg can't have pointer type!");
S.CheckMemberOperatorAccess(CastLoc, From, /*arg*/ nullptr, FoundDecl);
if (S.DiagnoseUseOfDecl(Method, CastLoc))
return ExprError();
// Create an implicit call expr that calls it.
CXXConversionDecl *Conv = cast<CXXConversionDecl>(Method);
ExprResult Result = S.BuildCXXMemberCallExpr(From, FoundDecl, Conv,
HadMultipleCandidates);
if (Result.isInvalid())
return ExprError();
// Record usage of conversion in an implicit cast.
Result = ImplicitCastExpr::Create(S.Context, Result.get()->getType(),
CK_UserDefinedConversion, Result.get(),
nullptr, Result.get()->getValueKind(),
S.CurFPFeatureOverrides());
return S.MaybeBindToTemporary(Result.get());
}
}
}
ExprResult
Sema::PerformImplicitConversion(Expr *From, QualType ToType,
const ImplicitConversionSequence &ICS,
AssignmentAction Action,
CheckedConversionKind CCK) {
// C++ [over.match.oper]p7: [...] operands of class type are converted [...]
if (CCK == CheckedConversionKind::ForBuiltinOverloadedOp &&
!From->getType()->isRecordType())
return From;
switch (ICS.getKind()) {
case ImplicitConversionSequence::StandardConversion: {
ExprResult Res = PerformImplicitConversion(From, ToType, ICS.Standard,
Action, CCK);
if (Res.isInvalid())
return ExprError();
From = Res.get();
break;
}
case ImplicitConversionSequence::UserDefinedConversion: {
FunctionDecl *FD = ICS.UserDefined.ConversionFunction;
CastKind CastKind;
QualType BeforeToType;
assert(FD && "no conversion function for user-defined conversion seq");
if (const CXXConversionDecl *Conv = dyn_cast<CXXConversionDecl>(FD)) {
CastKind = CK_UserDefinedConversion;
// If the user-defined conversion is specified by a conversion function,
// the initial standard conversion sequence converts the source type to
// the implicit object parameter of the conversion function.
BeforeToType = Context.getTagDeclType(Conv->getParent());
} else {
const CXXConstructorDecl *Ctor = cast<CXXConstructorDecl>(FD);
CastKind = CK_ConstructorConversion;
// Do no conversion if dealing with ... for the first conversion.
if (!ICS.UserDefined.EllipsisConversion) {
// If the user-defined conversion is specified by a constructor, the
// initial standard conversion sequence converts the source type to
// the type required by the argument of the constructor
BeforeToType = Ctor->getParamDecl(0)->getType().getNonReferenceType();
}
}
// Watch out for ellipsis conversion.
if (!ICS.UserDefined.EllipsisConversion) {
ExprResult Res = PerformImplicitConversion(
From, BeforeToType, ICS.UserDefined.Before,
AssignmentAction::Converting, CCK);
if (Res.isInvalid())
return ExprError();
From = Res.get();
}
ExprResult CastArg = BuildCXXCastArgument(
*this, From->getBeginLoc(), ToType.getNonReferenceType(), CastKind,
cast<CXXMethodDecl>(FD), ICS.UserDefined.FoundConversionFunction,
ICS.UserDefined.HadMultipleCandidates, From);
if (CastArg.isInvalid())
return ExprError();
From = CastArg.get();
// C++ [over.match.oper]p7:
// [...] the second standard conversion sequence of a user-defined
// conversion sequence is not applied.
if (CCK == CheckedConversionKind::ForBuiltinOverloadedOp)
return From;
return PerformImplicitConversion(From, ToType, ICS.UserDefined.After,
AssignmentAction::Converting, CCK);
}
case ImplicitConversionSequence::AmbiguousConversion:
ICS.DiagnoseAmbiguousConversion(*this, From->getExprLoc(),
PDiag(diag::err_typecheck_ambiguous_condition)
<< From->getSourceRange());
return ExprError();
case ImplicitConversionSequence::EllipsisConversion:
case ImplicitConversionSequence::StaticObjectArgumentConversion:
llvm_unreachable("bad conversion");
case ImplicitConversionSequence::BadConversion:
Sema::AssignConvertType ConvTy =
CheckAssignmentConstraints(From->getExprLoc(), ToType, From->getType());
bool Diagnosed = DiagnoseAssignmentResult(
ConvTy == Compatible ? Incompatible : ConvTy, From->getExprLoc(),
ToType, From->getType(), From, Action);
assert(Diagnosed && "failed to diagnose bad conversion"); (void)Diagnosed;
return ExprError();
}
// Everything went well.
return From;
}
// adjustVectorType - Compute the intermediate cast type casting elements of the
// from type to the elements of the to type without resizing the vector.
static QualType adjustVectorType(ASTContext &Context, QualType FromTy,
QualType ToType, QualType *ElTy = nullptr) {
QualType ElType = ToType;
if (auto *ToVec = ToType->getAs<VectorType>())
ElType = ToVec->getElementType();
if (ElTy)
*ElTy = ElType;
if (!FromTy->isVectorType())
return ElType;
auto *FromVec = FromTy->castAs<VectorType>();
return Context.getExtVectorType(ElType, FromVec->getNumElements());
}
ExprResult
Sema::PerformImplicitConversion(Expr *From, QualType ToType,
const StandardConversionSequence& SCS,
AssignmentAction Action,
CheckedConversionKind CCK) {
bool CStyle = (CCK == CheckedConversionKind::CStyleCast ||
CCK == CheckedConversionKind::FunctionalCast);
// Overall FIXME: we are recomputing too many types here and doing far too
// much extra work. What this means is that we need to keep track of more
// information that is computed when we try the implicit conversion initially,
// so that we don't need to recompute anything here.
QualType FromType = From->getType();
if (SCS.CopyConstructor) {
// FIXME: When can ToType be a reference type?
assert(!ToType->isReferenceType());
if (SCS.Second == ICK_Derived_To_Base) {
SmallVector<Expr*, 8> ConstructorArgs;
if (CompleteConstructorCall(
cast<CXXConstructorDecl>(SCS.CopyConstructor), ToType, From,
/*FIXME:ConstructLoc*/ SourceLocation(), ConstructorArgs))
return ExprError();
return BuildCXXConstructExpr(
/*FIXME:ConstructLoc*/ SourceLocation(), ToType,
SCS.FoundCopyConstructor, SCS.CopyConstructor, ConstructorArgs,
/*HadMultipleCandidates*/ false,
/*ListInit*/ false, /*StdInitListInit*/ false, /*ZeroInit*/ false,
CXXConstructionKind::Complete, SourceRange());
}
return BuildCXXConstructExpr(
/*FIXME:ConstructLoc*/ SourceLocation(), ToType,
SCS.FoundCopyConstructor, SCS.CopyConstructor, From,
/*HadMultipleCandidates*/ false,
/*ListInit*/ false, /*StdInitListInit*/ false, /*ZeroInit*/ false,
CXXConstructionKind::Complete, SourceRange());
}
// Resolve overloaded function references.
if (Context.hasSameType(FromType, Context.OverloadTy)) {
DeclAccessPair Found;
FunctionDecl *Fn = ResolveAddressOfOverloadedFunction(From, ToType,
true, Found);
if (!Fn)
return ExprError();
if (DiagnoseUseOfDecl(Fn, From->getBeginLoc()))
return ExprError();
ExprResult Res = FixOverloadedFunctionReference(From, Found, Fn);
if (Res.isInvalid())
return ExprError();
// We might get back another placeholder expression if we resolved to a
// builtin.
Res = CheckPlaceholderExpr(Res.get());
if (Res.isInvalid())
return ExprError();
From = Res.get();
FromType = From->getType();
}
// If we're converting to an atomic type, first convert to the corresponding
// non-atomic type.
QualType ToAtomicType;
if (const AtomicType *ToAtomic = ToType->getAs<AtomicType>()) {
ToAtomicType = ToType;
ToType = ToAtomic->getValueType();
}
QualType InitialFromType = FromType;
// Perform the first implicit conversion.
switch (SCS.First) {
case ICK_Identity:
if (const AtomicType *FromAtomic = FromType->getAs<AtomicType>()) {
FromType = FromAtomic->getValueType().getUnqualifiedType();
From = ImplicitCastExpr::Create(Context, FromType, CK_AtomicToNonAtomic,
From, /*BasePath=*/nullptr, VK_PRValue,
FPOptionsOverride());
}
break;
case ICK_Lvalue_To_Rvalue: {
assert(From->getObjectKind() != OK_ObjCProperty);
ExprResult FromRes = DefaultLvalueConversion(From);
if (FromRes.isInvalid())
return ExprError();
From = FromRes.get();
FromType = From->getType();
break;
}
case ICK_Array_To_Pointer:
FromType = Context.getArrayDecayedType(FromType);
From = ImpCastExprToType(From, FromType, CK_ArrayToPointerDecay, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
break;
case ICK_HLSL_Array_RValue:
if (ToType->isArrayParameterType()) {
FromType = Context.getArrayParameterType(FromType);
From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
} else { // FromType must be ArrayParameterType
assert(FromType->isArrayParameterType() &&
"FromType must be ArrayParameterType in ICK_HLSL_Array_RValue \
if it is not ToType");
const ArrayParameterType *APT = cast<ArrayParameterType>(FromType);
FromType = APT->getConstantArrayType(Context);
From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
}
break;
case ICK_Function_To_Pointer:
FromType = Context.getPointerType(FromType);
From = ImpCastExprToType(From, FromType, CK_FunctionToPointerDecay,
VK_PRValue, /*BasePath=*/nullptr, CCK)
.get();
break;
default:
llvm_unreachable("Improper first standard conversion");
}
// Perform the second implicit conversion
switch (SCS.Second) {
case ICK_Identity:
// C++ [except.spec]p5:
// [For] assignment to and initialization of pointers to functions,
// pointers to member functions, and references to functions: the
// target entity shall allow at least the exceptions allowed by the
// source value in the assignment or initialization.
switch (Action) {
case AssignmentAction::Assigning:
case AssignmentAction::Initializing:
// Note, function argument passing and returning are initialization.
case AssignmentAction::Passing:
case AssignmentAction::Returning:
case AssignmentAction::Sending:
case AssignmentAction::Passing_CFAudited:
if (CheckExceptionSpecCompatibility(From, ToType))
return ExprError();
break;
case AssignmentAction::Casting:
case AssignmentAction::Converting:
// Casts and implicit conversions are not initialization, so are not
// checked for exception specification mismatches.
break;
}
// Nothing else to do.
break;
case ICK_Integral_Promotion:
case ICK_Integral_Conversion: {
QualType ElTy = ToType;
QualType StepTy = ToType;
if (FromType->isVectorType() || ToType->isVectorType())
StepTy = adjustVectorType(Context, FromType, ToType, &ElTy);
if (ElTy->isBooleanType()) {
assert(FromType->castAs<EnumType>()->getDecl()->isFixed() &&
SCS.Second == ICK_Integral_Promotion &&
"only enums with fixed underlying type can promote to bool");
From = ImpCastExprToType(From, StepTy, CK_IntegralToBoolean, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
} else {
From = ImpCastExprToType(From, StepTy, CK_IntegralCast, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
}
break;
}
case ICK_Floating_Promotion:
case ICK_Floating_Conversion: {
QualType StepTy = ToType;
if (FromType->isVectorType() || ToType->isVectorType())
StepTy = adjustVectorType(Context, FromType, ToType);
From = ImpCastExprToType(From, StepTy, CK_FloatingCast, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
break;
}
case ICK_Complex_Promotion:
case ICK_Complex_Conversion: {
QualType FromEl = From->getType()->castAs<ComplexType>()->getElementType();
QualType ToEl = ToType->castAs<ComplexType>()->getElementType();
CastKind CK;
if (FromEl->isRealFloatingType()) {
if (ToEl->isRealFloatingType())
CK = CK_FloatingComplexCast;
else
CK = CK_FloatingComplexToIntegralComplex;
} else if (ToEl->isRealFloatingType()) {
CK = CK_IntegralComplexToFloatingComplex;
} else {
CK = CK_IntegralComplexCast;
}
From = ImpCastExprToType(From, ToType, CK, VK_PRValue, /*BasePath=*/nullptr,
CCK)
.get();
break;
}
case ICK_Floating_Integral: {
QualType ElTy = ToType;
QualType StepTy = ToType;
if (FromType->isVectorType() || ToType->isVectorType())
StepTy = adjustVectorType(Context, FromType, ToType, &ElTy);
if (ElTy->isRealFloatingType())
From = ImpCastExprToType(From, StepTy, CK_IntegralToFloating, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
else
From = ImpCastExprToType(From, StepTy, CK_FloatingToIntegral, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
break;
}
case ICK_Fixed_Point_Conversion:
assert((FromType->isFixedPointType() || ToType->isFixedPointType()) &&
"Attempting implicit fixed point conversion without a fixed "
"point operand");
if (FromType->isFloatingType())
From = ImpCastExprToType(From, ToType, CK_FloatingToFixedPoint,
VK_PRValue,
/*BasePath=*/nullptr, CCK).get();
else if (ToType->isFloatingType())
From = ImpCastExprToType(From, ToType, CK_FixedPointToFloating,
VK_PRValue,
/*BasePath=*/nullptr, CCK).get();
else if (FromType->isIntegralType(Context))
From = ImpCastExprToType(From, ToType, CK_IntegralToFixedPoint,
VK_PRValue,
/*BasePath=*/nullptr, CCK).get();
else if (ToType->isIntegralType(Context))
From = ImpCastExprToType(From, ToType, CK_FixedPointToIntegral,
VK_PRValue,
/*BasePath=*/nullptr, CCK).get();
else if (ToType->isBooleanType())
From = ImpCastExprToType(From, ToType, CK_FixedPointToBoolean,
VK_PRValue,
/*BasePath=*/nullptr, CCK).get();
else
From = ImpCastExprToType(From, ToType, CK_FixedPointCast,
VK_PRValue,
/*BasePath=*/nullptr, CCK).get();
break;
case ICK_Compatible_Conversion:
From = ImpCastExprToType(From, ToType, CK_NoOp, From->getValueKind(),
/*BasePath=*/nullptr, CCK).get();
break;
case ICK_Writeback_Conversion:
case ICK_Pointer_Conversion: {
if (SCS.IncompatibleObjC && Action != AssignmentAction::Casting) {
// Diagnose incompatible Objective-C conversions
if (Action == AssignmentAction::Initializing ||
Action == AssignmentAction::Assigning)
Diag(From->getBeginLoc(),
diag::ext_typecheck_convert_incompatible_pointer)
<< ToType << From->getType() << Action << From->getSourceRange()
<< 0;
else
Diag(From->getBeginLoc(),
diag::ext_typecheck_convert_incompatible_pointer)
<< From->getType() << ToType << Action << From->getSourceRange()
<< 0;
if (From->getType()->isObjCObjectPointerType() &&
ToType->isObjCObjectPointerType())
ObjC().EmitRelatedResultTypeNote(From);
} else if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers() &&
!ObjC().CheckObjCARCUnavailableWeakConversion(ToType,
From->getType())) {
if (Action == AssignmentAction::Initializing)
Diag(From->getBeginLoc(), diag::err_arc_weak_unavailable_assign);
else
Diag(From->getBeginLoc(), diag::err_arc_convesion_of_weak_unavailable)
<< (Action == AssignmentAction::Casting) << From->getType()
<< ToType << From->getSourceRange();
}
// Defer address space conversion to the third conversion.
QualType FromPteeType = From->getType()->getPointeeType();
QualType ToPteeType = ToType->getPointeeType();
QualType NewToType = ToType;
if (!FromPteeType.isNull() && !ToPteeType.isNull() &&
FromPteeType.getAddressSpace() != ToPteeType.getAddressSpace()) {
NewToType = Context.removeAddrSpaceQualType(ToPteeType);
NewToType = Context.getAddrSpaceQualType(NewToType,
FromPteeType.getAddressSpace());
if (ToType->isObjCObjectPointerType())
NewToType = Context.getObjCObjectPointerType(NewToType);
else if (ToType->isBlockPointerType())
NewToType = Context.getBlockPointerType(NewToType);
else
NewToType = Context.getPointerType(NewToType);
}
CastKind Kind;
CXXCastPath BasePath;
if (CheckPointerConversion(From, NewToType, Kind, BasePath, CStyle))
return ExprError();
// Make sure we extend blocks if necessary.
// FIXME: doing this here is really ugly.
if (Kind == CK_BlockPointerToObjCPointerCast) {
ExprResult E = From;
(void)ObjC().PrepareCastToObjCObjectPointer(E);
From = E.get();
}
if (getLangOpts().allowsNonTrivialObjCLifetimeQualifiers())
ObjC().CheckObjCConversion(SourceRange(), NewToType, From, CCK);
From = ImpCastExprToType(From, NewToType, Kind, VK_PRValue, &BasePath, CCK)
.get();
break;
}
case ICK_Pointer_Member: {
CastKind Kind;
CXXCastPath BasePath;
switch (CheckMemberPointerConversion(
From->getType(), ToType->castAs<MemberPointerType>(), Kind, BasePath,
From->getExprLoc(), From->getSourceRange(), CStyle,
MemberPointerConversionDirection::Downcast)) {
case MemberPointerConversionResult::Success:
assert((Kind != CK_NullToMemberPointer ||
From->isNullPointerConstant(Context,
Expr::NPC_ValueDependentIsNull)) &&
"Expr must be null pointer constant!");
break;
case MemberPointerConversionResult::Inaccessible:
break;
case MemberPointerConversionResult::DifferentPointee:
llvm_unreachable("unexpected result");
case MemberPointerConversionResult::NotDerived:
llvm_unreachable("Should not have been called if derivation isn't OK.");
case MemberPointerConversionResult::Ambiguous:
case MemberPointerConversionResult::Virtual:
return ExprError();
}
if (CheckExceptionSpecCompatibility(From, ToType))
return ExprError();
From =
ImpCastExprToType(From, ToType, Kind, VK_PRValue, &BasePath, CCK).get();
break;
}
case ICK_Boolean_Conversion: {
// Perform half-to-boolean conversion via float.
if (From->getType()->isHalfType()) {
From = ImpCastExprToType(From, Context.FloatTy, CK_FloatingCast).get();
FromType = Context.FloatTy;
}
QualType ElTy = FromType;
QualType StepTy = ToType;
if (FromType->isVectorType())
ElTy = FromType->castAs<VectorType>()->getElementType();
if (getLangOpts().HLSL &&
(FromType->isVectorType() || ToType->isVectorType()))
StepTy = adjustVectorType(Context, FromType, ToType);
From = ImpCastExprToType(From, StepTy, ScalarTypeToBooleanCastKind(ElTy),
VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
break;
}
case ICK_Derived_To_Base: {
CXXCastPath BasePath;
if (CheckDerivedToBaseConversion(
From->getType(), ToType.getNonReferenceType(), From->getBeginLoc(),
From->getSourceRange(), &BasePath, CStyle))
return ExprError();
From = ImpCastExprToType(From, ToType.getNonReferenceType(),
CK_DerivedToBase, From->getValueKind(),
&BasePath, CCK).get();
break;
}
case ICK_Vector_Conversion:
From = ImpCastExprToType(From, ToType, CK_BitCast, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
break;
case ICK_SVE_Vector_Conversion:
case ICK_RVV_Vector_Conversion:
From = ImpCastExprToType(From, ToType, CK_BitCast, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
break;
case ICK_Vector_Splat: {
// Vector splat from any arithmetic type to a vector.
Expr *Elem = prepareVectorSplat(ToType, From).get();
From = ImpCastExprToType(Elem, ToType, CK_VectorSplat, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
break;
}
case ICK_Complex_Real:
// Case 1. x -> _Complex y
if (const ComplexType *ToComplex = ToType->getAs<ComplexType>()) {
QualType ElType = ToComplex->getElementType();
bool isFloatingComplex = ElType->isRealFloatingType();
// x -> y
if (Context.hasSameUnqualifiedType(ElType, From->getType())) {
// do nothing
} else if (From->getType()->isRealFloatingType()) {
From = ImpCastExprToType(From, ElType,
isFloatingComplex ? CK_FloatingCast : CK_FloatingToIntegral).get();
} else {
assert(From->getType()->isIntegerType());
From = ImpCastExprToType(From, ElType,
isFloatingComplex ? CK_IntegralToFloating : CK_IntegralCast).get();
}
// y -> _Complex y
From = ImpCastExprToType(From, ToType,
isFloatingComplex ? CK_FloatingRealToComplex
: CK_IntegralRealToComplex).get();
// Case 2. _Complex x -> y
} else {
auto *FromComplex = From->getType()->castAs<ComplexType>();
QualType ElType = FromComplex->getElementType();
bool isFloatingComplex = ElType->isRealFloatingType();
// _Complex x -> x
From = ImpCastExprToType(From, ElType,
isFloatingComplex ? CK_FloatingComplexToReal
: CK_IntegralComplexToReal,
VK_PRValue, /*BasePath=*/nullptr, CCK)
.get();
// x -> y
if (Context.hasSameUnqualifiedType(ElType, ToType)) {
// do nothing
} else if (ToType->isRealFloatingType()) {
From = ImpCastExprToType(From, ToType,
isFloatingComplex ? CK_FloatingCast
: CK_IntegralToFloating,
VK_PRValue, /*BasePath=*/nullptr, CCK)
.get();
} else {
assert(ToType->isIntegerType());
From = ImpCastExprToType(From, ToType,
isFloatingComplex ? CK_FloatingToIntegral
: CK_IntegralCast,
VK_PRValue, /*BasePath=*/nullptr, CCK)
.get();
}
}
break;
case ICK_Block_Pointer_Conversion: {
LangAS AddrSpaceL =
ToType->castAs<BlockPointerType>()->getPointeeType().getAddressSpace();
LangAS AddrSpaceR =
FromType->castAs<BlockPointerType>()->getPointeeType().getAddressSpace();
assert(Qualifiers::isAddressSpaceSupersetOf(AddrSpaceL, AddrSpaceR,
getASTContext()) &&
"Invalid cast");
CastKind Kind =
AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion : CK_BitCast;
From = ImpCastExprToType(From, ToType.getUnqualifiedType(), Kind,
VK_PRValue, /*BasePath=*/nullptr, CCK)
.get();
break;
}
case ICK_TransparentUnionConversion: {
ExprResult FromRes = From;
Sema::AssignConvertType ConvTy =
CheckTransparentUnionArgumentConstraints(ToType, FromRes);
if (FromRes.isInvalid())
return ExprError();
From = FromRes.get();
assert ((ConvTy == Sema::Compatible) &&
"Improper transparent union conversion");
(void)ConvTy;
break;
}
case ICK_Zero_Event_Conversion:
case ICK_Zero_Queue_Conversion:
From = ImpCastExprToType(From, ToType,
CK_ZeroToOCLOpaqueType,
From->getValueKind()).get();
break;
case ICK_Lvalue_To_Rvalue:
case ICK_Array_To_Pointer:
case ICK_Function_To_Pointer:
case ICK_Function_Conversion:
case ICK_Qualification:
case ICK_Num_Conversion_Kinds:
case ICK_C_Only_Conversion:
case ICK_Incompatible_Pointer_Conversion:
case ICK_HLSL_Array_RValue:
case ICK_HLSL_Vector_Truncation:
case ICK_HLSL_Vector_Splat:
llvm_unreachable("Improper second standard conversion");
}
if (SCS.Dimension != ICK_Identity) {
// If SCS.Element is not ICK_Identity the To and From types must be HLSL
// vectors or matrices.
// TODO: Support HLSL matrices.
assert((!From->getType()->isMatrixType() && !ToType->isMatrixType()) &&
"Dimension conversion for matrix types is not implemented yet.");
assert((ToType->isVectorType() || ToType->isBuiltinType()) &&
"Dimension conversion output must be vector or scalar type.");
switch (SCS.Dimension) {
case ICK_HLSL_Vector_Splat: {
// Vector splat from any arithmetic type to a vector.
Expr *Elem = prepareVectorSplat(ToType, From).get();
From = ImpCastExprToType(Elem, ToType, CK_VectorSplat, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
break;
}
case ICK_HLSL_Vector_Truncation: {
// Note: HLSL built-in vectors are ExtVectors. Since this truncates a
// vector to a smaller vector or to a scalar, this can only operate on
// arguments where the source type is an ExtVector and the destination
// type is destination type is either an ExtVectorType or a builtin scalar
// type.
auto *FromVec = From->getType()->castAs<VectorType>();
QualType TruncTy = FromVec->getElementType();
if (auto *ToVec = ToType->getAs<VectorType>())
TruncTy = Context.getExtVectorType(TruncTy, ToVec->getNumElements());
From = ImpCastExprToType(From, TruncTy, CK_HLSLVectorTruncation,
From->getValueKind())
.get();
break;
}
case ICK_Identity:
default:
llvm_unreachable("Improper element standard conversion");
}
}
switch (SCS.Third) {
case ICK_Identity:
// Nothing to do.
break;
case ICK_Function_Conversion:
// If both sides are functions (or pointers/references to them), there could
// be incompatible exception declarations.
if (CheckExceptionSpecCompatibility(From, ToType))
return ExprError();
From = ImpCastExprToType(From, ToType, CK_NoOp, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
break;
case ICK_Qualification: {
ExprValueKind VK = From->getValueKind();
CastKind CK = CK_NoOp;
if (ToType->isReferenceType() &&
ToType->getPointeeType().getAddressSpace() !=
From->getType().getAddressSpace())
CK = CK_AddressSpaceConversion;
if (ToType->isPointerType() &&
ToType->getPointeeType().getAddressSpace() !=
From->getType()->getPointeeType().getAddressSpace())
CK = CK_AddressSpaceConversion;
if (!isCast(CCK) &&
!ToType->getPointeeType().getQualifiers().hasUnaligned() &&
From->getType()->getPointeeType().getQualifiers().hasUnaligned()) {
Diag(From->getBeginLoc(), diag::warn_imp_cast_drops_unaligned)
<< InitialFromType << ToType;
}
From = ImpCastExprToType(From, ToType.getNonLValueExprType(Context), CK, VK,
/*BasePath=*/nullptr, CCK)
.get();
if (SCS.DeprecatedStringLiteralToCharPtr &&
!getLangOpts().WritableStrings) {
Diag(From->getBeginLoc(),
getLangOpts().CPlusPlus11
? diag::ext_deprecated_string_literal_conversion
: diag::warn_deprecated_string_literal_conversion)
<< ToType.getNonReferenceType();
}
break;
}
default:
llvm_unreachable("Improper third standard conversion");
}
// If this conversion sequence involved a scalar -> atomic conversion, perform
// that conversion now.
if (!ToAtomicType.isNull()) {
assert(Context.hasSameType(
ToAtomicType->castAs<AtomicType>()->getValueType(), From->getType()));
From = ImpCastExprToType(From, ToAtomicType, CK_NonAtomicToAtomic,
VK_PRValue, nullptr, CCK)
.get();
}
// Materialize a temporary if we're implicitly converting to a reference
// type. This is not required by the C++ rules but is necessary to maintain
// AST invariants.
if (ToType->isReferenceType() && From->isPRValue()) {
ExprResult Res = TemporaryMaterializationConversion(From);
if (Res.isInvalid())
return ExprError();
From = Res.get();
}
// If this conversion sequence succeeded and involved implicitly converting a
// _Nullable type to a _Nonnull one, complain.
if (!isCast(CCK))
diagnoseNullableToNonnullConversion(ToType, InitialFromType,
From->getBeginLoc());
return From;
}
/// Checks that type T is not a VLA.
///
/// @returns @c true if @p T is VLA and a diagnostic was emitted,
/// @c false otherwise.
static bool DiagnoseVLAInCXXTypeTrait(Sema &S, const TypeSourceInfo *T,
clang::tok::TokenKind TypeTraitID) {
if (!T->getType()->isVariableArrayType())
return false;
S.Diag(T->getTypeLoc().getBeginLoc(), diag::err_vla_unsupported)
<< 1 << TypeTraitID;
return true;
}
/// Checks that type T is not an atomic type (_Atomic).
///
/// @returns @c true if @p T is VLA and a diagnostic was emitted,
/// @c false otherwise.
static bool DiagnoseAtomicInCXXTypeTrait(Sema &S, const TypeSourceInfo *T,
clang::tok::TokenKind TypeTraitID) {
if (!T->getType()->isAtomicType())
return false;
S.Diag(T->getTypeLoc().getBeginLoc(), diag::err_atomic_unsupported)
<< TypeTraitID;
return true;
}
/// Check the completeness of a type in a unary type trait.
///
/// If the particular type trait requires a complete type, tries to complete
/// it. If completing the type fails, a diagnostic is emitted and false
/// returned. If completing the type succeeds or no completion was required,
/// returns true.
static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
SourceLocation Loc,
QualType ArgTy) {
// C++0x [meta.unary.prop]p3:
// For all of the class templates X declared in this Clause, instantiating
// that template with a template argument that is a class template
// specialization may result in the implicit instantiation of the template
// argument if and only if the semantics of X require that the argument
// must be a complete type.
// We apply this rule to all the type trait expressions used to implement
// these class templates. We also try to follow any GCC documented behavior
// in these expressions to ensure portability of standard libraries.
switch (UTT) {
default: llvm_unreachable("not a UTT");
// is_complete_type somewhat obviously cannot require a complete type.
case UTT_IsCompleteType:
// Fall-through
// These traits are modeled on the type predicates in C++0x
// [meta.unary.cat] and [meta.unary.comp]. They are not specified as
// requiring a complete type, as whether or not they return true cannot be
// impacted by the completeness of the type.
case UTT_IsVoid:
case UTT_IsIntegral:
case UTT_IsFloatingPoint:
case UTT_IsArray:
case UTT_IsBoundedArray:
case UTT_IsPointer:
case UTT_IsLvalueReference:
case UTT_IsRvalueReference:
case UTT_IsMemberFunctionPointer:
case UTT_IsMemberObjectPointer:
case UTT_IsEnum:
case UTT_IsScopedEnum:
case UTT_IsUnion:
case UTT_IsClass:
case UTT_IsFunction:
case UTT_IsReference:
case UTT_IsArithmetic:
case UTT_IsFundamental:
case UTT_IsObject:
case UTT_IsScalar:
case UTT_IsCompound:
case UTT_IsMemberPointer:
case UTT_IsTypedResourceElementCompatible:
// Fall-through
// These traits are modeled on type predicates in C++0x [meta.unary.prop]
// which requires some of its traits to have the complete type. However,
// the completeness of the type cannot impact these traits' semantics, and
// so they don't require it. This matches the comments on these traits in
// Table 49.
case UTT_IsConst:
case UTT_IsVolatile:
case UTT_IsSigned:
case UTT_IsUnboundedArray:
case UTT_IsUnsigned:
// This type trait always returns false, checking the type is moot.
case UTT_IsInterfaceClass:
return true;
// We diagnose incomplete class types later.
case UTT_StructuredBindingSize:
return true;
// C++14 [meta.unary.prop]:
// If T is a non-union class type, T shall be a complete type.
case UTT_IsEmpty:
case UTT_IsPolymorphic:
case UTT_IsAbstract:
if (const auto *RD = ArgTy->getAsCXXRecordDecl())
if (!RD->isUnion())
return !S.RequireCompleteType(
Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
return true;
// C++14 [meta.unary.prop]:
// If T is a class type, T shall be a complete type.
case UTT_IsFinal:
case UTT_IsSealed:
if (ArgTy->getAsCXXRecordDecl())
return !S.RequireCompleteType(
Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
return true;
// LWG3823: T shall be an array type, a complete type, or cv void.
case UTT_IsAggregate:
case UTT_IsImplicitLifetime:
if (ArgTy->isArrayType() || ArgTy->isVoidType())
return true;
return !S.RequireCompleteType(
Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
// C++1z [meta.unary.prop]:
// remove_all_extents_t<T> shall be a complete type or cv void.
case UTT_IsTrivial:
case UTT_IsTriviallyCopyable:
case UTT_IsStandardLayout:
case UTT_IsPOD:
case UTT_IsLiteral:
case UTT_IsBitwiseCloneable:
// By analogy, is_trivially_relocatable and is_trivially_equality_comparable
// impose the same constraints.
case UTT_IsTriviallyRelocatable:
case UTT_IsTriviallyEqualityComparable:
case UTT_CanPassInRegs:
// Per the GCC type traits documentation, T shall be a complete type, cv void,
// or an array of unknown bound. But GCC actually imposes the same constraints
// as above.
case UTT_HasNothrowAssign:
case UTT_HasNothrowMoveAssign:
case UTT_HasNothrowConstructor:
case UTT_HasNothrowCopy:
case UTT_HasTrivialAssign:
case UTT_HasTrivialMoveAssign:
case UTT_HasTrivialDefaultConstructor:
case UTT_HasTrivialMoveConstructor:
case UTT_HasTrivialCopy:
case UTT_HasTrivialDestructor:
case UTT_HasVirtualDestructor:
// has_unique_object_representations<T> when T is an array is defined in terms
// of has_unique_object_representations<remove_all_extents_t<T>>, so the base
// type needs to be complete even if the type is an incomplete array type.
case UTT_HasUniqueObjectRepresentations:
ArgTy = QualType(ArgTy->getBaseElementTypeUnsafe(), 0);
[[fallthrough]];
// C++1z [meta.unary.prop]:
// T shall be a complete type, cv void, or an array of unknown bound.
case UTT_IsDestructible:
case UTT_IsNothrowDestructible:
case UTT_IsTriviallyDestructible:
case UTT_IsIntangibleType:
if (ArgTy->isIncompleteArrayType() || ArgTy->isVoidType())
return true;
return !S.RequireCompleteType(
Loc, ArgTy, diag::err_incomplete_type_used_in_type_trait_expr);
}
}
static bool HasNoThrowOperator(const RecordType *RT, OverloadedOperatorKind Op,
Sema &Self, SourceLocation KeyLoc, ASTContext &C,
bool (CXXRecordDecl::*HasTrivial)() const,
bool (CXXRecordDecl::*HasNonTrivial)() const,
bool (CXXMethodDecl::*IsDesiredOp)() const)
{
CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
if ((RD->*HasTrivial)() && !(RD->*HasNonTrivial)())
return true;
DeclarationName Name = C.DeclarationNames.getCXXOperatorName(Op);
DeclarationNameInfo NameInfo(Name, KeyLoc);
LookupResult Res(Self, NameInfo, Sema::LookupOrdinaryName);
if (Self.LookupQualifiedName(Res, RD)) {
bool FoundOperator = false;
Res.suppressDiagnostics();
for (LookupResult::iterator Op = Res.begin(), OpEnd = Res.end();
Op != OpEnd; ++Op) {
if (isa<FunctionTemplateDecl>(*Op))
continue;
CXXMethodDecl *Operator = cast<CXXMethodDecl>(*Op);
if((Operator->*IsDesiredOp)()) {
FoundOperator = true;
auto *CPT = Operator->getType()->castAs<FunctionProtoType>();
CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
if (!CPT || !CPT->isNothrow())
return false;
}
}
return FoundOperator;
}
return false;
}
static bool HasNonDeletedDefaultedEqualityComparison(Sema &S,
const CXXRecordDecl *Decl,
SourceLocation KeyLoc) {
if (Decl->isUnion())
return false;
if (Decl->isLambda())
return Decl->isCapturelessLambda();
{
EnterExpressionEvaluationContext UnevaluatedContext(
S, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
// const ClassT& obj;
OpaqueValueExpr Operand(
KeyLoc,
Decl->getTypeForDecl()->getCanonicalTypeUnqualified().withConst(),
ExprValueKind::VK_LValue);
UnresolvedSet<16> Functions;
// obj == obj;
S.LookupBinOp(S.TUScope, {}, BinaryOperatorKind::BO_EQ, Functions);
auto Result = S.CreateOverloadedBinOp(KeyLoc, BinaryOperatorKind::BO_EQ,
Functions, &Operand, &Operand);
if (Result.isInvalid() || SFINAE.hasErrorOccurred())
return false;
const auto *CallExpr = dyn_cast<CXXOperatorCallExpr>(Result.get());
if (!CallExpr)
return false;
const auto *Callee = CallExpr->getDirectCallee();
auto ParamT = Callee->getParamDecl(0)->getType();
if (!Callee->isDefaulted())
return false;
if (!ParamT->isReferenceType() && !Decl->isTriviallyCopyable())
return false;
if (ParamT.getNonReferenceType()->getUnqualifiedDesugaredType() !=
Decl->getTypeForDecl())
return false;
}
return llvm::all_of(Decl->bases(),
[&](const CXXBaseSpecifier &BS) {
if (const auto *RD = BS.getType()->getAsCXXRecordDecl())
return HasNonDeletedDefaultedEqualityComparison(
S, RD, KeyLoc);
return true;
}) &&
llvm::all_of(Decl->fields(), [&](const FieldDecl *FD) {
auto Type = FD->getType();
if (Type->isArrayType())
Type = Type->getBaseElementTypeUnsafe()
->getCanonicalTypeUnqualified();
if (Type->isReferenceType() || Type->isEnumeralType())
return false;
if (const auto *RD = Type->getAsCXXRecordDecl())
return HasNonDeletedDefaultedEqualityComparison(S, RD, KeyLoc);
return true;
});
}
static bool isTriviallyEqualityComparableType(Sema &S, QualType Type, SourceLocation KeyLoc) {
QualType CanonicalType = Type.getCanonicalType();
if (CanonicalType->isIncompleteType() || CanonicalType->isDependentType() ||
CanonicalType->isEnumeralType() || CanonicalType->isArrayType())
return false;
if (const auto *RD = CanonicalType->getAsCXXRecordDecl()) {
if (!HasNonDeletedDefaultedEqualityComparison(S, RD, KeyLoc))
return false;
}
return S.getASTContext().hasUniqueObjectRepresentations(
CanonicalType, /*CheckIfTriviallyCopyable=*/false);
}
static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
SourceLocation KeyLoc,
TypeSourceInfo *TInfo) {
QualType T = TInfo->getType();
assert(!T->isDependentType() && "Cannot evaluate traits of dependent type");
ASTContext &C = Self.Context;
switch(UTT) {
default: llvm_unreachable("not a UTT");
// Type trait expressions corresponding to the primary type category
// predicates in C++0x [meta.unary.cat].
case UTT_IsVoid:
return T->isVoidType();
case UTT_IsIntegral:
return T->isIntegralType(C);
case UTT_IsFloatingPoint:
return T->isFloatingType();
case UTT_IsArray:
// Zero-sized arrays aren't considered arrays in partial specializations,
// so __is_array shouldn't consider them arrays either.
if (const auto *CAT = C.getAsConstantArrayType(T))
return CAT->getSize() != 0;
return T->isArrayType();
case UTT_IsBoundedArray:
if (DiagnoseVLAInCXXTypeTrait(Self, TInfo, tok::kw___is_bounded_array))
return false;
// Zero-sized arrays aren't considered arrays in partial specializations,
// so __is_bounded_array shouldn't consider them arrays either.
if (const auto *CAT = C.getAsConstantArrayType(T))
return CAT->getSize() != 0;
return T->isArrayType() && !T->isIncompleteArrayType();
case UTT_IsUnboundedArray:
if (DiagnoseVLAInCXXTypeTrait(Self, TInfo, tok::kw___is_unbounded_array))
return false;
return T->isIncompleteArrayType();
case UTT_IsPointer:
return T->isAnyPointerType();
case UTT_IsLvalueReference:
return T->isLValueReferenceType();
case UTT_IsRvalueReference:
return T->isRValueReferenceType();
case UTT_IsMemberFunctionPointer:
return T->isMemberFunctionPointerType();
case UTT_IsMemberObjectPointer:
return T->isMemberDataPointerType();
case UTT_IsEnum:
return T->isEnumeralType();
case UTT_IsScopedEnum:
return T->isScopedEnumeralType();
case UTT_IsUnion:
return T->isUnionType();
case UTT_IsClass:
return T->isClassType() || T->isStructureType() || T->isInterfaceType();
case UTT_IsFunction:
return T->isFunctionType();
// Type trait expressions which correspond to the convenient composition
// predicates in C++0x [meta.unary.comp].
case UTT_IsReference:
return T->isReferenceType();
case UTT_IsArithmetic:
return T->isArithmeticType() && !T->isEnumeralType();
case UTT_IsFundamental:
return T->isFundamentalType();
case UTT_IsObject:
return T->isObjectType();
case UTT_IsScalar:
// Note: semantic analysis depends on Objective-C lifetime types to be
// considered scalar types. However, such types do not actually behave
// like scalar types at run time (since they may require retain/release
// operations), so we report them as non-scalar.
if (T->isObjCLifetimeType()) {
switch (T.getObjCLifetime()) {
case Qualifiers::OCL_None:
case Qualifiers::OCL_ExplicitNone:
return true;
case Qualifiers::OCL_Strong:
case Qualifiers::OCL_Weak:
case Qualifiers::OCL_Autoreleasing:
return false;
}
}
return T->isScalarType();
case UTT_IsCompound:
return T->isCompoundType();
case UTT_IsMemberPointer:
return T->isMemberPointerType();
// Type trait expressions which correspond to the type property predicates
// in C++0x [meta.unary.prop].
case UTT_IsConst:
return T.isConstQualified();
case UTT_IsVolatile:
return T.isVolatileQualified();
case UTT_IsTrivial:
return T.isTrivialType(C);
case UTT_IsTriviallyCopyable:
return T.isTriviallyCopyableType(C);
case UTT_IsStandardLayout:
return T->isStandardLayoutType();
case UTT_IsPOD:
return T.isPODType(C);
case UTT_IsLiteral:
return T->isLiteralType(C);
case UTT_IsEmpty:
if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
return !RD->isUnion() && RD->isEmpty();
return false;
case UTT_IsPolymorphic:
if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
return !RD->isUnion() && RD->isPolymorphic();
return false;
case UTT_IsAbstract:
if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
return !RD->isUnion() && RD->isAbstract();
return false;
case UTT_IsAggregate:
// Report vector extensions and complex types as aggregates because they
// support aggregate initialization. GCC mirrors this behavior for vectors
// but not _Complex.
return T->isAggregateType() || T->isVectorType() || T->isExtVectorType() ||
T->isAnyComplexType();
// __is_interface_class only returns true when CL is invoked in /CLR mode and
// even then only when it is used with the 'interface struct ...' syntax
// Clang doesn't support /CLR which makes this type trait moot.
case UTT_IsInterfaceClass:
return false;
case UTT_IsFinal:
case UTT_IsSealed:
if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
return RD->hasAttr<FinalAttr>();
return false;
case UTT_IsSigned:
// Enum types should always return false.
// Floating points should always return true.
return T->isFloatingType() ||
(T->isSignedIntegerType() && !T->isEnumeralType());
case UTT_IsUnsigned:
// Enum types should always return false.
return T->isUnsignedIntegerType() && !T->isEnumeralType();
// Type trait expressions which query classes regarding their construction,
// destruction, and copying. Rather than being based directly on the
// related type predicates in the standard, they are specified by both
// GCC[1] and the Embarcadero C++ compiler[2], and Clang implements those
// specifications.
//
// 1: http://gcc.gnu/.org/onlinedocs/gcc/Type-Traits.html
// 2: http://docwiki.embarcadero.com/RADStudio/XE/en/Type_Trait_Functions_(C%2B%2B0x)_Index
//
// Note that these builtins do not behave as documented in g++: if a class
// has both a trivial and a non-trivial special member of a particular kind,
// they return false! For now, we emulate this behavior.
// FIXME: This appears to be a g++ bug: more complex cases reveal that it
// does not correctly compute triviality in the presence of multiple special
// members of the same kind. Revisit this once the g++ bug is fixed.
case UTT_HasTrivialDefaultConstructor:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
// If __is_pod (type) is true then the trait is true, else if type is
// a cv class or union type (or array thereof) with a trivial default
// constructor ([class.ctor]) then the trait is true, else it is false.
if (T.isPODType(C))
return true;
if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
return RD->hasTrivialDefaultConstructor() &&
!RD->hasNonTrivialDefaultConstructor();
return false;
case UTT_HasTrivialMoveConstructor:
// This trait is implemented by MSVC 2012 and needed to parse the
// standard library headers. Specifically this is used as the logic
// behind std::is_trivially_move_constructible (20.9.4.3).
if (T.isPODType(C))
return true;
if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
return RD->hasTrivialMoveConstructor() && !RD->hasNonTrivialMoveConstructor();
return false;
case UTT_HasTrivialCopy:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
// If __is_pod (type) is true or type is a reference type then
// the trait is true, else if type is a cv class or union type
// with a trivial copy constructor ([class.copy]) then the trait
// is true, else it is false.
if (T.isPODType(C) || T->isReferenceType())
return true;
if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
return RD->hasTrivialCopyConstructor() &&
!RD->hasNonTrivialCopyConstructor();
return false;
case UTT_HasTrivialMoveAssign:
// This trait is implemented by MSVC 2012 and needed to parse the
// standard library headers. Specifically it is used as the logic
// behind std::is_trivially_move_assignable (20.9.4.3)
if (T.isPODType(C))
return true;
if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
return RD->hasTrivialMoveAssignment() && !RD->hasNonTrivialMoveAssignment();
return false;
case UTT_HasTrivialAssign:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
// If type is const qualified or is a reference type then the
// trait is false. Otherwise if __is_pod (type) is true then the
// trait is true, else if type is a cv class or union type with
// a trivial copy assignment ([class.copy]) then the trait is
// true, else it is false.
// Note: the const and reference restrictions are interesting,
// given that const and reference members don't prevent a class
// from having a trivial copy assignment operator (but do cause
// errors if the copy assignment operator is actually used, q.v.
// [class.copy]p12).
if (T.isConstQualified())
return false;
if (T.isPODType(C))
return true;
if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
return RD->hasTrivialCopyAssignment() &&
!RD->hasNonTrivialCopyAssignment();
return false;
case UTT_IsDestructible:
case UTT_IsTriviallyDestructible:
case UTT_IsNothrowDestructible:
// C++14 [meta.unary.prop]:
// For reference types, is_destructible<T>::value is true.
if (T->isReferenceType())
return true;
// Objective-C++ ARC: autorelease types don't require destruction.
if (T->isObjCLifetimeType() &&
T.getObjCLifetime() == Qualifiers::OCL_Autoreleasing)
return true;
// C++14 [meta.unary.prop]:
// For incomplete types and function types, is_destructible<T>::value is
// false.
if (T->isIncompleteType() || T->isFunctionType())
return false;
// A type that requires destruction (via a non-trivial destructor or ARC
// lifetime semantics) is not trivially-destructible.
if (UTT == UTT_IsTriviallyDestructible && T.isDestructedType())
return false;
// C++14 [meta.unary.prop]:
// For object types and given U equal to remove_all_extents_t<T>, if the
// expression std::declval<U&>().~U() is well-formed when treated as an
// unevaluated operand (Clause 5), then is_destructible<T>::value is true
if (auto *RD = C.getBaseElementType(T)->getAsCXXRecordDecl()) {
CXXDestructorDecl *Destructor = Self.LookupDestructor(RD);
if (!Destructor)
return false;
// C++14 [dcl.fct.def.delete]p2:
// A program that refers to a deleted function implicitly or
// explicitly, other than to declare it, is ill-formed.
if (Destructor->isDeleted())
return false;
if (C.getLangOpts().AccessControl && Destructor->getAccess() != AS_public)
return false;
if (UTT == UTT_IsNothrowDestructible) {
auto *CPT = Destructor->getType()->castAs<FunctionProtoType>();
CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
if (!CPT || !CPT->isNothrow())
return false;
}
}
return true;
case UTT_HasTrivialDestructor:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html
// If __is_pod (type) is true or type is a reference type
// then the trait is true, else if type is a cv class or union
// type (or array thereof) with a trivial destructor
// ([class.dtor]) then the trait is true, else it is
// false.
if (T.isPODType(C) || T->isReferenceType())
return true;
// Objective-C++ ARC: autorelease types don't require destruction.
if (T->isObjCLifetimeType() &&
T.getObjCLifetime() == Qualifiers::OCL_Autoreleasing)
return true;
if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl())
return RD->hasTrivialDestructor();
return false;
// TODO: Propagate nothrowness for implicitly declared special members.
case UTT_HasNothrowAssign:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
// If type is const qualified or is a reference type then the
// trait is false. Otherwise if __has_trivial_assign (type)
// is true then the trait is true, else if type is a cv class
// or union type with copy assignment operators that are known
// not to throw an exception then the trait is true, else it is
// false.
if (C.getBaseElementType(T).isConstQualified())
return false;
if (T->isReferenceType())
return false;
if (T.isPODType(C) || T->isObjCLifetimeType())
return true;
if (const RecordType *RT = T->getAs<RecordType>())
return HasNoThrowOperator(RT, OO_Equal, Self, KeyLoc, C,
&CXXRecordDecl::hasTrivialCopyAssignment,
&CXXRecordDecl::hasNonTrivialCopyAssignment,
&CXXMethodDecl::isCopyAssignmentOperator);
return false;
case UTT_HasNothrowMoveAssign:
// This trait is implemented by MSVC 2012 and needed to parse the
// standard library headers. Specifically this is used as the logic
// behind std::is_nothrow_move_assignable (20.9.4.3).
if (T.isPODType(C))
return true;
if (const RecordType *RT = C.getBaseElementType(T)->getAs<RecordType>())
return HasNoThrowOperator(RT, OO_Equal, Self, KeyLoc, C,
&CXXRecordDecl::hasTrivialMoveAssignment,
&CXXRecordDecl::hasNonTrivialMoveAssignment,
&CXXMethodDecl::isMoveAssignmentOperator);
return false;
case UTT_HasNothrowCopy:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
// If __has_trivial_copy (type) is true then the trait is true, else
// if type is a cv class or union type with copy constructors that are
// known not to throw an exception then the trait is true, else it is
// false.
if (T.isPODType(C) || T->isReferenceType() || T->isObjCLifetimeType())
return true;
if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
if (RD->hasTrivialCopyConstructor() &&
!RD->hasNonTrivialCopyConstructor())
return true;
bool FoundConstructor = false;
unsigned FoundTQs;
for (const auto *ND : Self.LookupConstructors(RD)) {
// A template constructor is never a copy constructor.
// FIXME: However, it may actually be selected at the actual overload
// resolution point.
if (isa<FunctionTemplateDecl>(ND->getUnderlyingDecl()))
continue;
// UsingDecl itself is not a constructor
if (isa<UsingDecl>(ND))
continue;
auto *Constructor = cast<CXXConstructorDecl>(ND->getUnderlyingDecl());
if (Constructor->isCopyConstructor(FoundTQs)) {
FoundConstructor = true;
auto *CPT = Constructor->getType()->castAs<FunctionProtoType>();
CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
if (!CPT)
return false;
// TODO: check whether evaluating default arguments can throw.
// For now, we'll be conservative and assume that they can throw.
if (!CPT->isNothrow() || CPT->getNumParams() > 1)
return false;
}
}
return FoundConstructor;
}
return false;
case UTT_HasNothrowConstructor:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html
// If __has_trivial_constructor (type) is true then the trait is
// true, else if type is a cv class or union type (or array
// thereof) with a default constructor that is known not to
// throw an exception then the trait is true, else it is false.
if (T.isPODType(C) || T->isObjCLifetimeType())
return true;
if (CXXRecordDecl *RD = C.getBaseElementType(T)->getAsCXXRecordDecl()) {
if (RD->hasTrivialDefaultConstructor() &&
!RD->hasNonTrivialDefaultConstructor())
return true;
bool FoundConstructor = false;
for (const auto *ND : Self.LookupConstructors(RD)) {
// FIXME: In C++0x, a constructor template can be a default constructor.
if (isa<FunctionTemplateDecl>(ND->getUnderlyingDecl()))
continue;
// UsingDecl itself is not a constructor
if (isa<UsingDecl>(ND))
continue;
auto *Constructor = cast<CXXConstructorDecl>(ND->getUnderlyingDecl());
if (Constructor->isDefaultConstructor()) {
FoundConstructor = true;
auto *CPT = Constructor->getType()->castAs<FunctionProtoType>();
CPT = Self.ResolveExceptionSpec(KeyLoc, CPT);
if (!CPT)
return false;
// FIXME: check whether evaluating default arguments can throw.
// For now, we'll be conservative and assume that they can throw.
if (!CPT->isNothrow() || CPT->getNumParams() > 0)
return false;
}
}
return FoundConstructor;
}
return false;
case UTT_HasVirtualDestructor:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
// If type is a class type with a virtual destructor ([class.dtor])
// then the trait is true, else it is false.
if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
if (CXXDestructorDecl *Destructor = Self.LookupDestructor(RD))
return Destructor->isVirtual();
return false;
// These type trait expressions are modeled on the specifications for the
// Embarcadero C++0x type trait functions:
// http://docwiki.embarcadero.com/RADStudio/XE/en/Type_Trait_Functions_(C%2B%2B0x)_Index
case UTT_IsCompleteType:
// http://docwiki.embarcadero.com/RADStudio/XE/en/Is_complete_type_(typename_T_):
// Returns True if and only if T is a complete type at the point of the
// function call.
return !T->isIncompleteType();
case UTT_HasUniqueObjectRepresentations:
return C.hasUniqueObjectRepresentations(T);
case UTT_IsTriviallyRelocatable:
return T.isTriviallyRelocatableType(C);
case UTT_IsBitwiseCloneable:
return T.isBitwiseCloneableType(C);
case UTT_CanPassInRegs:
if (CXXRecordDecl *RD = T->getAsCXXRecordDecl(); RD && !T.hasQualifiers())
return RD->canPassInRegisters();
Self.Diag(KeyLoc, diag::err_builtin_pass_in_regs_non_class) << T;
return false;
case UTT_IsTriviallyEqualityComparable:
return isTriviallyEqualityComparableType(Self, T, KeyLoc);
case UTT_IsImplicitLifetime: {
DiagnoseVLAInCXXTypeTrait(Self, TInfo,
tok::kw___builtin_is_implicit_lifetime);
DiagnoseAtomicInCXXTypeTrait(Self, TInfo,
tok::kw___builtin_is_implicit_lifetime);
// [basic.types.general] p9
// Scalar types, implicit-lifetime class types ([class.prop]),
// array types, and cv-qualified versions of these types
// are collectively called implicit-lifetime types.
QualType UnqualT = T->getCanonicalTypeUnqualified();
if (UnqualT->isScalarType())
return true;
if (UnqualT->isArrayType() || UnqualT->isVectorType())
return true;
const CXXRecordDecl *RD = UnqualT->getAsCXXRecordDecl();
if (!RD)
return false;
// [class.prop] p9
// A class S is an implicit-lifetime class if
// - it is an aggregate whose destructor is not user-provided or
// - it has at least one trivial eligible constructor and a trivial,
// non-deleted destructor.
const CXXDestructorDecl *Dtor = RD->getDestructor();
if (UnqualT->isAggregateType())
if (Dtor && !Dtor->isUserProvided())
return true;
if (RD->hasTrivialDestructor() && (!Dtor || !Dtor->isDeleted()))
if (RD->hasTrivialDefaultConstructor() ||
RD->hasTrivialCopyConstructor() || RD->hasTrivialMoveConstructor())
return true;
return false;
}
case UTT_IsIntangibleType:
assert(Self.getLangOpts().HLSL && "intangible types are HLSL-only feature");
if (!T->isVoidType() && !T->isIncompleteArrayType())
if (Self.RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), T,
diag::err_incomplete_type))
return false;
if (DiagnoseVLAInCXXTypeTrait(Self, TInfo,
tok::kw___builtin_hlsl_is_intangible))
return false;
return T->isHLSLIntangibleType();
case UTT_IsTypedResourceElementCompatible:
assert(Self.getLangOpts().HLSL &&
"typed resource element compatible types are an HLSL-only feature");
if (T->isIncompleteType())
return false;
return Self.HLSL().IsTypedResourceElementCompatible(T);
}
}
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
const TypeSourceInfo *Rhs, SourceLocation KeyLoc);
static ExprResult CheckConvertibilityForTypeTraits(
Sema &Self, const TypeSourceInfo *Lhs, const TypeSourceInfo *Rhs,
SourceLocation KeyLoc, llvm::BumpPtrAllocator &OpaqueExprAllocator) {
QualType LhsT = Lhs->getType();
QualType RhsT = Rhs->getType();
// C++0x [meta.rel]p4:
// Given the following function prototype:
//
// template <class T>
// typename add_rvalue_reference<T>::type create();
//
// the predicate condition for a template specialization
// is_convertible<From, To> shall be satisfied if and only if
// the return expression in the following code would be
// well-formed, including any implicit conversions to the return
// type of the function:
//
// To test() {
// return create<From>();
// }
//
// Access checking is performed as if in a context unrelated to To and
// From. Only the validity of the immediate context of the expression
// of the return-statement (including conversions to the return type)
// is considered.
//
// We model the initialization as a copy-initialization of a temporary
// of the appropriate type, which for this expression is identical to the
// return statement (since NRVO doesn't apply).
// Functions aren't allowed to return function or array types.
if (RhsT->isFunctionType() || RhsT->isArrayType())
return ExprError();
// A function definition requires a complete, non-abstract return type.
if (!Self.isCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT) ||
Self.isAbstractType(Rhs->getTypeLoc().getBeginLoc(), RhsT))
return ExprError();
// Compute the result of add_rvalue_reference.
if (LhsT->isObjectType() || LhsT->isFunctionType())
LhsT = Self.Context.getRValueReferenceType(LhsT);
// Build a fake source and destination for initialization.
InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT));
Expr *From = new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
OpaqueValueExpr(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
Expr::getValueKindForType(LhsT));
InitializationKind Kind =
InitializationKind::CreateCopy(KeyLoc, SourceLocation());
// Perform the initialization in an unevaluated context within a SFINAE
// trap at translation unit scope.
EnterExpressionEvaluationContext Unevaluated(
Self, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true);
Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
InitializationSequence Init(Self, To, Kind, From);
if (Init.Failed())
return ExprError();
ExprResult Result = Init.Perform(Self, To, Kind, From);
if (Result.isInvalid() || SFINAE.hasErrorOccurred())
return ExprError();
return Result;
}
static APValue EvaluateSizeTTypeTrait(Sema &S, TypeTrait Kind,
SourceLocation KWLoc,
ArrayRef<TypeSourceInfo *> Args,
SourceLocation RParenLoc,
bool IsDependent) {
if (IsDependent)
return APValue();
switch (Kind) {
case TypeTrait::UTT_StructuredBindingSize: {
QualType T = Args[0]->getType();
SourceRange ArgRange = Args[0]->getTypeLoc().getSourceRange();
UnsignedOrNone Size =
S.GetDecompositionElementCount(T, ArgRange.getBegin());
if (!Size) {
S.Diag(KWLoc, diag::err_arg_is_not_destructurable) << T << ArgRange;
return APValue();
}
llvm::APSInt V =
S.getASTContext().MakeIntValue(*Size, S.getASTContext().getSizeType());
return APValue{V};
break;
}
default:
llvm_unreachable("Not a SizeT type trait");
}
}
static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
SourceLocation KWLoc,
ArrayRef<TypeSourceInfo *> Args,
SourceLocation RParenLoc,
bool IsDependent) {
if (IsDependent)
return false;
if (Kind <= UTT_Last)
return EvaluateUnaryTypeTrait(S, Kind, KWLoc, Args[0]);
// Evaluate ReferenceBindsToTemporary and ReferenceConstructsFromTemporary
// alongside the IsConstructible traits to avoid duplication.
if (Kind <= BTT_Last && Kind != BTT_ReferenceBindsToTemporary &&
Kind != BTT_ReferenceConstructsFromTemporary &&
Kind != BTT_ReferenceConvertsFromTemporary)
return EvaluateBinaryTypeTrait(S, Kind, Args[0],
Args[1], RParenLoc);
switch (Kind) {
case clang::BTT_ReferenceBindsToTemporary:
case clang::BTT_ReferenceConstructsFromTemporary:
case clang::BTT_ReferenceConvertsFromTemporary:
case clang::TT_IsConstructible:
case clang::TT_IsNothrowConstructible:
case clang::TT_IsTriviallyConstructible: {
// C++11 [meta.unary.prop]:
// is_trivially_constructible is defined as:
//
// is_constructible<T, Args...>::value is true and the variable
// definition for is_constructible, as defined below, is known to call
// no operation that is not trivial.
//
// The predicate condition for a template specialization
// is_constructible<T, Args...> shall be satisfied if and only if the
// following variable definition would be well-formed for some invented
// variable t:
//
// T t(create<Args>()...);
assert(!Args.empty());
// Precondition: T and all types in the parameter pack Args shall be
// complete types, (possibly cv-qualified) void, or arrays of
// unknown bound.
for (const auto *TSI : Args) {
QualType ArgTy = TSI->getType();
if (ArgTy->isVoidType() || ArgTy->isIncompleteArrayType())
continue;
if (S.RequireCompleteType(KWLoc, ArgTy,
diag::err_incomplete_type_used_in_type_trait_expr))
return false;
}
// Make sure the first argument is not incomplete nor a function type.
QualType T = Args[0]->getType();
if (T->isIncompleteType() || T->isFunctionType())
return false;
// Make sure the first argument is not an abstract type.
CXXRecordDecl *RD = T->getAsCXXRecordDecl();
if (RD && RD->isAbstract())
return false;
llvm::BumpPtrAllocator OpaqueExprAllocator;
SmallVector<Expr *, 2> ArgExprs;
ArgExprs.reserve(Args.size() - 1);
for (unsigned I = 1, N = Args.size(); I != N; ++I) {
QualType ArgTy = Args[I]->getType();
if (ArgTy->isObjectType() || ArgTy->isFunctionType())
ArgTy = S.Context.getRValueReferenceType(ArgTy);
ArgExprs.push_back(
new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
OpaqueValueExpr(Args[I]->getTypeLoc().getBeginLoc(),
ArgTy.getNonLValueExprType(S.Context),
Expr::getValueKindForType(ArgTy)));
}
// Perform the initialization in an unevaluated context within a SFINAE
// trap at translation unit scope.
EnterExpressionEvaluationContext Unevaluated(
S, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
InitializedEntity To(
InitializedEntity::InitializeTemporary(S.Context, Args[0]));
InitializationKind InitKind(
Kind == clang::BTT_ReferenceConvertsFromTemporary
? InitializationKind::CreateCopy(KWLoc, KWLoc)
: InitializationKind::CreateDirect(KWLoc, KWLoc, RParenLoc));
InitializationSequence Init(S, To, InitKind, ArgExprs);
if (Init.Failed())
return false;
ExprResult Result = Init.Perform(S, To, InitKind, ArgExprs);
if (Result.isInvalid() || SFINAE.hasErrorOccurred())
return false;
if (Kind == clang::TT_IsConstructible)
return true;
if (Kind == clang::BTT_ReferenceBindsToTemporary ||
Kind == clang::BTT_ReferenceConstructsFromTemporary ||
Kind == clang::BTT_ReferenceConvertsFromTemporary) {
if (!T->isReferenceType())
return false;
if (!Init.isDirectReferenceBinding())
return true;
if (Kind == clang::BTT_ReferenceBindsToTemporary)
return false;
QualType U = Args[1]->getType();
if (U->isReferenceType())
return false;
TypeSourceInfo *TPtr = S.Context.CreateTypeSourceInfo(
S.Context.getPointerType(T.getNonReferenceType()));
TypeSourceInfo *UPtr = S.Context.CreateTypeSourceInfo(
S.Context.getPointerType(U.getNonReferenceType()));
return !CheckConvertibilityForTypeTraits(S, UPtr, TPtr, RParenLoc,
OpaqueExprAllocator)
.isInvalid();
}
if (Kind == clang::TT_IsNothrowConstructible)
return S.canThrow(Result.get()) == CT_Cannot;
if (Kind == clang::TT_IsTriviallyConstructible) {
// Under Objective-C ARC and Weak, if the destination has non-trivial
// Objective-C lifetime, this is a non-trivial construction.
if (T.getNonReferenceType().hasNonTrivialObjCLifetime())
return false;
// The initialization succeeded; now make sure there are no non-trivial
// calls.
return !Result.get()->hasNonTrivialCall(S.Context);
}
llvm_unreachable("unhandled type trait");
return false;
}
default: llvm_unreachable("not a TT");
}
return false;
}
namespace {
void DiagnoseBuiltinDeprecation(Sema& S, TypeTrait Kind,
SourceLocation KWLoc) {
TypeTrait Replacement;
switch (Kind) {
case UTT_HasNothrowAssign:
case UTT_HasNothrowMoveAssign:
Replacement = BTT_IsNothrowAssignable;
break;
case UTT_HasNothrowCopy:
case UTT_HasNothrowConstructor:
Replacement = TT_IsNothrowConstructible;
break;
case UTT_HasTrivialAssign:
case UTT_HasTrivialMoveAssign:
Replacement = BTT_IsTriviallyAssignable;
break;
case UTT_HasTrivialCopy:
Replacement = UTT_IsTriviallyCopyable;
break;
case UTT_HasTrivialDefaultConstructor:
case UTT_HasTrivialMoveConstructor:
Replacement = TT_IsTriviallyConstructible;
break;
case UTT_HasTrivialDestructor:
Replacement = UTT_IsTriviallyDestructible;
break;
default:
return;
}
S.Diag(KWLoc, diag::warn_deprecated_builtin)
<< getTraitSpelling(Kind) << getTraitSpelling(Replacement);
}
}
bool Sema::CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N) {
if (Arity && N != Arity) {
Diag(Loc, diag::err_type_trait_arity)
<< Arity << 0 << (Arity > 1) << (int)N << SourceRange(Loc);
return false;
}
if (!Arity && N == 0) {
Diag(Loc, diag::err_type_trait_arity)
<< 1 << 1 << 1 << (int)N << SourceRange(Loc);
return false;
}
return true;
}
enum class TypeTraitReturnType {
Bool,
SizeT,
};
static TypeTraitReturnType GetReturnType(TypeTrait Kind) {
if (Kind == TypeTrait::UTT_StructuredBindingSize)
return TypeTraitReturnType::SizeT;
return TypeTraitReturnType::Bool;
}
ExprResult Sema::BuildTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
ArrayRef<TypeSourceInfo *> Args,
SourceLocation RParenLoc) {
if (!CheckTypeTraitArity(getTypeTraitArity(Kind), KWLoc, Args.size()))
return ExprError();
if (Kind <= UTT_Last && !CheckUnaryTypeTraitTypeCompleteness(
*this, Kind, KWLoc, Args[0]->getType()))
return ExprError();
DiagnoseBuiltinDeprecation(*this, Kind, KWLoc);
bool Dependent = false;
for (unsigned I = 0, N = Args.size(); I != N; ++I) {
if (Args[I]->getType()->isDependentType()) {
Dependent = true;
break;
}
}
switch (GetReturnType(Kind)) {
case TypeTraitReturnType::Bool: {
bool Result = EvaluateBooleanTypeTrait(*this, Kind, KWLoc, Args, RParenLoc,
Dependent);
return TypeTraitExpr::Create(Context, Context.getLogicalOperationType(),
KWLoc, Kind, Args, RParenLoc, Result);
}
case TypeTraitReturnType::SizeT: {
APValue Result =
EvaluateSizeTTypeTrait(*this, Kind, KWLoc, Args, RParenLoc, Dependent);
return TypeTraitExpr::Create(Context, Context.getSizeType(), KWLoc, Kind,
Args, RParenLoc, Result);
}
}
llvm_unreachable("unhandled type trait return type");
}
ExprResult Sema::ActOnTypeTrait(TypeTrait Kind, SourceLocation KWLoc,
ArrayRef<ParsedType> Args,
SourceLocation RParenLoc) {
SmallVector<TypeSourceInfo *, 4> ConvertedArgs;
ConvertedArgs.reserve(Args.size());
for (unsigned I = 0, N = Args.size(); I != N; ++I) {
TypeSourceInfo *TInfo;
QualType T = GetTypeFromParser(Args[I], &TInfo);
if (!TInfo)
TInfo = Context.getTrivialTypeSourceInfo(T, KWLoc);
ConvertedArgs.push_back(TInfo);
}
return BuildTypeTrait(Kind, KWLoc, ConvertedArgs, RParenLoc);
}
static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
const TypeSourceInfo *Rhs, SourceLocation KeyLoc) {
QualType LhsT = Lhs->getType();
QualType RhsT = Rhs->getType();
assert(!LhsT->isDependentType() && !RhsT->isDependentType() &&
"Cannot evaluate traits of dependent types");
switch(BTT) {
case BTT_IsBaseOf: {
// C++0x [meta.rel]p2
// Base is a base class of Derived without regard to cv-qualifiers or
// Base and Derived are not unions and name the same class type without
// regard to cv-qualifiers.
const RecordType *lhsRecord = LhsT->getAs<RecordType>();
const RecordType *rhsRecord = RhsT->getAs<RecordType>();
if (!rhsRecord || !lhsRecord) {
const ObjCObjectType *LHSObjTy = LhsT->getAs<ObjCObjectType>();
const ObjCObjectType *RHSObjTy = RhsT->getAs<ObjCObjectType>();
if (!LHSObjTy || !RHSObjTy)
return false;
ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface();
ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface();
if (!BaseInterface || !DerivedInterface)
return false;
if (Self.RequireCompleteType(
Rhs->getTypeLoc().getBeginLoc(), RhsT,
diag::err_incomplete_type_used_in_type_trait_expr))
return false;
return BaseInterface->isSuperClassOf(DerivedInterface);
}
assert(Self.Context.hasSameUnqualifiedType(LhsT, RhsT)
== (lhsRecord == rhsRecord));
// Unions are never base classes, and never have base classes.
// It doesn't matter if they are complete or not. See PR#41843
if (lhsRecord && lhsRecord->getDecl()->isUnion())
return false;
if (rhsRecord && rhsRecord->getDecl()->isUnion())
return false;
if (lhsRecord == rhsRecord)
return true;
// C++0x [meta.rel]p2:
// If Base and Derived are class types and are different types
// (ignoring possible cv-qualifiers) then Derived shall be a
// complete type.
if (Self.RequireCompleteType(
Rhs->getTypeLoc().getBeginLoc(), RhsT,
diag::err_incomplete_type_used_in_type_trait_expr))
return false;
return cast<CXXRecordDecl>(rhsRecord->getDecl())
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
}
case BTT_IsVirtualBaseOf: {
const RecordType *BaseRecord = LhsT->getAs<RecordType>();
const RecordType *DerivedRecord = RhsT->getAs<RecordType>();
if (!BaseRecord || !DerivedRecord) {
DiagnoseVLAInCXXTypeTrait(Self, Lhs,
tok::kw___builtin_is_virtual_base_of);
DiagnoseVLAInCXXTypeTrait(Self, Rhs,
tok::kw___builtin_is_virtual_base_of);
return false;
}
if (BaseRecord->isUnionType() || DerivedRecord->isUnionType())
return false;
if (!BaseRecord->isStructureOrClassType() ||
!DerivedRecord->isStructureOrClassType())
return false;
if (Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
diag::err_incomplete_type))
return false;
return cast<CXXRecordDecl>(DerivedRecord->getDecl())
->isVirtuallyDerivedFrom(cast<CXXRecordDecl>(BaseRecord->getDecl()));
}
case BTT_IsSame:
return Self.Context.hasSameType(LhsT, RhsT);
case BTT_TypeCompatible: {
// GCC ignores cv-qualifiers on arrays for this builtin.
Qualifiers LhsQuals, RhsQuals;
QualType Lhs = Self.getASTContext().getUnqualifiedArrayType(LhsT, LhsQuals);
QualType Rhs = Self.getASTContext().getUnqualifiedArrayType(RhsT, RhsQuals);
return Self.Context.typesAreCompatible(Lhs, Rhs);
}
case BTT_IsConvertible:
case BTT_IsConvertibleTo:
case BTT_IsNothrowConvertible: {
if (RhsT->isVoidType())
return LhsT->isVoidType();
llvm::BumpPtrAllocator OpaqueExprAllocator;
ExprResult Result = CheckConvertibilityForTypeTraits(Self, Lhs, Rhs, KeyLoc,
OpaqueExprAllocator);
if (Result.isInvalid())
return false;
if (BTT != BTT_IsNothrowConvertible)
return true;
return Self.canThrow(Result.get()) == CT_Cannot;
}
case BTT_IsAssignable:
case BTT_IsNothrowAssignable:
case BTT_IsTriviallyAssignable: {
// C++11 [meta.unary.prop]p3:
// is_trivially_assignable is defined as:
// is_assignable<T, U>::value is true and the assignment, as defined by
// is_assignable, is known to call no operation that is not trivial
//
// is_assignable is defined as:
// The expression declval<T>() = declval<U>() is well-formed when
// treated as an unevaluated operand (Clause 5).
//
// For both, T and U shall be complete types, (possibly cv-qualified)
// void, or arrays of unknown bound.
if (!LhsT->isVoidType() && !LhsT->isIncompleteArrayType() &&
Self.RequireCompleteType(
Lhs->getTypeLoc().getBeginLoc(), LhsT,
diag::err_incomplete_type_used_in_type_trait_expr))
return false;
if (!RhsT->isVoidType() && !RhsT->isIncompleteArrayType() &&
Self.RequireCompleteType(
Rhs->getTypeLoc().getBeginLoc(), RhsT,
diag::err_incomplete_type_used_in_type_trait_expr))
return false;
// cv void is never assignable.
if (LhsT->isVoidType() || RhsT->isVoidType())
return false;
// Build expressions that emulate the effect of declval<T>() and
// declval<U>().
if (LhsT->isObjectType() || LhsT->isFunctionType())
LhsT = Self.Context.getRValueReferenceType(LhsT);
if (RhsT->isObjectType() || RhsT->isFunctionType())
RhsT = Self.Context.getRValueReferenceType(RhsT);
OpaqueValueExpr Lhs(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
Expr::getValueKindForType(LhsT));
OpaqueValueExpr Rhs(KeyLoc, RhsT.getNonLValueExprType(Self.Context),
Expr::getValueKindForType(RhsT));
// Attempt the assignment in an unevaluated context within a SFINAE
// trap at translation unit scope.
EnterExpressionEvaluationContext Unevaluated(
Self, Sema::ExpressionEvaluationContext::Unevaluated);
Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true);
Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
ExprResult Result = Self.BuildBinOp(/*S=*/nullptr, KeyLoc, BO_Assign, &Lhs,
&Rhs);
if (Result.isInvalid())
return false;
// Treat the assignment as unused for the purpose of -Wdeprecated-volatile.
Self.CheckUnusedVolatileAssignment(Result.get());
if (SFINAE.hasErrorOccurred())
return false;
if (BTT == BTT_IsAssignable)
return true;
if (BTT == BTT_IsNothrowAssignable)
return Self.canThrow(Result.get()) == CT_Cannot;
if (BTT == BTT_IsTriviallyAssignable) {
// Under Objective-C ARC and Weak, if the destination has non-trivial
// Objective-C lifetime, this is a non-trivial assignment.
if (LhsT.getNonReferenceType().hasNonTrivialObjCLifetime())
return false;
return !Result.get()->hasNonTrivialCall(Self.Context);
}
llvm_unreachable("unhandled type trait");
return false;
}
case BTT_IsLayoutCompatible: {
if (!LhsT->isVoidType() && !LhsT->isIncompleteArrayType())
Self.RequireCompleteType(Lhs->getTypeLoc().getBeginLoc(), LhsT,
diag::err_incomplete_type);
if (!RhsT->isVoidType() && !RhsT->isIncompleteArrayType())
Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
diag::err_incomplete_type);
DiagnoseVLAInCXXTypeTrait(Self, Lhs, tok::kw___is_layout_compatible);
DiagnoseVLAInCXXTypeTrait(Self, Rhs, tok::kw___is_layout_compatible);
return Self.IsLayoutCompatible(LhsT, RhsT);
}
case BTT_IsPointerInterconvertibleBaseOf: {
if (LhsT->isStructureOrClassType() && RhsT->isStructureOrClassType() &&
!Self.getASTContext().hasSameUnqualifiedType(LhsT, RhsT)) {
Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
diag::err_incomplete_type);
}
DiagnoseVLAInCXXTypeTrait(Self, Lhs,
tok::kw___is_pointer_interconvertible_base_of);
DiagnoseVLAInCXXTypeTrait(Self, Rhs,
tok::kw___is_pointer_interconvertible_base_of);
return Self.IsPointerInterconvertibleBaseOf(Lhs, Rhs);
}
case BTT_IsDeducible: {
const auto *TSTToBeDeduced = cast<DeducedTemplateSpecializationType>(LhsT);
sema::TemplateDeductionInfo Info(KeyLoc);
return Self.DeduceTemplateArgumentsFromType(
TSTToBeDeduced->getTemplateName().getAsTemplateDecl(), RhsT,
Info) == TemplateDeductionResult::Success;
}
case BTT_IsScalarizedLayoutCompatible: {
if (!LhsT->isVoidType() && !LhsT->isIncompleteArrayType() &&
Self.RequireCompleteType(Lhs->getTypeLoc().getBeginLoc(), LhsT,
diag::err_incomplete_type))
return true;
if (!RhsT->isVoidType() && !RhsT->isIncompleteArrayType() &&
Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
diag::err_incomplete_type))
return true;
DiagnoseVLAInCXXTypeTrait(
Self, Lhs, tok::kw___builtin_hlsl_is_scalarized_layout_compatible);
DiagnoseVLAInCXXTypeTrait(
Self, Rhs, tok::kw___builtin_hlsl_is_scalarized_layout_compatible);
return Self.HLSL().IsScalarizedLayoutCompatible(LhsT, RhsT);
}
default:
llvm_unreachable("not a BTT");
}
llvm_unreachable("Unknown type trait or not implemented");
}
ExprResult Sema::ActOnArrayTypeTrait(ArrayTypeTrait ATT,
SourceLocation KWLoc,
ParsedType Ty,
Expr* DimExpr,
SourceLocation RParen) {
TypeSourceInfo *TSInfo;
QualType T = GetTypeFromParser(Ty, &TSInfo);
if (!TSInfo)
TSInfo = Context.getTrivialTypeSourceInfo(T);
return BuildArrayTypeTrait(ATT, KWLoc, TSInfo, DimExpr, RParen);
}
static uint64_t EvaluateArrayTypeTrait(Sema &Self, ArrayTypeTrait ATT,
QualType T, Expr *DimExpr,
SourceLocation KeyLoc) {
assert(!T->isDependentType() && "Cannot evaluate traits of dependent type");
switch(ATT) {
case ATT_ArrayRank:
if (T->isArrayType()) {
unsigned Dim = 0;
while (const ArrayType *AT = Self.Context.getAsArrayType(T)) {
++Dim;
T = AT->getElementType();
}
return Dim;
}
return 0;
case ATT_ArrayExtent: {
llvm::APSInt Value;
uint64_t Dim;
if (Self.VerifyIntegerConstantExpression(
DimExpr, &Value, diag::err_dimension_expr_not_constant_integer)
.isInvalid())
return 0;
if (Value.isSigned() && Value.isNegative()) {
Self.Diag(KeyLoc, diag::err_dimension_expr_not_constant_integer)
<< DimExpr->getSourceRange();
return 0;
}
Dim = Value.getLimitedValue();
if (T->isArrayType()) {
unsigned D = 0;
bool Matched = false;
while (const ArrayType *AT = Self.Context.getAsArrayType(T)) {
if (Dim == D) {
Matched = true;
break;
}
++D;
T = AT->getElementType();
}
if (Matched && T->isArrayType()) {
if (const ConstantArrayType *CAT = Self.Context.getAsConstantArrayType(T))
return CAT->getLimitedSize();
}
}
return 0;
}
}
llvm_unreachable("Unknown type trait or not implemented");
}
ExprResult Sema::BuildArrayTypeTrait(ArrayTypeTrait ATT,
SourceLocation KWLoc,
TypeSourceInfo *TSInfo,
Expr* DimExpr,
SourceLocation RParen) {
QualType T = TSInfo->getType();
// FIXME: This should likely be tracked as an APInt to remove any host
// assumptions about the width of size_t on the target.
uint64_t Value = 0;
if (!T->isDependentType())
Value = EvaluateArrayTypeTrait(*this, ATT, T, DimExpr, KWLoc);
// While the specification for these traits from the Embarcadero C++
// compiler's documentation says the return type is 'unsigned int', Clang
// returns 'size_t'. On Windows, the primary platform for the Embarcadero
// compiler, there is no difference. On several other platforms this is an
// important distinction.
return new (Context) ArrayTypeTraitExpr(KWLoc, ATT, TSInfo, Value, DimExpr,
RParen, Context.getSizeType());
}
ExprResult Sema::ActOnExpressionTrait(ExpressionTrait ET,
SourceLocation KWLoc,
Expr *Queried,
SourceLocation RParen) {
// If error parsing the expression, ignore.
if (!Queried)
return ExprError();
ExprResult Result = BuildExpressionTrait(ET, KWLoc, Queried, RParen);
return Result;
}
static bool EvaluateExpressionTrait(ExpressionTrait ET, Expr *E) {
switch (ET) {
case ET_IsLValueExpr: return E->isLValue();
case ET_IsRValueExpr:
return E->isPRValue();
}
llvm_unreachable("Expression trait not covered by switch");
}
ExprResult Sema::BuildExpressionTrait(ExpressionTrait ET,
SourceLocation KWLoc,
Expr *Queried,
SourceLocation RParen) {
if (Queried->isTypeDependent()) {
// Delay type-checking for type-dependent expressions.
} else if (Queried->hasPlaceholderType()) {
ExprResult PE = CheckPlaceholderExpr(Queried);
if (PE.isInvalid()) return ExprError();
return BuildExpressionTrait(ET, KWLoc, PE.get(), RParen);
}
bool Value = EvaluateExpressionTrait(ET, Queried);
return new (Context)
ExpressionTraitExpr(KWLoc, ET, Queried, Value, RParen, Context.BoolTy);
}
QualType Sema::CheckPointerToMemberOperands(ExprResult &LHS, ExprResult &RHS,
ExprValueKind &VK,
SourceLocation Loc,
bool isIndirect) {
assert(!LHS.get()->hasPlaceholderType() && !RHS.get()->hasPlaceholderType() &&
"placeholders should have been weeded out by now");
// The LHS undergoes lvalue conversions if this is ->*, and undergoes the
// temporary materialization conversion otherwise.
if (isIndirect)
LHS = DefaultLvalueConversion(LHS.get());
else if (LHS.get()->isPRValue())
LHS = TemporaryMaterializationConversion(LHS.get());
if (LHS.isInvalid())
return QualType();
// The RHS always undergoes lvalue conversions.
RHS = DefaultLvalueConversion(RHS.get());
if (RHS.isInvalid()) return QualType();
const char *OpSpelling = isIndirect ? "->*" : ".*";
// C++ 5.5p2
// The binary operator .* [p3: ->*] binds its second operand, which shall
// be of type "pointer to member of T" (where T is a completely-defined
// class type) [...]
QualType RHSType = RHS.get()->getType();
const MemberPointerType *MemPtr = RHSType->getAs<MemberPointerType>();
if (!MemPtr) {
Diag(Loc, diag::err_bad_memptr_rhs)
<< OpSpelling << RHSType << RHS.get()->getSourceRange();
return QualType();
}
CXXRecordDecl *RHSClass = MemPtr->getMostRecentCXXRecordDecl();
// Note: C++ [expr.mptr.oper]p2-3 says that the class type into which the
// member pointer points must be completely-defined. However, there is no
// reason for this semantic distinction, and the rule is not enforced by
// other compilers. Therefore, we do not check this property, as it is
// likely to be considered a defect.
// C++ 5.5p2
// [...] to its first operand, which shall be of class T or of a class of
// which T is an unambiguous and accessible base class. [p3: a pointer to
// such a class]
QualType LHSType = LHS.get()->getType();
if (isIndirect) {
if (const PointerType *Ptr = LHSType->getAs<PointerType>())
LHSType = Ptr->getPointeeType();
else {
Diag(Loc, diag::err_bad_memptr_lhs)
<< OpSpelling << 1 << LHSType
<< FixItHint::CreateReplacement(SourceRange(Loc), ".*");
return QualType();
}
}
CXXRecordDecl *LHSClass = LHSType->getAsCXXRecordDecl();
if (!declaresSameEntity(LHSClass, RHSClass)) {
// If we want to check the hierarchy, we need a complete type.
if (RequireCompleteType(Loc, LHSType, diag::err_bad_memptr_lhs,
OpSpelling, (int)isIndirect)) {
return QualType();
}
if (!IsDerivedFrom(Loc, LHSClass, RHSClass)) {
Diag(Loc, diag::err_bad_memptr_lhs) << OpSpelling
<< (int)isIndirect << LHS.get()->getType();
return QualType();
}
CXXCastPath BasePath;
if (CheckDerivedToBaseConversion(
LHSType, QualType(RHSClass->getTypeForDecl(), 0), Loc,
SourceRange(LHS.get()->getBeginLoc(), RHS.get()->getEndLoc()),
&BasePath))
return QualType();
// Cast LHS to type of use.
QualType UseType = Context.getQualifiedType(RHSClass->getTypeForDecl(),
LHSType.getQualifiers());
if (isIndirect)
UseType = Context.getPointerType(UseType);
ExprValueKind VK = isIndirect ? VK_PRValue : LHS.get()->getValueKind();
LHS = ImpCastExprToType(LHS.get(), UseType, CK_DerivedToBase, VK,
&BasePath);
}
if (isa<CXXScalarValueInitExpr>(RHS.get()->IgnoreParens())) {
// Diagnose use of pointer-to-member type which when used as
// the functional cast in a pointer-to-member expression.
Diag(Loc, diag::err_pointer_to_member_type) << isIndirect;
return QualType();
}
// C++ 5.5p2
// The result is an object or a function of the type specified by the
// second operand.
// The cv qualifiers are the union of those in the pointer and the left side,
// in accordance with 5.5p5 and 5.2.5.
QualType Result = MemPtr->getPointeeType();
Result = Context.getCVRQualifiedType(Result, LHSType.getCVRQualifiers());
// C++0x [expr.mptr.oper]p6:
// In a .* expression whose object expression is an rvalue, the program is
// ill-formed if the second operand is a pointer to member function with
// ref-qualifier &. In a ->* expression or in a .* expression whose object
// expression is an lvalue, the program is ill-formed if the second operand
// is a pointer to member function with ref-qualifier &&.
if (const FunctionProtoType *Proto = Result->getAs<FunctionProtoType>()) {
switch (Proto->getRefQualifier()) {
case RQ_None:
// Do nothing
break;
case RQ_LValue:
if (!isIndirect && !LHS.get()->Classify(Context).isLValue()) {
// C++2a allows functions with ref-qualifier & if their cv-qualifier-seq
// is (exactly) 'const'.
if (Proto->isConst() && !Proto->isVolatile())
Diag(Loc, getLangOpts().CPlusPlus20
? diag::warn_cxx17_compat_pointer_to_const_ref_member_on_rvalue
: diag::ext_pointer_to_const_ref_member_on_rvalue);
else
Diag(Loc, diag::err_pointer_to_member_oper_value_classify)
<< RHSType << 1 << LHS.get()->getSourceRange();
}
break;
case RQ_RValue:
if (isIndirect || !LHS.get()->Classify(Context).isRValue())
Diag(Loc, diag::err_pointer_to_member_oper_value_classify)
<< RHSType << 0 << LHS.get()->getSourceRange();
break;
}
}
// C++ [expr.mptr.oper]p6:
// The result of a .* expression whose second operand is a pointer
// to a data member is of the same value category as its
// first operand. The result of a .* expression whose second
// operand is a pointer to a member function is a prvalue. The
// result of an ->* expression is an lvalue if its second operand
// is a pointer to data member and a prvalue otherwise.
if (Result->isFunctionType()) {
VK = VK_PRValue;
return Context.BoundMemberTy;
} else if (isIndirect) {
VK = VK_LValue;
} else {
VK = LHS.get()->getValueKind();
}
return Result;
}
/// Try to convert a type to another according to C++11 5.16p3.
///
/// This is part of the parameter validation for the ? operator. If either
/// value operand is a class type, the two operands are attempted to be
/// converted to each other. This function does the conversion in one direction.
/// It returns true if the program is ill-formed and has already been diagnosed
/// as such.
static bool TryClassUnification(Sema &Self, Expr *From, Expr *To,
SourceLocation QuestionLoc,
bool &HaveConversion,
QualType &ToType) {
HaveConversion = false;
ToType = To->getType();
InitializationKind Kind =
InitializationKind::CreateCopy(To->getBeginLoc(), SourceLocation());
// C++11 5.16p3
// The process for determining whether an operand expression E1 of type T1
// can be converted to match an operand expression E2 of type T2 is defined
// as follows:
// -- If E2 is an lvalue: E1 can be converted to match E2 if E1 can be
// implicitly converted to type "lvalue reference to T2", subject to the
// constraint that in the conversion the reference must bind directly to
// an lvalue.
// -- If E2 is an xvalue: E1 can be converted to match E2 if E1 can be
// implicitly converted to the type "rvalue reference to R2", subject to
// the constraint that the reference must bind directly.
if (To->isGLValue()) {
QualType T = Self.Context.getReferenceQualifiedType(To);
InitializedEntity Entity = InitializedEntity::InitializeTemporary(T);
InitializationSequence InitSeq(Self, Entity, Kind, From);
if (InitSeq.isDirectReferenceBinding()) {
ToType = T;
HaveConversion = true;
return false;
}
if (InitSeq.isAmbiguous())
return InitSeq.Diagnose(Self, Entity, Kind, From);
}
// -- If E2 is an rvalue, or if the conversion above cannot be done:
// -- if E1 and E2 have class type, and the underlying class types are
// the same or one is a base class of the other:
QualType FTy = From->getType();
QualType TTy = To->getType();
const RecordType *FRec = FTy->getAs<RecordType>();
const RecordType *TRec = TTy->getAs<RecordType>();
bool FDerivedFromT = FRec && TRec && FRec != TRec &&
Self.IsDerivedFrom(QuestionLoc, FTy, TTy);
if (FRec && TRec && (FRec == TRec || FDerivedFromT ||
Self.IsDerivedFrom(QuestionLoc, TTy, FTy))) {
// E1 can be converted to match E2 if the class of T2 is the
// same type as, or a base class of, the class of T1, and
// [cv2 > cv1].
if (FRec == TRec || FDerivedFromT) {
if (TTy.isAtLeastAsQualifiedAs(FTy, Self.getASTContext())) {
InitializedEntity Entity = InitializedEntity::InitializeTemporary(TTy);
InitializationSequence InitSeq(Self, Entity, Kind, From);
if (InitSeq) {
HaveConversion = true;
return false;
}
if (InitSeq.isAmbiguous())
return InitSeq.Diagnose(Self, Entity, Kind, From);
}
}
return false;
}
// -- Otherwise: E1 can be converted to match E2 if E1 can be
// implicitly converted to the type that expression E2 would have
// if E2 were converted to an rvalue (or the type it has, if E2 is
// an rvalue).
//
// This actually refers very narrowly to the lvalue-to-rvalue conversion, not
// to the array-to-pointer or function-to-pointer conversions.
TTy = TTy.getNonLValueExprType(Self.Context);
InitializedEntity Entity = InitializedEntity::InitializeTemporary(TTy);
InitializationSequence InitSeq(Self, Entity, Kind, From);
HaveConversion = !InitSeq.Failed();
ToType = TTy;
if (InitSeq.isAmbiguous())
return InitSeq.Diagnose(Self, Entity, Kind, From);
return false;
}
/// Try to find a common type for two according to C++0x 5.16p5.
///
/// This is part of the parameter validation for the ? operator. If either
/// value operand is a class type, overload resolution is used to find a
/// conversion to a common type.
static bool FindConditionalOverload(Sema &Self, ExprResult &LHS, ExprResult &RHS,
SourceLocation QuestionLoc) {
Expr *Args[2] = { LHS.get(), RHS.get() };
OverloadCandidateSet CandidateSet(QuestionLoc,
OverloadCandidateSet::CSK_Operator);
Self.AddBuiltinOperatorCandidates(OO_Conditional, QuestionLoc, Args,
CandidateSet);
OverloadCandidateSet::iterator Best;
switch (CandidateSet.BestViableFunction(Self, QuestionLoc, Best)) {
case OR_Success: {
// We found a match. Perform the conversions on the arguments and move on.
ExprResult LHSRes = Self.PerformImplicitConversion(
LHS.get(), Best->BuiltinParamTypes[0], Best->Conversions[0],
AssignmentAction::Converting);
if (LHSRes.isInvalid())
break;
LHS = LHSRes;
ExprResult RHSRes = Self.PerformImplicitConversion(
RHS.get(), Best->BuiltinParamTypes[1], Best->Conversions[1],
AssignmentAction::Converting);
if (RHSRes.isInvalid())
break;
RHS = RHSRes;
if (Best->Function)
Self.MarkFunctionReferenced(QuestionLoc, Best->Function);
return false;
}
case OR_No_Viable_Function:
// Emit a better diagnostic if one of the expressions is a null pointer
// constant and the other is a pointer type. In this case, the user most
// likely forgot to take the address of the other expression.
if (Self.DiagnoseConditionalForNull(LHS.get(), RHS.get(), QuestionLoc))
return true;
Self.Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands)
<< LHS.get()->getType() << RHS.get()->getType()
<< LHS.get()->getSourceRange() << RHS.get()->getSourceRange();
return true;
case OR_Ambiguous:
Self.Diag(QuestionLoc, diag::err_conditional_ambiguous_ovl)
<< LHS.get()->getType() << RHS.get()->getType()
<< LHS.get()->getSourceRange() << RHS.get()->getSourceRange();
// FIXME: Print the possible common types by printing the return types of
// the viable candidates.
break;
case OR_Deleted:
llvm_unreachable("Conditional operator has only built-in overloads");
}
return true;
}
/// Perform an "extended" implicit conversion as returned by
/// TryClassUnification.
static bool ConvertForConditional(Sema &Self, ExprResult &E, QualType T) {
InitializedEntity Entity = InitializedEntity::InitializeTemporary(T);
InitializationKind Kind =
InitializationKind::CreateCopy(E.get()->getBeginLoc(), SourceLocation());
Expr *Arg = E.get();
InitializationSequence InitSeq(Self, Entity, Kind, Arg);
ExprResult Result = InitSeq.Perform(Self, Entity, Kind, Arg);
if (Result.isInvalid())
return true;
E = Result;
return false;
}
// Check the condition operand of ?: to see if it is valid for the GCC
// extension.
static bool isValidVectorForConditionalCondition(ASTContext &Ctx,
QualType CondTy) {
if (!CondTy->isVectorType() && !CondTy->isExtVectorType())
return false;
const QualType EltTy =
cast<VectorType>(CondTy.getCanonicalType())->getElementType();
assert(!EltTy->isEnumeralType() && "Vectors cant be enum types");
return EltTy->isIntegralType(Ctx);
}
static bool isValidSizelessVectorForConditionalCondition(ASTContext &Ctx,
QualType CondTy) {
if (!CondTy->isSveVLSBuiltinType())
return false;
const QualType EltTy =
cast<BuiltinType>(CondTy.getCanonicalType())->getSveEltType(Ctx);
assert(!EltTy->isEnumeralType() && "Vectors cant be enum types");
return EltTy->isIntegralType(Ctx);
}
QualType Sema::CheckVectorConditionalTypes(ExprResult &Cond, ExprResult &LHS,
ExprResult &RHS,
SourceLocation QuestionLoc) {
LHS = DefaultFunctionArrayLvalueConversion(LHS.get());
RHS = DefaultFunctionArrayLvalueConversion(RHS.get());
QualType CondType = Cond.get()->getType();
const auto *CondVT = CondType->castAs<VectorType>();
QualType CondElementTy = CondVT->getElementType();
unsigned CondElementCount = CondVT->getNumElements();
QualType LHSType = LHS.get()->getType();
const auto *LHSVT = LHSType->getAs<VectorType>();
QualType RHSType = RHS.get()->getType();
const auto *RHSVT = RHSType->getAs<VectorType>();
QualType ResultType;
if (LHSVT && RHSVT) {
if (isa<ExtVectorType>(CondVT) != isa<ExtVectorType>(LHSVT)) {
Diag(QuestionLoc, diag::err_conditional_vector_cond_result_mismatch)
<< /*isExtVector*/ isa<ExtVectorType>(CondVT);
return {};
}
// If both are vector types, they must be the same type.
if (!Context.hasSameType(LHSType, RHSType)) {
Diag(QuestionLoc, diag::err_conditional_vector_mismatched)
<< LHSType << RHSType;
return {};
}
ResultType = Context.getCommonSugaredType(LHSType, RHSType);
} else if (LHSVT || RHSVT) {
ResultType = CheckVectorOperands(
LHS, RHS, QuestionLoc, /*isCompAssign*/ false, /*AllowBothBool*/ true,
/*AllowBoolConversions*/ false,
/*AllowBoolOperation*/ true,
/*ReportInvalid*/ true);
if (ResultType.isNull())
return {};
} else {
// Both are scalar.
LHSType = LHSType.getUnqualifiedType();
RHSType = RHSType.getUnqualifiedType();
QualType ResultElementTy =
Context.hasSameType(LHSType, RHSType)
? Context.getCommonSugaredType(LHSType, RHSType)
: UsualArithmeticConversions(LHS, RHS, QuestionLoc,
ACK_Conditional);
if (ResultElementTy->isEnumeralType()) {
Diag(QuestionLoc, diag::err_conditional_vector_operand_type)
<< ResultElementTy;
return {};
}
if (CondType->isExtVectorType())
ResultType =
Context.getExtVectorType(ResultElementTy, CondVT->getNumElements());
else
ResultType = Context.getVectorType(
ResultElementTy, CondVT->getNumElements(), VectorKind::Generic);
LHS = ImpCastExprToType(LHS.get(), ResultType, CK_VectorSplat);
RHS = ImpCastExprToType(RHS.get(), ResultType, CK_VectorSplat);
}
assert(!ResultType.isNull() && ResultType->isVectorType() &&
(!CondType->isExtVectorType() || ResultType->isExtVectorType()) &&
"Result should have been a vector type");
auto *ResultVectorTy = ResultType->castAs<VectorType>();
QualType ResultElementTy = ResultVectorTy->getElementType();
unsigned ResultElementCount = ResultVectorTy->getNumElements();
if (ResultElementCount != CondElementCount) {
Diag(QuestionLoc, diag::err_conditional_vector_size) << CondType
<< ResultType;
return {};
}
if (Context.getTypeSize(ResultElementTy) !=
Context.getTypeSize(CondElementTy)) {
Diag(QuestionLoc, diag::err_conditional_vector_element_size) << CondType
<< ResultType;
return {};
}
return ResultType;
}
QualType Sema::CheckSizelessVectorConditionalTypes(ExprResult &Cond,
ExprResult &LHS,
ExprResult &RHS,
SourceLocation QuestionLoc) {
LHS = DefaultFunctionArrayLvalueConversion(LHS.get());
RHS = DefaultFunctionArrayLvalueConversion(RHS.get());
QualType CondType = Cond.get()->getType();
const auto *CondBT = CondType->castAs<BuiltinType>();
QualType CondElementTy = CondBT->getSveEltType(Context);
llvm::ElementCount CondElementCount =
Context.getBuiltinVectorTypeInfo(CondBT).EC;
QualType LHSType = LHS.get()->getType();
const auto *LHSBT =
LHSType->isSveVLSBuiltinType() ? LHSType->getAs<BuiltinType>() : nullptr;
QualType RHSType = RHS.get()->getType();
const auto *RHSBT =
RHSType->isSveVLSBuiltinType() ? RHSType->getAs<BuiltinType>() : nullptr;
QualType ResultType;
if (LHSBT && RHSBT) {
// If both are sizeless vector types, they must be the same type.
if (!Context.hasSameType(LHSType, RHSType)) {
Diag(QuestionLoc, diag::err_conditional_vector_mismatched)
<< LHSType << RHSType;
return QualType();
}
ResultType = LHSType;
} else if (LHSBT || RHSBT) {
ResultType = CheckSizelessVectorOperands(
LHS, RHS, QuestionLoc, /*IsCompAssign*/ false, ACK_Conditional);
if (ResultType.isNull())
return QualType();
} else {
// Both are scalar so splat
QualType ResultElementTy;
LHSType = LHSType.getCanonicalType().getUnqualifiedType();
RHSType = RHSType.getCanonicalType().getUnqualifiedType();
if (Context.hasSameType(LHSType, RHSType))
ResultElementTy = LHSType;
else
ResultElementTy =
UsualArithmeticConversions(LHS, RHS, QuestionLoc, ACK_Conditional);
if (ResultElementTy->isEnumeralType()) {
Diag(QuestionLoc, diag::err_conditional_vector_operand_type)
<< ResultElementTy;
return QualType();
}
ResultType = Context.getScalableVectorType(
ResultElementTy, CondElementCount.getKnownMinValue());
LHS = ImpCastExprToType(LHS.get(), ResultType, CK_VectorSplat);
RHS = ImpCastExprToType(RHS.get(), ResultType, CK_VectorSplat);
}
assert(!ResultType.isNull() && ResultType->isSveVLSBuiltinType() &&
"Result should have been a vector type");
auto *ResultBuiltinTy = ResultType->castAs<BuiltinType>();
QualType ResultElementTy = ResultBuiltinTy->getSveEltType(Context);
llvm::ElementCount ResultElementCount =
Context.getBuiltinVectorTypeInfo(ResultBuiltinTy).EC;
if (ResultElementCount != CondElementCount) {
Diag(QuestionLoc, diag::err_conditional_vector_size)
<< CondType << ResultType;
return QualType();
}
if (Context.getTypeSize(ResultElementTy) !=
Context.getTypeSize(CondElementTy)) {
Diag(QuestionLoc, diag::err_conditional_vector_element_size)
<< CondType << ResultType;
return QualType();
}
return ResultType;
}
QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
ExprResult &RHS, ExprValueKind &VK,
ExprObjectKind &OK,
SourceLocation QuestionLoc) {
// FIXME: Handle C99's complex types, block pointers and Obj-C++ interface
// pointers.
// Assume r-value.
VK = VK_PRValue;
OK = OK_Ordinary;
bool IsVectorConditional =
isValidVectorForConditionalCondition(Context, Cond.get()->getType());
bool IsSizelessVectorConditional =
isValidSizelessVectorForConditionalCondition(Context,
Cond.get()->getType());
// C++11 [expr.cond]p1
// The first expression is contextually converted to bool.
if (!Cond.get()->isTypeDependent()) {
ExprResult CondRes = IsVectorConditional || IsSizelessVectorConditional
? DefaultFunctionArrayLvalueConversion(Cond.get())
: CheckCXXBooleanCondition(Cond.get());
if (CondRes.isInvalid())
return QualType();
Cond = CondRes;
} else {
// To implement C++, the first expression typically doesn't alter the result
// type of the conditional, however the GCC compatible vector extension
// changes the result type to be that of the conditional. Since we cannot
// know if this is a vector extension here, delay the conversion of the
// LHS/RHS below until later.
return Context.DependentTy;
}
// Either of the arguments dependent?
if (LHS.get()->isTypeDependent() || RHS.get()->isTypeDependent())
return Context.DependentTy;
// C++11 [expr.cond]p2
// If either the second or the third operand has type (cv) void, ...
QualType LTy = LHS.get()->getType();
QualType RTy = RHS.get()->getType();
bool LVoid = LTy->isVoidType();
bool RVoid = RTy->isVoidType();
if (LVoid || RVoid) {
// ... one of the following shall hold:
// -- The second or the third operand (but not both) is a (possibly
// parenthesized) throw-expression; the result is of the type
// and value category of the other.
bool LThrow = isa<CXXThrowExpr>(LHS.get()->IgnoreParenImpCasts());
bool RThrow = isa<CXXThrowExpr>(RHS.get()->IgnoreParenImpCasts());
// Void expressions aren't legal in the vector-conditional expressions.
if (IsVectorConditional) {
SourceRange DiagLoc =
LVoid ? LHS.get()->getSourceRange() : RHS.get()->getSourceRange();
bool IsThrow = LVoid ? LThrow : RThrow;
Diag(DiagLoc.getBegin(), diag::err_conditional_vector_has_void)
<< DiagLoc << IsThrow;
return QualType();
}
if (LThrow != RThrow) {
Expr *NonThrow = LThrow ? RHS.get() : LHS.get();
VK = NonThrow->getValueKind();
// DR (no number yet): the result is a bit-field if the
// non-throw-expression operand is a bit-field.
OK = NonThrow->getObjectKind();
return NonThrow->getType();
}
// -- Both the second and third operands have type void; the result is of
// type void and is a prvalue.
if (LVoid && RVoid)
return Context.getCommonSugaredType(LTy, RTy);
// Neither holds, error.
Diag(QuestionLoc, diag::err_conditional_void_nonvoid)
<< (LVoid ? RTy : LTy) << (LVoid ? 0 : 1)
<< LHS.get()->getSourceRange() << RHS.get()->getSourceRange();
return QualType();
}
// Neither is void.
if (IsVectorConditional)
return CheckVectorConditionalTypes(Cond, LHS, RHS, QuestionLoc);
if (IsSizelessVectorConditional)
return CheckSizelessVectorConditionalTypes(Cond, LHS, RHS, QuestionLoc);
// WebAssembly tables are not allowed as conditional LHS or RHS.
if (LTy->isWebAssemblyTableType() || RTy->isWebAssemblyTableType()) {
Diag(QuestionLoc, diag::err_wasm_table_conditional_expression)
<< LHS.get()->getSourceRange() << RHS.get()->getSourceRange();
return QualType();
}
// C++11 [expr.cond]p3
// Otherwise, if the second and third operand have different types, and
// either has (cv) class type [...] an attempt is made to convert each of
// those operands to the type of the other.
if (!Context.hasSameType(LTy, RTy) &&
(LTy->isRecordType() || RTy->isRecordType())) {
// These return true if a single direction is already ambiguous.
QualType L2RType, R2LType;
bool HaveL2R, HaveR2L;
if (TryClassUnification(*this, LHS.get(), RHS.get(), QuestionLoc, HaveL2R, L2RType))
return QualType();
if (TryClassUnification(*this, RHS.get(), LHS.get(), QuestionLoc, HaveR2L, R2LType))
return QualType();
// If both can be converted, [...] the program is ill-formed.
if (HaveL2R && HaveR2L) {
Diag(QuestionLoc, diag::err_conditional_ambiguous)
<< LTy << RTy << LHS.get()->getSourceRange() << RHS.get()->getSourceRange();
return QualType();
}
// If exactly one conversion is possible, that conversion is applied to
// the chosen operand and the converted operands are used in place of the
// original operands for the remainder of this section.
if (HaveL2R) {
if (ConvertForConditional(*this, LHS, L2RType) || LHS.isInvalid())
return QualType();
LTy = LHS.get()->getType();
} else if (HaveR2L) {
if (ConvertForConditional(*this, RHS, R2LType) || RHS.isInvalid())
return QualType();
RTy = RHS.get()->getType();
}
}
// C++11 [expr.cond]p3
// if both are glvalues of the same value category and the same type except
// for cv-qualification, an attempt is made to convert each of those
// operands to the type of the other.
// FIXME:
// Resolving a defect in P0012R1: we extend this to cover all cases where
// one of the operands is reference-compatible with the other, in order
// to support conditionals between functions differing in noexcept. This
// will similarly cover difference in array bounds after P0388R4.
// FIXME: If LTy and RTy have a composite pointer type, should we convert to
// that instead?
ExprValueKind LVK = LHS.get()->getValueKind();
ExprValueKind RVK = RHS.get()->getValueKind();
if (!Context.hasSameType(LTy, RTy) && LVK == RVK && LVK != VK_PRValue) {
// DerivedToBase was already handled by the class-specific case above.
// FIXME: Should we allow ObjC conversions here?
const ReferenceConversions AllowedConversions =
ReferenceConversions::Qualification |
ReferenceConversions::NestedQualification |
ReferenceConversions::Function;
ReferenceConversions RefConv;
if (CompareReferenceRelationship(QuestionLoc, LTy, RTy, &RefConv) ==
Ref_Compatible &&
!(RefConv & ~AllowedConversions) &&
// [...] subject to the constraint that the reference must bind
// directly [...]
!RHS.get()->refersToBitField() && !RHS.get()->refersToVectorElement()) {
RHS = ImpCastExprToType(RHS.get(), LTy, CK_NoOp, RVK);
RTy = RHS.get()->getType();
} else if (CompareReferenceRelationship(QuestionLoc, RTy, LTy, &RefConv) ==
Ref_Compatible &&
!(RefConv & ~AllowedConversions) &&
!LHS.get()->refersToBitField() &&
!LHS.get()->refersToVectorElement()) {
LHS = ImpCastExprToType(LHS.get(), RTy, CK_NoOp, LVK);
LTy = LHS.get()->getType();
}
}
// C++11 [expr.cond]p4
// If the second and third operands are glvalues of the same value
// category and have the same type, the result is of that type and
// value category and it is a bit-field if the second or the third
// operand is a bit-field, or if both are bit-fields.
// We only extend this to bitfields, not to the crazy other kinds of
// l-values.
bool Same = Context.hasSameType(LTy, RTy);
if (Same && LVK == RVK && LVK != VK_PRValue &&
LHS.get()->isOrdinaryOrBitFieldObject() &&
RHS.get()->isOrdinaryOrBitFieldObject()) {
VK = LHS.get()->getValueKind();
if (LHS.get()->getObjectKind() == OK_BitField ||
RHS.get()->getObjectKind() == OK_BitField)
OK = OK_BitField;
return Context.getCommonSugaredType(LTy, RTy);
}
// C++11 [expr.cond]p5
// Otherwise, the result is a prvalue. If the second and third operands
// do not have the same type, and either has (cv) class type, ...
if (!Same && (LTy->isRecordType() || RTy->isRecordType())) {
// ... overload resolution is used to determine the conversions (if any)
// to be applied to the operands. If the overload resolution fails, the
// program is ill-formed.
if (FindConditionalOverload(*this, LHS, RHS, QuestionLoc))
return QualType();
}
// C++11 [expr.cond]p6
// Lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard
// conversions are performed on the second and third operands.
LHS = DefaultFunctionArrayLvalueConversion(LHS.get());
RHS = DefaultFunctionArrayLvalueConversion(RHS.get());
if (LHS.isInvalid() || RHS.isInvalid())
return QualType();
LTy = LHS.get()->getType();
RTy = RHS.get()->getType();
// After those conversions, one of the following shall hold:
// -- The second and third operands have the same type; the result
// is of that type. If the operands have class type, the result
// is a prvalue temporary of the result type, which is
// copy-initialized from either the second operand or the third
// operand depending on the value of the first operand.
if (Context.hasSameType(LTy, RTy)) {
if (LTy->isRecordType()) {
// The operands have class type. Make a temporary copy.
ExprResult LHSCopy = PerformCopyInitialization(
InitializedEntity::InitializeTemporary(LTy), SourceLocation(), LHS);
if (LHSCopy.isInvalid())
return QualType();
ExprResult RHSCopy = PerformCopyInitialization(
InitializedEntity::InitializeTemporary(RTy), SourceLocation(), RHS);
if (RHSCopy.isInvalid())
return QualType();
LHS = LHSCopy;
RHS = RHSCopy;
}
return Context.getCommonSugaredType(LTy, RTy);
}
// Extension: conditional operator involving vector types.
if (LTy->isVectorType() || RTy->isVectorType())
return CheckVectorOperands(LHS, RHS, QuestionLoc, /*isCompAssign*/ false,
/*AllowBothBool*/ true,
/*AllowBoolConversions*/ false,
/*AllowBoolOperation*/ false,
/*ReportInvalid*/ true);
// -- The second and third operands have arithmetic or enumeration type;
// the usual arithmetic conversions are performed to bring them to a
// common type, and the result is of that type.
if (LTy->isArithmeticType() && RTy->isArithmeticType()) {
QualType ResTy =
UsualArithmeticConversions(LHS, RHS, QuestionLoc, ACK_Conditional);
if (LHS.isInvalid() || RHS.isInvalid())
return QualType();
if (ResTy.isNull()) {
Diag(QuestionLoc,
diag::err_typecheck_cond_incompatible_operands) << LTy << RTy
<< LHS.get()->getSourceRange() << RHS.get()->getSourceRange();
return QualType();
}
LHS = ImpCastExprToType(LHS.get(), ResTy, PrepareScalarCast(LHS, ResTy));
RHS = ImpCastExprToType(RHS.get(), ResTy, PrepareScalarCast(RHS, ResTy));
return ResTy;
}
// -- The second and third operands have pointer type, or one has pointer
// type and the other is a null pointer constant, or both are null
// pointer constants, at least one of which is non-integral; pointer
// conversions and qualification conversions are performed to bring them
// to their composite pointer type. The result is of the composite
// pointer type.
// -- The second and third operands have pointer to member type, or one has
// pointer to member type and the other is a null pointer constant;
// pointer to member conversions and qualification conversions are
// performed to bring them to a common type, whose cv-qualification
// shall match the cv-qualification of either the second or the third
// operand. The result is of the common type.
QualType Composite = FindCompositePointerType(QuestionLoc, LHS, RHS);
if (!Composite.isNull())
return Composite;
// Similarly, attempt to find composite type of two objective-c pointers.
Composite = ObjC().FindCompositeObjCPointerType(LHS, RHS, QuestionLoc);
if (LHS.isInvalid() || RHS.isInvalid())
return QualType();
if (!Composite.isNull())
return Composite;
// Check if we are using a null with a non-pointer type.
if (DiagnoseConditionalForNull(LHS.get(), RHS.get(), QuestionLoc))
return QualType();
Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands)
<< LHS.get()->getType() << RHS.get()->getType()
<< LHS.get()->getSourceRange() << RHS.get()->getSourceRange();
return QualType();
}
QualType Sema::FindCompositePointerType(SourceLocation Loc,
Expr *&E1, Expr *&E2,
bool ConvertArgs) {
assert(getLangOpts().CPlusPlus && "This function assumes C++");
// C++1z [expr]p14:
// The composite pointer type of two operands p1 and p2 having types T1
// and T2
QualType T1 = E1->getType(), T2 = E2->getType();
// where at least one is a pointer or pointer to member type or
// std::nullptr_t is:
bool T1IsPointerLike = T1->isAnyPointerType() || T1->isMemberPointerType() ||
T1->isNullPtrType();
bool T2IsPointerLike = T2->isAnyPointerType() || T2->isMemberPointerType() ||
T2->isNullPtrType();
if (!T1IsPointerLike && !T2IsPointerLike)
return QualType();
// - if both p1 and p2 are null pointer constants, std::nullptr_t;
// This can't actually happen, following the standard, but we also use this
// to implement the end of [expr.conv], which hits this case.
//
// - if either p1 or p2 is a null pointer constant, T2 or T1, respectively;
if (T1IsPointerLike &&
E2->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) {
if (ConvertArgs)
E2 = ImpCastExprToType(E2, T1, T1->isMemberPointerType()
? CK_NullToMemberPointer
: CK_NullToPointer).get();
return T1;
}
if (T2IsPointerLike &&
E1->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)) {
if (ConvertArgs)
E1 = ImpCastExprToType(E1, T2, T2->isMemberPointerType()
? CK_NullToMemberPointer
: CK_NullToPointer).get();
return T2;
}
// Now both have to be pointers or member pointers.
if (!T1IsPointerLike || !T2IsPointerLike)
return QualType();
assert(!T1->isNullPtrType() && !T2->isNullPtrType() &&
"nullptr_t should be a null pointer constant");
struct Step {
enum Kind { Pointer, ObjCPointer, MemberPointer, Array } K;
// Qualifiers to apply under the step kind.
Qualifiers Quals;
/// The class for a pointer-to-member; a constant array type with a bound
/// (if any) for an array.
/// FIXME: Store Qualifier for pointer-to-member.
const Type *ClassOrBound;
Step(Kind K, const Type *ClassOrBound = nullptr)
: K(K), ClassOrBound(ClassOrBound) {}
QualType rebuild(ASTContext &Ctx, QualType T) const {
T = Ctx.getQualifiedType(T, Quals);
switch (K) {
case Pointer:
return Ctx.getPointerType(T);
case MemberPointer:
return Ctx.getMemberPointerType(T, /*Qualifier=*/nullptr,
ClassOrBound->getAsCXXRecordDecl());
case ObjCPointer:
return Ctx.getObjCObjectPointerType(T);
case Array:
if (auto *CAT = cast_or_null<ConstantArrayType>(ClassOrBound))
return Ctx.getConstantArrayType(T, CAT->getSize(), nullptr,
ArraySizeModifier::Normal, 0);
else
return Ctx.getIncompleteArrayType(T, ArraySizeModifier::Normal, 0);
}
llvm_unreachable("unknown step kind");
}
};
SmallVector<Step, 8> Steps;
// - if T1 is "pointer to cv1 C1" and T2 is "pointer to cv2 C2", where C1
// is reference-related to C2 or C2 is reference-related to C1 (8.6.3),
// the cv-combined type of T1 and T2 or the cv-combined type of T2 and T1,
// respectively;
// - if T1 is "pointer to member of C1 of type cv1 U1" and T2 is "pointer
// to member of C2 of type cv2 U2" for some non-function type U, where
// C1 is reference-related to C2 or C2 is reference-related to C1, the
// cv-combined type of T2 and T1 or the cv-combined type of T1 and T2,
// respectively;
// - if T1 and T2 are similar types (4.5), the cv-combined type of T1 and
// T2;
//
// Dismantle T1 and T2 to simultaneously determine whether they are similar
// and to prepare to form the cv-combined type if so.
QualType Composite1 = T1;
QualType Composite2 = T2;
unsigned NeedConstBefore = 0;
while (true) {
assert(!Composite1.isNull() && !Composite2.isNull());
Qualifiers Q1, Q2;
Composite1 = Context.getUnqualifiedArrayType(Composite1, Q1);
Composite2 = Context.getUnqualifiedArrayType(Composite2, Q2);
// Top-level qualifiers are ignored. Merge at all lower levels.
if (!Steps.empty()) {
// Find the qualifier union: (approximately) the unique minimal set of
// qualifiers that is compatible with both types.
Qualifiers Quals = Qualifiers::fromCVRUMask(Q1.getCVRUQualifiers() |
Q2.getCVRUQualifiers());
// Under one level of pointer or pointer-to-member, we can change to an
// unambiguous compatible address space.
if (Q1.getAddressSpace() == Q2.getAddressSpace()) {
Quals.setAddressSpace(Q1.getAddressSpace());
} else if (Steps.size() == 1) {
bool MaybeQ1 = Q1.isAddressSpaceSupersetOf(Q2, getASTContext());
bool MaybeQ2 = Q2.isAddressSpaceSupersetOf(Q1, getASTContext());
if (MaybeQ1 == MaybeQ2) {
// Exception for ptr size address spaces. Should be able to choose
// either address space during comparison.
if (isPtrSizeAddressSpace(Q1.getAddressSpace()) ||
isPtrSizeAddressSpace(Q2.getAddressSpace()))
MaybeQ1 = true;
else
return QualType(); // No unique best address space.
}
Quals.setAddressSpace(MaybeQ1 ? Q1.getAddressSpace()
: Q2.getAddressSpace());
} else {
return QualType();
}
// FIXME: In C, we merge __strong and none to __strong at the top level.
if (Q1.getObjCGCAttr() == Q2.getObjCGCAttr())
Quals.setObjCGCAttr(Q1.getObjCGCAttr());
else if (T1->isVoidPointerType() || T2->isVoidPointerType())
assert(Steps.size() == 1);
else
return QualType();
// Mismatched lifetime qualifiers never compatibly include each other.
if (Q1.getObjCLifetime() == Q2.getObjCLifetime())
Quals.setObjCLifetime(Q1.getObjCLifetime());
else if (T1->isVoidPointerType() || T2->isVoidPointerType())
assert(Steps.size() == 1);
else
return QualType();
Steps.back().Quals = Quals;
if (Q1 != Quals || Q2 != Quals)
NeedConstBefore = Steps.size() - 1;
}
// FIXME: Can we unify the following with UnwrapSimilarTypes?
const ArrayType *Arr1, *Arr2;
if ((Arr1 = Context.getAsArrayType(Composite1)) &&
(Arr2 = Context.getAsArrayType(Composite2))) {
auto *CAT1 = dyn_cast<ConstantArrayType>(Arr1);
auto *CAT2 = dyn_cast<ConstantArrayType>(Arr2);
if (CAT1 && CAT2 && CAT1->getSize() == CAT2->getSize()) {
Composite1 = Arr1->getElementType();
Composite2 = Arr2->getElementType();
Steps.emplace_back(Step::Array, CAT1);
continue;
}
bool IAT1 = isa<IncompleteArrayType>(Arr1);
bool IAT2 = isa<IncompleteArrayType>(Arr2);
if ((IAT1 && IAT2) ||
(getLangOpts().CPlusPlus20 && (IAT1 != IAT2) &&
((bool)CAT1 != (bool)CAT2) &&
(Steps.empty() || Steps.back().K != Step::Array))) {
// In C++20 onwards, we can unify an array of N T with an array of
// a different or unknown bound. But we can't form an array whose
// element type is an array of unknown bound by doing so.
Composite1 = Arr1->getElementType();
Composite2 = Arr2->getElementType();
Steps.emplace_back(Step::Array);
if (CAT1 || CAT2)
NeedConstBefore = Steps.size();
continue;
}
}
const PointerType *Ptr1, *Ptr2;
if ((Ptr1 = Composite1->getAs<PointerType>()) &&
(Ptr2 = Composite2->getAs<PointerType>())) {
Composite1 = Ptr1->getPointeeType();
Composite2 = Ptr2->getPointeeType();
Steps.emplace_back(Step::Pointer);
continue;
}
const ObjCObjectPointerType *ObjPtr1, *ObjPtr2;
if ((ObjPtr1 = Composite1->getAs<ObjCObjectPointerType>()) &&
(ObjPtr2 = Composite2->getAs<ObjCObjectPointerType>())) {
Composite1 = ObjPtr1->getPointeeType();
Composite2 = ObjPtr2->getPointeeType();
Steps.emplace_back(Step::ObjCPointer);
continue;
}
const MemberPointerType *MemPtr1, *MemPtr2;
if ((MemPtr1 = Composite1->getAs<MemberPointerType>()) &&
(MemPtr2 = Composite2->getAs<MemberPointerType>())) {
Composite1 = MemPtr1->getPointeeType();
Composite2 = MemPtr2->getPointeeType();
// At the top level, we can perform a base-to-derived pointer-to-member
// conversion:
//
// - [...] where C1 is reference-related to C2 or C2 is
// reference-related to C1
//
// (Note that the only kinds of reference-relatedness in scope here are
// "same type or derived from".) At any other level, the class must
// exactly match.
CXXRecordDecl *Cls = nullptr,
*Cls1 = MemPtr1->getMostRecentCXXRecordDecl(),
*Cls2 = MemPtr2->getMostRecentCXXRecordDecl();
if (declaresSameEntity(Cls1, Cls2))
Cls = Cls1;
else if (Steps.empty())
Cls = IsDerivedFrom(Loc, Cls1, Cls2) ? Cls1
: IsDerivedFrom(Loc, Cls2, Cls1) ? Cls2
: nullptr;
if (!Cls)
return QualType();
Steps.emplace_back(Step::MemberPointer,
Context.getTypeDeclType(Cls).getTypePtr());
continue;
}
// Special case: at the top level, we can decompose an Objective-C pointer
// and a 'cv void *'. Unify the qualifiers.
if (Steps.empty() && ((Composite1->isVoidPointerType() &&
Composite2->isObjCObjectPointerType()) ||
(Composite1->isObjCObjectPointerType() &&
Composite2->isVoidPointerType()))) {
Composite1 = Composite1->getPointeeType();
Composite2 = Composite2->getPointeeType();
Steps.emplace_back(Step::Pointer);
continue;
}
// FIXME: block pointer types?
// Cannot unwrap any more types.
break;
}
// - if T1 or T2 is "pointer to noexcept function" and the other type is
// "pointer to function", where the function types are otherwise the same,
// "pointer to function";
// - if T1 or T2 is "pointer to member of C1 of type function", the other
// type is "pointer to member of C2 of type noexcept function", and C1
// is reference-related to C2 or C2 is reference-related to C1, where
// the function types are otherwise the same, "pointer to member of C2 of
// type function" or "pointer to member of C1 of type function",
// respectively;
//
// We also support 'noreturn' here, so as a Clang extension we generalize the
// above to:
//
// - [Clang] If T1 and T2 are both of type "pointer to function" or
// "pointer to member function" and the pointee types can be unified
// by a function pointer conversion, that conversion is applied
// before checking the following rules.
//
// We've already unwrapped down to the function types, and we want to merge
// rather than just convert, so do this ourselves rather than calling
// IsFunctionConversion.
//
// FIXME: In order to match the standard wording as closely as possible, we
// currently only do this under a single level of pointers. Ideally, we would
// allow this in general, and set NeedConstBefore to the relevant depth on
// the side(s) where we changed anything. If we permit that, we should also
// consider this conversion when determining type similarity and model it as
// a qualification conversion.
if (Steps.size() == 1) {
if (auto *FPT1 = Composite1->getAs<FunctionProtoType>()) {
if (auto *FPT2 = Composite2->getAs<FunctionProtoType>()) {
FunctionProtoType::ExtProtoInfo EPI1 = FPT1->getExtProtoInfo();
FunctionProtoType::ExtProtoInfo EPI2 = FPT2->getExtProtoInfo();
// The result is noreturn if both operands are.
bool Noreturn =
EPI1.ExtInfo.getNoReturn() && EPI2.ExtInfo.getNoReturn();
EPI1.ExtInfo = EPI1.ExtInfo.withNoReturn(Noreturn);
EPI2.ExtInfo = EPI2.ExtInfo.withNoReturn(Noreturn);
// The result is nothrow if both operands are.
SmallVector<QualType, 8> ExceptionTypeStorage;
EPI1.ExceptionSpec = EPI2.ExceptionSpec = Context.mergeExceptionSpecs(
EPI1.ExceptionSpec, EPI2.ExceptionSpec, ExceptionTypeStorage,
getLangOpts().CPlusPlus17);
Composite1 = Context.getFunctionType(FPT1->getReturnType(),
FPT1->getParamTypes(), EPI1);
Composite2 = Context.getFunctionType(FPT2->getReturnType(),
FPT2->getParamTypes(), EPI2);
}
}
}
// There are some more conversions we can perform under exactly one pointer.
if (Steps.size() == 1 && Steps.front().K == Step::Pointer &&
!Context.hasSameType(Composite1, Composite2)) {
// - if T1 or T2 is "pointer to cv1 void" and the other type is
// "pointer to cv2 T", where T is an object type or void,
// "pointer to cv12 void", where cv12 is the union of cv1 and cv2;
if (Composite1->isVoidType() && Composite2->isObjectType())
Composite2 = Composite1;
else if (Composite2->isVoidType() && Composite1->isObjectType())
Composite1 = Composite2;
// - if T1 is "pointer to cv1 C1" and T2 is "pointer to cv2 C2", where C1
// is reference-related to C2 or C2 is reference-related to C1 (8.6.3),
// the cv-combined type of T1 and T2 or the cv-combined type of T2 and
// T1, respectively;
//
// The "similar type" handling covers all of this except for the "T1 is a
// base class of T2" case in the definition of reference-related.
else if (IsDerivedFrom(Loc, Composite1, Composite2))
Composite1 = Composite2;
else if (IsDerivedFrom(Loc, Composite2, Composite1))
Composite2 = Composite1;
}
// At this point, either the inner types are the same or we have failed to
// find a composite pointer type.
if (!Context.hasSameType(Composite1, Composite2))
return QualType();
// Per C++ [conv.qual]p3, add 'const' to every level before the last
// differing qualifier.
for (unsigned I = 0; I != NeedConstBefore; ++I)
Steps[I].Quals.addConst();
// Rebuild the composite type.
QualType Composite = Context.getCommonSugaredType(Composite1, Composite2);
for (auto &S : llvm::reverse(Steps))
Composite = S.rebuild(Context, Composite);
if (ConvertArgs) {
// Convert the expressions to the composite pointer type.
InitializedEntity Entity =
InitializedEntity::InitializeTemporary(Composite);
InitializationKind Kind =
InitializationKind::CreateCopy(Loc, SourceLocation());
InitializationSequence E1ToC(*this, Entity, Kind, E1);
if (!E1ToC)
return QualType();
InitializationSequence E2ToC(*this, Entity, Kind, E2);
if (!E2ToC)
return QualType();
// FIXME: Let the caller know if these fail to avoid duplicate diagnostics.
ExprResult E1Result = E1ToC.Perform(*this, Entity, Kind, E1);
if (E1Result.isInvalid())
return QualType();
E1 = E1Result.get();
ExprResult E2Result = E2ToC.Perform(*this, Entity, Kind, E2);
if (E2Result.isInvalid())
return QualType();
E2 = E2Result.get();
}
return Composite;
}
ExprResult Sema::MaybeBindToTemporary(Expr *E) {
if (!E)
return ExprError();
assert(!isa<CXXBindTemporaryExpr>(E) && "Double-bound temporary?");
// If the result is a glvalue, we shouldn't bind it.
if (E->isGLValue())
return E;
// In ARC, calls that return a retainable type can return retained,
// in which case we have to insert a consuming cast.
if (getLangOpts().ObjCAutoRefCount &&
E->getType()->isObjCRetainableType()) {
bool ReturnsRetained;
// For actual calls, we compute this by examining the type of the
// called value.
if (CallExpr *Call = dyn_cast<CallExpr>(E)) {
Expr *Callee = Call->getCallee()->IgnoreParens();
QualType T = Callee->getType();
if (T == Context.BoundMemberTy) {
// Handle pointer-to-members.
if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(Callee))
T = BinOp->getRHS()->getType();
else if (MemberExpr *Mem = dyn_cast<MemberExpr>(Callee))
T = Mem->getMemberDecl()->getType();
}
if (const PointerType *Ptr = T->getAs<PointerType>())
T = Ptr->getPointeeType();
else if (const BlockPointerType *Ptr = T->getAs<BlockPointerType>())
T = Ptr->getPointeeType();
else if (const MemberPointerType *MemPtr = T->getAs<MemberPointerType>())
T = MemPtr->getPointeeType();
auto *FTy = T->castAs<FunctionType>();
ReturnsRetained = FTy->getExtInfo().getProducesResult();
// ActOnStmtExpr arranges things so that StmtExprs of retainable
// type always produce a +1 object.
} else if (isa<StmtExpr>(E)) {
ReturnsRetained = true;
// We hit this case with the lambda conversion-to-block optimization;
// we don't want any extra casts here.
} else if (isa<CastExpr>(E) &&
isa<BlockExpr>(cast<CastExpr>(E)->getSubExpr())) {
return E;
// For message sends and property references, we try to find an
// actual method. FIXME: we should infer retention by selector in
// cases where we don't have an actual method.
} else {
ObjCMethodDecl *D = nullptr;
if (ObjCMessageExpr *Send = dyn_cast<ObjCMessageExpr>(E)) {
D = Send->getMethodDecl();
} else if (ObjCBoxedExpr *BoxedExpr = dyn_cast<ObjCBoxedExpr>(E)) {
D = BoxedExpr->getBoxingMethod();
} else if (ObjCArrayLiteral *ArrayLit = dyn_cast<ObjCArrayLiteral>(E)) {
// Don't do reclaims if we're using the zero-element array
// constant.
if (ArrayLit->getNumElements() == 0 &&
Context.getLangOpts().ObjCRuntime.hasEmptyCollections())
return E;
D = ArrayLit->getArrayWithObjectsMethod();
} else if (ObjCDictionaryLiteral *DictLit
= dyn_cast<ObjCDictionaryLiteral>(E)) {
// Don't do reclaims if we're using the zero-element dictionary
// constant.
if (DictLit->getNumElements() == 0 &&
Context.getLangOpts().ObjCRuntime.hasEmptyCollections())
return E;
D = DictLit->getDictWithObjectsMethod();
}
ReturnsRetained = (D && D->hasAttr<NSReturnsRetainedAttr>());
// Don't do reclaims on performSelector calls; despite their
// return type, the invoked method doesn't necessarily actually
// return an object.
if (!ReturnsRetained &&
D && D->getMethodFamily() == OMF_performSelector)
return E;
}
// Don't reclaim an object of Class type.
if (!ReturnsRetained && E->getType()->isObjCARCImplicitlyUnretainedType())
return E;
Cleanup.setExprNeedsCleanups(true);
CastKind ck = (ReturnsRetained ? CK_ARCConsumeObject
: CK_ARCReclaimReturnedObject);
return ImplicitCastExpr::Create(Context, E->getType(), ck, E, nullptr,
VK_PRValue, FPOptionsOverride());
}
if (E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct)
Cleanup.setExprNeedsCleanups(true);
if (!getLangOpts().CPlusPlus)
return E;
// Search for the base element type (cf. ASTContext::getBaseElementType) with
// a fast path for the common case that the type is directly a RecordType.
const Type *T = Context.getCanonicalType(E->getType().getTypePtr());
const RecordType *RT = nullptr;
while (!RT) {
switch (T->getTypeClass()) {
case Type::Record:
RT = cast<RecordType>(T);
break;
case Type::ConstantArray:
case Type::IncompleteArray:
case Type::VariableArray:
case Type::DependentSizedArray:
T = cast<ArrayType>(T)->getElementType().getTypePtr();
break;
default:
return E;
}
}
// That should be enough to guarantee that this type is complete, if we're
// not processing a decltype expression.
CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
if (RD->isInvalidDecl() || RD->isDependentContext())
return E;
bool IsDecltype = ExprEvalContexts.back().ExprContext ==
ExpressionEvaluationContextRecord::EK_Decltype;
CXXDestructorDecl *Destructor = IsDecltype ? nullptr : LookupDestructor(RD);
if (Destructor) {
MarkFunctionReferenced(E->getExprLoc(), Destructor);
CheckDestructorAccess(E->getExprLoc(), Destructor,
PDiag(diag::err_access_dtor_temp)
<< E->getType());
if (DiagnoseUseOfDecl(Destructor, E->getExprLoc()))
return ExprError();
// If destructor is trivial, we can avoid the extra copy.
if (Destructor->isTrivial())
return E;
// We need a cleanup, but we don't need to remember the temporary.
Cleanup.setExprNeedsCleanups(true);
}
CXXTemporary *Temp = CXXTemporary::Create(Context, Destructor);
CXXBindTemporaryExpr *Bind = CXXBindTemporaryExpr::Create(Context, Temp, E);
if (IsDecltype)
ExprEvalContexts.back().DelayedDecltypeBinds.push_back(Bind);
return Bind;
}
ExprResult
Sema::MaybeCreateExprWithCleanups(ExprResult SubExpr) {
if (SubExpr.isInvalid())
return ExprError();
return MaybeCreateExprWithCleanups(SubExpr.get());
}
Expr *Sema::MaybeCreateExprWithCleanups(Expr *SubExpr) {
assert(SubExpr && "subexpression can't be null!");
CleanupVarDeclMarking();
unsigned FirstCleanup = ExprEvalContexts.back().NumCleanupObjects;
assert(ExprCleanupObjects.size() >= FirstCleanup);
assert(Cleanup.exprNeedsCleanups() ||
ExprCleanupObjects.size() == FirstCleanup);
if (!Cleanup.exprNeedsCleanups())
return SubExpr;
auto Cleanups = llvm::ArrayRef(ExprCleanupObjects.begin() + FirstCleanup,
ExprCleanupObjects.size() - FirstCleanup);
auto *E = ExprWithCleanups::Create(
Context, SubExpr, Cleanup.cleanupsHaveSideEffects(), Cleanups);
DiscardCleanupsInEvaluationContext();
return E;
}
Stmt *Sema::MaybeCreateStmtWithCleanups(Stmt *SubStmt) {
assert(SubStmt && "sub-statement can't be null!");
CleanupVarDeclMarking();
if (!Cleanup.exprNeedsCleanups())
return SubStmt;
// FIXME: In order to attach the temporaries, wrap the statement into
// a StmtExpr; currently this is only used for asm statements.
// This is hacky, either create a new CXXStmtWithTemporaries statement or
// a new AsmStmtWithTemporaries.
CompoundStmt *CompStmt =
CompoundStmt::Create(Context, SubStmt, FPOptionsOverride(),
SourceLocation(), SourceLocation());
Expr *E = new (Context)
StmtExpr(CompStmt, Context.VoidTy, SourceLocation(), SourceLocation(),
/*FIXME TemplateDepth=*/0);
return MaybeCreateExprWithCleanups(E);
}
ExprResult Sema::ActOnDecltypeExpression(Expr *E) {
assert(ExprEvalContexts.back().ExprContext ==
ExpressionEvaluationContextRecord::EK_Decltype &&
"not in a decltype expression");
ExprResult Result = CheckPlaceholderExpr(E);
if (Result.isInvalid())
return ExprError();
E = Result.get();
// C++11 [expr.call]p11:
// If a function call is a prvalue of object type,
// -- if the function call is either
// -- the operand of a decltype-specifier, or
// -- the right operand of a comma operator that is the operand of a
// decltype-specifier,
// a temporary object is not introduced for the prvalue.
// Recursively rebuild ParenExprs and comma expressions to strip out the
// outermost CXXBindTemporaryExpr, if any.
if (ParenExpr *PE = dyn_cast<ParenExpr>(E)) {
ExprResult SubExpr = ActOnDecltypeExpression(PE->getSubExpr());
if (SubExpr.isInvalid())
return ExprError();
if (SubExpr.get() == PE->getSubExpr())
return E;
return ActOnParenExpr(PE->getLParen(), PE->getRParen(), SubExpr.get());
}
if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
if (BO->getOpcode() == BO_Comma) {
ExprResult RHS = ActOnDecltypeExpression(BO->getRHS());
if (RHS.isInvalid())
return ExprError();
if (RHS.get() == BO->getRHS())
return E;
return BinaryOperator::Create(Context, BO->getLHS(), RHS.get(), BO_Comma,
BO->getType(), BO->getValueKind(),
BO->getObjectKind(), BO->getOperatorLoc(),
BO->getFPFeatures());
}
}
CXXBindTemporaryExpr *TopBind = dyn_cast<CXXBindTemporaryExpr>(E);
CallExpr *TopCall = TopBind ? dyn_cast<CallExpr>(TopBind->getSubExpr())
: nullptr;
if (TopCall)
E = TopCall;
else
TopBind = nullptr;
// Disable the special decltype handling now.
ExprEvalContexts.back().ExprContext =
ExpressionEvaluationContextRecord::EK_Other;
Result = CheckUnevaluatedOperand(E);
if (Result.isInvalid())
return ExprError();
E = Result.get();
// In MS mode, don't perform any extra checking of call return types within a
// decltype expression.
if (getLangOpts().MSVCCompat)
return E;
// Perform the semantic checks we delayed until this point.
for (unsigned I = 0, N = ExprEvalContexts.back().DelayedDecltypeCalls.size();
I != N; ++I) {
CallExpr *Call = ExprEvalContexts.back().DelayedDecltypeCalls[I];
if (Call == TopCall)
continue;
if (CheckCallReturnType(Call->getCallReturnType(Context),
Call->getBeginLoc(), Call, Call->getDirectCallee()))
return ExprError();
}
// Now all relevant types are complete, check the destructors are accessible
// and non-deleted, and annotate them on the temporaries.
for (unsigned I = 0, N = ExprEvalContexts.back().DelayedDecltypeBinds.size();
I != N; ++I) {
CXXBindTemporaryExpr *Bind =
ExprEvalContexts.back().DelayedDecltypeBinds[I];
if (Bind == TopBind)
continue;
CXXTemporary *Temp = Bind->getTemporary();
CXXRecordDecl *RD =
Bind->getType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
CXXDestructorDecl *Destructor = LookupDestructor(RD);
Temp->setDestructor(Destructor);
MarkFunctionReferenced(Bind->getExprLoc(), Destructor);
CheckDestructorAccess(Bind->getExprLoc(), Destructor,
PDiag(diag::err_access_dtor_temp)
<< Bind->getType());
if (DiagnoseUseOfDecl(Destructor, Bind->getExprLoc()))
return ExprError();
// We need a cleanup, but we don't need to remember the temporary.
Cleanup.setExprNeedsCleanups(true);
}
// Possibly strip off the top CXXBindTemporaryExpr.
return E;
}
/// Note a set of 'operator->' functions that were used for a member access.
static void noteOperatorArrows(Sema &S,
ArrayRef<FunctionDecl *> OperatorArrows) {
unsigned SkipStart = OperatorArrows.size(), SkipCount = 0;
// FIXME: Make this configurable?
unsigned Limit = 9;
if (OperatorArrows.size() > Limit) {
// Produce Limit-1 normal notes and one 'skipping' note.
SkipStart = (Limit - 1) / 2 + (Limit - 1) % 2;
SkipCount = OperatorArrows.size() - (Limit - 1);
}
for (unsigned I = 0; I < OperatorArrows.size(); /**/) {
if (I == SkipStart) {
S.Diag(OperatorArrows[I]->getLocation(),
diag::note_operator_arrows_suppressed)
<< SkipCount;
I += SkipCount;
} else {
S.Diag(OperatorArrows[I]->getLocation(), diag::note_operator_arrow_here)
<< OperatorArrows[I]->getCallResultType();
++I;
}
}
}
ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
SourceLocation OpLoc,
tok::TokenKind OpKind,
ParsedType &ObjectType,
bool &MayBePseudoDestructor) {
// Since this might be a postfix expression, get rid of ParenListExprs.
ExprResult Result = MaybeConvertParenListExprToParenExpr(S, Base);
if (Result.isInvalid()) return ExprError();
Base = Result.get();
Result = CheckPlaceholderExpr(Base);
if (Result.isInvalid()) return ExprError();
Base = Result.get();
QualType BaseType = Base->getType();
MayBePseudoDestructor = false;
if (BaseType->isDependentType()) {
// If we have a pointer to a dependent type and are using the -> operator,
// the object type is the type that the pointer points to. We might still
// have enough information about that type to do something useful.
if (OpKind == tok::arrow)
if (const PointerType *Ptr = BaseType->getAs<PointerType>())
BaseType = Ptr->getPointeeType();
ObjectType = ParsedType::make(BaseType);
MayBePseudoDestructor = true;
return Base;
}
// C++ [over.match.oper]p8:
// [...] When operator->returns, the operator-> is applied to the value
// returned, with the original second operand.
if (OpKind == tok::arrow) {
QualType StartingType = BaseType;
bool NoArrowOperatorFound = false;
bool FirstIteration = true;
FunctionDecl *CurFD = dyn_cast<FunctionDecl>(CurContext);
// The set of types we've considered so far.
llvm::SmallPtrSet<CanQualType,8> CTypes;
SmallVector<FunctionDecl*, 8> OperatorArrows;
CTypes.insert(Context.getCanonicalType(BaseType));
while (BaseType->isRecordType()) {
if (OperatorArrows.size() >= getLangOpts().ArrowDepth) {
Diag(OpLoc, diag::err_operator_arrow_depth_exceeded)
<< StartingType << getLangOpts().ArrowDepth << Base->getSourceRange();
noteOperatorArrows(*this, OperatorArrows);
Diag(OpLoc, diag::note_operator_arrow_depth)
<< getLangOpts().ArrowDepth;
return ExprError();
}
Result = BuildOverloadedArrowExpr(
S, Base, OpLoc,
// When in a template specialization and on the first loop iteration,
// potentially give the default diagnostic (with the fixit in a
// separate note) instead of having the error reported back to here
// and giving a diagnostic with a fixit attached to the error itself.
(FirstIteration && CurFD && CurFD->isFunctionTemplateSpecialization())
? nullptr
: &NoArrowOperatorFound);
if (Result.isInvalid()) {
if (NoArrowOperatorFound) {
if (FirstIteration) {
Diag(OpLoc, diag::err_typecheck_member_reference_suggestion)
<< BaseType << 1 << Base->getSourceRange()
<< FixItHint::CreateReplacement(OpLoc, ".");
OpKind = tok::period;
break;
}
Diag(OpLoc, diag::err_typecheck_member_reference_arrow)
<< BaseType << Base->getSourceRange();
CallExpr *CE = dyn_cast<CallExpr>(Base);
if (Decl *CD = (CE ? CE->getCalleeDecl() : nullptr)) {
Diag(CD->getBeginLoc(),
diag::note_member_reference_arrow_from_operator_arrow);
}
}
return ExprError();
}
Base = Result.get();
if (CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(Base))
OperatorArrows.push_back(OpCall->getDirectCallee());
BaseType = Base->getType();
CanQualType CBaseType = Context.getCanonicalType(BaseType);
if (!CTypes.insert(CBaseType).second) {
Diag(OpLoc, diag::err_operator_arrow_circular) << StartingType;
noteOperatorArrows(*this, OperatorArrows);
return ExprError();
}
FirstIteration = false;
}
if (OpKind == tok::arrow) {
if (BaseType->isPointerType())
BaseType = BaseType->getPointeeType();
else if (auto *AT = Context.getAsArrayType(BaseType))
BaseType = AT->getElementType();
}
}
// Objective-C properties allow "." access on Objective-C pointer types,
// so adjust the base type to the object type itself.
if (BaseType->isObjCObjectPointerType())
BaseType = BaseType->getPointeeType();
// C++ [basic.lookup.classref]p2:
// [...] If the type of the object expression is of pointer to scalar
// type, the unqualified-id is looked up in the context of the complete
// postfix-expression.
//
// This also indicates that we could be parsing a pseudo-destructor-name.
// Note that Objective-C class and object types can be pseudo-destructor
// expressions or normal member (ivar or property) access expressions, and
// it's legal for the type to be incomplete if this is a pseudo-destructor
// call. We'll do more incomplete-type checks later in the lookup process,
// so just skip this check for ObjC types.
if (!BaseType->isRecordType()) {
ObjectType = ParsedType::make(BaseType);
MayBePseudoDestructor = true;
return Base;
}
// The object type must be complete (or dependent), or
// C++11 [expr.prim.general]p3:
// Unlike the object expression in other contexts, *this is not required to
// be of complete type for purposes of class member access (5.2.5) outside
// the member function body.
if (!BaseType->isDependentType() &&
!isThisOutsideMemberFunctionBody(BaseType) &&
RequireCompleteType(OpLoc, BaseType,
diag::err_incomplete_member_access)) {
return CreateRecoveryExpr(Base->getBeginLoc(), Base->getEndLoc(), {Base});
}
// C++ [basic.lookup.classref]p2:
// If the id-expression in a class member access (5.2.5) is an
// unqualified-id, and the type of the object expression is of a class
// type C (or of pointer to a class type C), the unqualified-id is looked
// up in the scope of class C. [...]
ObjectType = ParsedType::make(BaseType);
return Base;
}
static bool CheckArrow(Sema &S, QualType &ObjectType, Expr *&Base,
tok::TokenKind &OpKind, SourceLocation OpLoc) {
if (Base->hasPlaceholderType()) {
ExprResult result = S.CheckPlaceholderExpr(Base);
if (result.isInvalid()) return true;
Base = result.get();
}
ObjectType = Base->getType();
// C++ [expr.pseudo]p2:
// The left-hand side of the dot operator shall be of scalar type. The
// left-hand side of the arrow operator shall be of pointer to scalar type.
// This scalar type is the object type.
// Note that this is rather different from the normal handling for the
// arrow operator.
if (OpKind == tok::arrow) {
// The operator requires a prvalue, so perform lvalue conversions.
// Only do this if we might plausibly end with a pointer, as otherwise
// this was likely to be intended to be a '.'.
if (ObjectType->isPointerType() || ObjectType->isArrayType() ||
ObjectType->isFunctionType()) {
ExprResult BaseResult = S.DefaultFunctionArrayLvalueConversion(Base);
if (BaseResult.isInvalid())
return true;
Base = BaseResult.get();
ObjectType = Base->getType();
}
if (const PointerType *Ptr = ObjectType->getAs<PointerType>()) {
ObjectType = Ptr->getPointeeType();
} else if (!Base->isTypeDependent()) {
// The user wrote "p->" when they probably meant "p."; fix it.
S.Diag(OpLoc, diag::err_typecheck_member_reference_suggestion)
<< ObjectType << true
<< FixItHint::CreateReplacement(OpLoc, ".");
if (S.isSFINAEContext())
return true;
OpKind = tok::period;
}
}
return false;
}
/// Check if it's ok to try and recover dot pseudo destructor calls on
/// pointer objects.
static bool
canRecoverDotPseudoDestructorCallsOnPointerObjects(Sema &SemaRef,
QualType DestructedType) {
// If this is a record type, check if its destructor is callable.
if (auto *RD = DestructedType->getAsCXXRecordDecl()) {
if (RD->hasDefinition())
if (CXXDestructorDecl *D = SemaRef.LookupDestructor(RD))
return SemaRef.CanUseDecl(D, /*TreatUnavailableAsInvalid=*/false);
return false;
}
// Otherwise, check if it's a type for which it's valid to use a pseudo-dtor.
return DestructedType->isDependentType() || DestructedType->isScalarType() ||
DestructedType->isVectorType();
}
ExprResult Sema::BuildPseudoDestructorExpr(Expr *Base,
SourceLocation OpLoc,
tok::TokenKind OpKind,
const CXXScopeSpec &SS,
TypeSourceInfo *ScopeTypeInfo,
SourceLocation CCLoc,
SourceLocation TildeLoc,
PseudoDestructorTypeStorage Destructed) {
TypeSourceInfo *DestructedTypeInfo = Destructed.getTypeSourceInfo();
QualType ObjectType;
if (CheckArrow(*this, ObjectType, Base, OpKind, OpLoc))
return ExprError();
if (!ObjectType->isDependentType() && !ObjectType->isScalarType() &&
!ObjectType->isVectorType() && !ObjectType->isMatrixType()) {
if (getLangOpts().MSVCCompat && ObjectType->isVoidType())
Diag(OpLoc, diag::ext_pseudo_dtor_on_void) << Base->getSourceRange();
else {
Diag(OpLoc, diag::err_pseudo_dtor_base_not_scalar)
<< ObjectType << Base->getSourceRange();
return ExprError();
}
}
// C++ [expr.pseudo]p2:
// [...] The cv-unqualified versions of the object type and of the type
// designated by the pseudo-destructor-name shall be the same type.
if (DestructedTypeInfo) {
QualType DestructedType = DestructedTypeInfo->getType();
SourceLocation DestructedTypeStart =
DestructedTypeInfo->getTypeLoc().getBeginLoc();
if (!DestructedType->isDependentType() && !ObjectType->isDependentType()) {
if (!Context.hasSameUnqualifiedType(DestructedType, ObjectType)) {
// Detect dot pseudo destructor calls on pointer objects, e.g.:
// Foo *foo;
// foo.~Foo();
if (OpKind == tok::period && ObjectType->isPointerType() &&
Context.hasSameUnqualifiedType(DestructedType,
ObjectType->getPointeeType())) {
auto Diagnostic =
Diag(OpLoc, diag::err_typecheck_member_reference_suggestion)
<< ObjectType << /*IsArrow=*/0 << Base->getSourceRange();
// Issue a fixit only when the destructor is valid.
if (canRecoverDotPseudoDestructorCallsOnPointerObjects(
*this, DestructedType))
Diagnostic << FixItHint::CreateReplacement(OpLoc, "->");
// Recover by setting the object type to the destructed type and the
// operator to '->'.
ObjectType = DestructedType;
OpKind = tok::arrow;
} else {
Diag(DestructedTypeStart, diag::err_pseudo_dtor_type_mismatch)
<< ObjectType << DestructedType << Base->getSourceRange()
<< DestructedTypeInfo->getTypeLoc().getSourceRange();
// Recover by setting the destructed type to the object type.
DestructedType = ObjectType;
DestructedTypeInfo =
Context.getTrivialTypeSourceInfo(ObjectType, DestructedTypeStart);
Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo);
}
} else if (DestructedType.getObjCLifetime() !=
ObjectType.getObjCLifetime()) {
if (DestructedType.getObjCLifetime() == Qualifiers::OCL_None) {
// Okay: just pretend that the user provided the correctly-qualified
// type.
} else {
Diag(DestructedTypeStart, diag::err_arc_pseudo_dtor_inconstant_quals)
<< ObjectType << DestructedType << Base->getSourceRange()
<< DestructedTypeInfo->getTypeLoc().getSourceRange();
}
// Recover by setting the destructed type to the object type.
DestructedType = ObjectType;
DestructedTypeInfo = Context.getTrivialTypeSourceInfo(ObjectType,
DestructedTypeStart);
Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo);
}
}
}
// C++ [expr.pseudo]p2:
// [...] Furthermore, the two type-names in a pseudo-destructor-name of the
// form
//
// ::[opt] nested-name-specifier[opt] type-name :: ~ type-name
//
// shall designate the same scalar type.
if (ScopeTypeInfo) {
QualType ScopeType = ScopeTypeInfo->getType();
if (!ScopeType->isDependentType() && !ObjectType->isDependentType() &&
!Context.hasSameUnqualifiedType(ScopeType, ObjectType)) {
Diag(ScopeTypeInfo->getTypeLoc().getSourceRange().getBegin(),
diag::err_pseudo_dtor_type_mismatch)
<< ObjectType << ScopeType << Base->getSourceRange()
<< ScopeTypeInfo->getTypeLoc().getSourceRange();
ScopeType = QualType();
ScopeTypeInfo = nullptr;
}
}
Expr *Result
= new (Context) CXXPseudoDestructorExpr(Context, Base,
OpKind == tok::arrow, OpLoc,
SS.getWithLocInContext(Context),
ScopeTypeInfo,
CCLoc,
TildeLoc,
Destructed);
return Result;
}
ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base,
SourceLocation OpLoc,
tok::TokenKind OpKind,
CXXScopeSpec &SS,
UnqualifiedId &FirstTypeName,
SourceLocation CCLoc,
SourceLocation TildeLoc,
UnqualifiedId &SecondTypeName) {
assert((FirstTypeName.getKind() == UnqualifiedIdKind::IK_TemplateId ||
FirstTypeName.getKind() == UnqualifiedIdKind::IK_Identifier) &&
"Invalid first type name in pseudo-destructor");
assert((SecondTypeName.getKind() == UnqualifiedIdKind::IK_TemplateId ||
SecondTypeName.getKind() == UnqualifiedIdKind::IK_Identifier) &&
"Invalid second type name in pseudo-destructor");
QualType ObjectType;
if (CheckArrow(*this, ObjectType, Base, OpKind, OpLoc))
return ExprError();
// Compute the object type that we should use for name lookup purposes. Only
// record types and dependent types matter.
ParsedType ObjectTypePtrForLookup;
if (!SS.isSet()) {
if (ObjectType->isRecordType())
ObjectTypePtrForLookup = ParsedType::make(ObjectType);
else if (ObjectType->isDependentType())
ObjectTypePtrForLookup = ParsedType::make(Context.DependentTy);
}
// Convert the name of the type being destructed (following the ~) into a
// type (with source-location information).
QualType DestructedType;
TypeSourceInfo *DestructedTypeInfo = nullptr;
PseudoDestructorTypeStorage Destructed;
if (SecondTypeName.getKind() == UnqualifiedIdKind::IK_Identifier) {
ParsedType T = getTypeName(*SecondTypeName.Identifier,
SecondTypeName.StartLocation,
S, &SS, true, false, ObjectTypePtrForLookup,
/*IsCtorOrDtorName*/true);
if (!T &&
((SS.isSet() && !computeDeclContext(SS, false)) ||
(!SS.isSet() && ObjectType->isDependentType()))) {
// The name of the type being destroyed is a dependent name, and we
// couldn't find anything useful in scope. Just store the identifier and
// it's location, and we'll perform (qualified) name lookup again at
// template instantiation time.
Destructed = PseudoDestructorTypeStorage(SecondTypeName.Identifier,
SecondTypeName.StartLocation);
} else if (!T) {
Diag(SecondTypeName.StartLocation,
diag::err_pseudo_dtor_destructor_non_type)
<< SecondTypeName.Identifier << ObjectType;
if (isSFINAEContext())
return ExprError();
// Recover by assuming we had the right type all along.
DestructedType = ObjectType;
} else
DestructedType = GetTypeFromParser(T, &DestructedTypeInfo);
} else {
// Resolve the template-id to a type.
TemplateIdAnnotation *TemplateId = SecondTypeName.TemplateId;
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
TypeResult T = ActOnTemplateIdType(S,
SS,
TemplateId->TemplateKWLoc,
TemplateId->Template,
TemplateId->Name,
TemplateId->TemplateNameLoc,
TemplateId->LAngleLoc,
TemplateArgsPtr,
TemplateId->RAngleLoc,
/*IsCtorOrDtorName*/true);
if (T.isInvalid() || !T.get()) {
// Recover by assuming we had the right type all along.
DestructedType = ObjectType;
} else
DestructedType = GetTypeFromParser(T.get(), &DestructedTypeInfo);
}
// If we've performed some kind of recovery, (re-)build the type source
// information.
if (!DestructedType.isNull()) {
if (!DestructedTypeInfo)
DestructedTypeInfo = Context.getTrivialTypeSourceInfo(DestructedType,
SecondTypeName.StartLocation);
Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo);
}
// Convert the name of the scope type (the type prior to '::') into a type.
TypeSourceInfo *ScopeTypeInfo = nullptr;
QualType ScopeType;
if (FirstTypeName.getKind() == UnqualifiedIdKind::IK_TemplateId ||
FirstTypeName.Identifier) {
if (FirstTypeName.getKind() == UnqualifiedIdKind::IK_Identifier) {
ParsedType T = getTypeName(*FirstTypeName.Identifier,
FirstTypeName.StartLocation,
S, &SS, true, false, ObjectTypePtrForLookup,
/*IsCtorOrDtorName*/true);
if (!T) {
Diag(FirstTypeName.StartLocation,
diag::err_pseudo_dtor_destructor_non_type)
<< FirstTypeName.Identifier << ObjectType;
if (isSFINAEContext())
return ExprError();
// Just drop this type. It's unnecessary anyway.
ScopeType = QualType();
} else
ScopeType = GetTypeFromParser(T, &ScopeTypeInfo);
} else {
// Resolve the template-id to a type.
TemplateIdAnnotation *TemplateId = FirstTypeName.TemplateId;
ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
TypeResult T = ActOnTemplateIdType(S,
SS,
TemplateId->TemplateKWLoc,
TemplateId->Template,
TemplateId->Name,
TemplateId->TemplateNameLoc,
TemplateId->LAngleLoc,
TemplateArgsPtr,
TemplateId->RAngleLoc,
/*IsCtorOrDtorName*/true);
if (T.isInvalid() || !T.get()) {
// Recover by dropping this type.
ScopeType = QualType();
} else
ScopeType = GetTypeFromParser(T.get(), &ScopeTypeInfo);
}
}
if (!ScopeType.isNull() && !ScopeTypeInfo)
ScopeTypeInfo = Context.getTrivialTypeSourceInfo(ScopeType,
FirstTypeName.StartLocation);
return BuildPseudoDestructorExpr(Base, OpLoc, OpKind, SS,
ScopeTypeInfo, CCLoc, TildeLoc,
Destructed);
}
ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base,
SourceLocation OpLoc,
tok::TokenKind OpKind,
SourceLocation TildeLoc,
const DeclSpec& DS) {
QualType ObjectType;
QualType T;
TypeLocBuilder TLB;
if (CheckArrow(*this, ObjectType, Base, OpKind, OpLoc) ||
DS.getTypeSpecType() == DeclSpec::TST_error)
return ExprError();
switch (DS.getTypeSpecType()) {
case DeclSpec::TST_decltype_auto: {
Diag(DS.getTypeSpecTypeLoc(), diag::err_decltype_auto_invalid);
return true;
}
case DeclSpec::TST_decltype: {
T = BuildDecltypeType(DS.getRepAsExpr(), /*AsUnevaluated=*/false);
DecltypeTypeLoc DecltypeTL = TLB.push<DecltypeTypeLoc>(T);
DecltypeTL.setDecltypeLoc(DS.getTypeSpecTypeLoc());
DecltypeTL.setRParenLoc(DS.getTypeofParensRange().getEnd());
break;
}
case DeclSpec::TST_typename_pack_indexing: {
T = ActOnPackIndexingType(DS.getRepAsType().get(), DS.getPackIndexingExpr(),
DS.getBeginLoc(), DS.getEllipsisLoc());
TLB.pushTrivial(getASTContext(),
cast<PackIndexingType>(T.getTypePtr())->getPattern(),
DS.getBeginLoc());
PackIndexingTypeLoc PITL = TLB.push<PackIndexingTypeLoc>(T);
PITL.setEllipsisLoc(DS.getEllipsisLoc());
break;
}
default:
llvm_unreachable("Unsupported type in pseudo destructor");
}
TypeSourceInfo *DestructedTypeInfo = TLB.getTypeSourceInfo(Context, T);
PseudoDestructorTypeStorage Destructed(DestructedTypeInfo);
return BuildPseudoDestructorExpr(Base, OpLoc, OpKind, CXXScopeSpec(),
nullptr, SourceLocation(), TildeLoc,
Destructed);
}
ExprResult Sema::BuildCXXNoexceptExpr(SourceLocation KeyLoc, Expr *Operand,
SourceLocation RParen) {
// If the operand is an unresolved lookup expression, the expression is ill-
// formed per [over.over]p1, because overloaded function names cannot be used
// without arguments except in explicit contexts.
ExprResult R = CheckPlaceholderExpr(Operand);
if (R.isInvalid())
return R;
R = CheckUnevaluatedOperand(R.get());
if (R.isInvalid())
return ExprError();
Operand = R.get();
if (!inTemplateInstantiation() && !Operand->isInstantiationDependent() &&
Operand->HasSideEffects(Context, false)) {
// The expression operand for noexcept is in an unevaluated expression
// context, so side effects could result in unintended consequences.
Diag(Operand->getExprLoc(), diag::warn_side_effects_unevaluated_context);
}
CanThrowResult CanThrow = canThrow(Operand);
return new (Context)
CXXNoexceptExpr(Context.BoolTy, Operand, CanThrow, KeyLoc, RParen);
}
ExprResult Sema::ActOnNoexceptExpr(SourceLocation KeyLoc, SourceLocation,
Expr *Operand, SourceLocation RParen) {
return BuildCXXNoexceptExpr(KeyLoc, Operand, RParen);
}
static void MaybeDecrementCount(
Expr *E, llvm::DenseMap<const VarDecl *, int> &RefsMinusAssignments) {
DeclRefExpr *LHS = nullptr;
bool IsCompoundAssign = false;
bool isIncrementDecrementUnaryOp = false;
if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
if (BO->getLHS()->getType()->isDependentType() ||
BO->getRHS()->getType()->isDependentType()) {
if (BO->getOpcode() != BO_Assign)
return;
} else if (!BO->isAssignmentOp())
return;
else
IsCompoundAssign = BO->isCompoundAssignmentOp();
LHS = dyn_cast<DeclRefExpr>(BO->getLHS());
} else if (CXXOperatorCallExpr *COCE = dyn_cast<CXXOperatorCallExpr>(E)) {
if (COCE->getOperator() != OO_Equal)
return;
LHS = dyn_cast<DeclRefExpr>(COCE->getArg(0));
} else if (UnaryOperator *UO = dyn_cast<UnaryOperator>(E)) {
if (!UO->isIncrementDecrementOp())
return;
isIncrementDecrementUnaryOp = true;
LHS = dyn_cast<DeclRefExpr>(UO->getSubExpr());
}
if (!LHS)
return;
VarDecl *VD = dyn_cast<VarDecl>(LHS->getDecl());
if (!VD)
return;
// Don't decrement RefsMinusAssignments if volatile variable with compound
// assignment (+=, ...) or increment/decrement unary operator to avoid
// potential unused-but-set-variable warning.
if ((IsCompoundAssign || isIncrementDecrementUnaryOp) &&
VD->getType().isVolatileQualified())
return;
auto iter = RefsMinusAssignments.find(VD);
if (iter == RefsMinusAssignments.end())
return;
iter->getSecond()--;
}
/// Perform the conversions required for an expression used in a
/// context that ignores the result.
ExprResult Sema::IgnoredValueConversions(Expr *E) {
MaybeDecrementCount(E, RefsMinusAssignments);
if (E->hasPlaceholderType()) {
ExprResult result = CheckPlaceholderExpr(E);
if (result.isInvalid()) return E;
E = result.get();
}
if (getLangOpts().CPlusPlus) {
// The C++11 standard defines the notion of a discarded-value expression;
// normally, we don't need to do anything to handle it, but if it is a
// volatile lvalue with a special form, we perform an lvalue-to-rvalue
// conversion.
if (getLangOpts().CPlusPlus11 && E->isReadIfDiscardedInCPlusPlus11()) {
ExprResult Res = DefaultLvalueConversion(E);
if (Res.isInvalid())
return E;
E = Res.get();
} else {
// Per C++2a [expr.ass]p5, a volatile assignment is not deprecated if
// it occurs as a discarded-value expression.
CheckUnusedVolatileAssignment(E);
}
// C++1z:
// If the expression is a prvalue after this optional conversion, the
// temporary materialization conversion is applied.
//
// We do not materialize temporaries by default in order to avoid creating
// unnecessary temporary objects. If we skip this step, IR generation is
// able to synthesize the storage for itself in the aggregate case, and
// adding the extra node to the AST is just clutter.
if (isInLifetimeExtendingContext() && getLangOpts().CPlusPlus17 &&
E->isPRValue() && !E->getType()->isVoidType()) {
ExprResult Res = TemporaryMaterializationConversion(E);
if (Res.isInvalid())
return E;
E = Res.get();
}
return E;
}
// C99 6.3.2.1:
// [Except in specific positions,] an lvalue that does not have
// array type is converted to the value stored in the
// designated object (and is no longer an lvalue).
if (E->isPRValue()) {
// In C, function designators (i.e. expressions of function type)
// are r-values, but we still want to do function-to-pointer decay
// on them. This is both technically correct and convenient for
// some clients.
if (!getLangOpts().CPlusPlus && E->getType()->isFunctionType())
return DefaultFunctionArrayConversion(E);
return E;
}
// GCC seems to also exclude expressions of incomplete enum type.
if (const EnumType *T = E->getType()->getAs<EnumType>()) {
if (!T->getDecl()->isComplete()) {
// FIXME: stupid workaround for a codegen bug!
E = ImpCastExprToType(E, Context.VoidTy, CK_ToVoid).get();
return E;
}
}
ExprResult Res = DefaultFunctionArrayLvalueConversion(E);
if (Res.isInvalid())
return E;
E = Res.get();
if (!E->getType()->isVoidType())
RequireCompleteType(E->getExprLoc(), E->getType(),
diag::err_incomplete_type);
return E;
}
ExprResult Sema::CheckUnevaluatedOperand(Expr *E) {
// Per C++2a [expr.ass]p5, a volatile assignment is not deprecated if
// it occurs as an unevaluated operand.
CheckUnusedVolatileAssignment(E);
return E;
}
// If we can unambiguously determine whether Var can never be used
// in a constant expression, return true.
// - if the variable and its initializer are non-dependent, then
// we can unambiguously check if the variable is a constant expression.
// - if the initializer is not value dependent - we can determine whether
// it can be used to initialize a constant expression. If Init can not
// be used to initialize a constant expression we conclude that Var can
// never be a constant expression.
// - FXIME: if the initializer is dependent, we can still do some analysis and
// identify certain cases unambiguously as non-const by using a Visitor:
// - such as those that involve odr-use of a ParmVarDecl, involve a new
// delete, lambda-expr, dynamic-cast, reinterpret-cast etc...
static inline bool VariableCanNeverBeAConstantExpression(VarDecl *Var,
ASTContext &Context) {
if (isa<ParmVarDecl>(Var)) return true;
const VarDecl *DefVD = nullptr;
// If there is no initializer - this can not be a constant expression.
const Expr *Init = Var->getAnyInitializer(DefVD);
if (!Init)
return true;
assert(DefVD);
if (DefVD->isWeak())
return false;
if (Var->getType()->isDependentType() || Init->isValueDependent()) {
// FIXME: Teach the constant evaluator to deal with the non-dependent parts
// of value-dependent expressions, and use it here to determine whether the
// initializer is a potential constant expression.
return false;
}
return !Var->isUsableInConstantExpressions(Context);
}
/// Check if the current lambda has any potential captures
/// that must be captured by any of its enclosing lambdas that are ready to
/// capture. If there is a lambda that can capture a nested
/// potential-capture, go ahead and do so. Also, check to see if any
/// variables are uncaptureable or do not involve an odr-use so do not
/// need to be captured.
static void CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures(
Expr *const FE, LambdaScopeInfo *const CurrentLSI, Sema &S) {
assert(!S.isUnevaluatedContext());
assert(S.CurContext->isDependentContext());
#ifndef NDEBUG
DeclContext *DC = S.CurContext;
while (isa_and_nonnull<CapturedDecl>(DC))
DC = DC->getParent();
assert(
(CurrentLSI->CallOperator == DC || !CurrentLSI->AfterParameterList) &&
"The current call operator must be synchronized with Sema's CurContext");
#endif // NDEBUG
const bool IsFullExprInstantiationDependent = FE->isInstantiationDependent();
// All the potentially captureable variables in the current nested
// lambda (within a generic outer lambda), must be captured by an
// outer lambda that is enclosed within a non-dependent context.
CurrentLSI->visitPotentialCaptures([&](ValueDecl *Var, Expr *VarExpr) {
// If the variable is clearly identified as non-odr-used and the full
// expression is not instantiation dependent, only then do we not
// need to check enclosing lambda's for speculative captures.
// For e.g.:
// Even though 'x' is not odr-used, it should be captured.
// int test() {
// const int x = 10;
// auto L = [=](auto a) {
// (void) +x + a;
// };
// }
if (CurrentLSI->isVariableExprMarkedAsNonODRUsed(VarExpr) &&
!IsFullExprInstantiationDependent)
return;
VarDecl *UnderlyingVar = Var->getPotentiallyDecomposedVarDecl();
if (!UnderlyingVar)
return;
// If we have a capture-capable lambda for the variable, go ahead and
// capture the variable in that lambda (and all its enclosing lambdas).
if (const UnsignedOrNone Index =
getStackIndexOfNearestEnclosingCaptureCapableLambda(
S.FunctionScopes, Var, S))
S.MarkCaptureUsedInEnclosingContext(Var, VarExpr->getExprLoc(), *Index);
const bool IsVarNeverAConstantExpression =
VariableCanNeverBeAConstantExpression(UnderlyingVar, S.Context);
if (!IsFullExprInstantiationDependent || IsVarNeverAConstantExpression) {
// This full expression is not instantiation dependent or the variable
// can not be used in a constant expression - which means
// this variable must be odr-used here, so diagnose a
// capture violation early, if the variable is un-captureable.
// This is purely for diagnosing errors early. Otherwise, this
// error would get diagnosed when the lambda becomes capture ready.
QualType CaptureType, DeclRefType;
SourceLocation ExprLoc = VarExpr->getExprLoc();
if (S.tryCaptureVariable(Var, ExprLoc, S.TryCapture_Implicit,
/*EllipsisLoc*/ SourceLocation(),
/*BuildAndDiagnose*/false, CaptureType,
DeclRefType, nullptr)) {
// We will never be able to capture this variable, and we need
// to be able to in any and all instantiations, so diagnose it.
S.tryCaptureVariable(Var, ExprLoc, S.TryCapture_Implicit,
/*EllipsisLoc*/ SourceLocation(),
/*BuildAndDiagnose*/true, CaptureType,
DeclRefType, nullptr);
}
}
});
// Check if 'this' needs to be captured.
if (CurrentLSI->hasPotentialThisCapture()) {
// If we have a capture-capable lambda for 'this', go ahead and capture
// 'this' in that lambda (and all its enclosing lambdas).
if (const UnsignedOrNone Index =
getStackIndexOfNearestEnclosingCaptureCapableLambda(
S.FunctionScopes, /*0 is 'this'*/ nullptr, S)) {
const unsigned FunctionScopeIndexOfCapturableLambda = *Index;
S.CheckCXXThisCapture(CurrentLSI->PotentialThisCaptureLocation,
/*Explicit*/ false, /*BuildAndDiagnose*/ true,
&FunctionScopeIndexOfCapturableLambda);
}
}
// Reset all the potential captures at the end of each full-expression.
CurrentLSI->clearPotentialCaptures();
}
static ExprResult attemptRecovery(Sema &SemaRef,
const TypoCorrectionConsumer &Consumer,
const TypoCorrection &TC) {
LookupResult R(SemaRef, Consumer.getLookupResult().getLookupNameInfo(),
Consumer.getLookupResult().getLookupKind());
const CXXScopeSpec *SS = Consumer.getSS();
CXXScopeSpec NewSS;
// Use an approprate CXXScopeSpec for building the expr.
if (auto *NNS = TC.getCorrectionSpecifier())
NewSS.MakeTrivial(SemaRef.Context, NNS, TC.getCorrectionRange());
else if (SS && !TC.WillReplaceSpecifier())
NewSS = *SS;
if (auto *ND = TC.getFoundDecl()) {
R.setLookupName(ND->getDeclName());
R.addDecl(ND);
if (ND->isCXXClassMember()) {
// Figure out the correct naming class to add to the LookupResult.
CXXRecordDecl *Record = nullptr;
if (auto *NNS = TC.getCorrectionSpecifier())
Record = NNS->getAsType()->getAsCXXRecordDecl();
if (!Record)
Record =
dyn_cast<CXXRecordDecl>(ND->getDeclContext()->getRedeclContext());
if (Record)
R.setNamingClass(Record);
// Detect and handle the case where the decl might be an implicit
// member.
if (SemaRef.isPotentialImplicitMemberAccess(
NewSS, R, Consumer.isAddressOfOperand()))
return SemaRef.BuildPossibleImplicitMemberExpr(
NewSS, /*TemplateKWLoc*/ SourceLocation(), R,
/*TemplateArgs*/ nullptr, /*S*/ nullptr);
} else if (auto *Ivar = dyn_cast<ObjCIvarDecl>(ND)) {
return SemaRef.ObjC().LookupInObjCMethod(R, Consumer.getScope(),
Ivar->getIdentifier());
}
}
return SemaRef.BuildDeclarationNameExpr(NewSS, R, /*NeedsADL*/ false,
/*AcceptInvalidDecl*/ true);
}
namespace {
class FindTypoExprs : public DynamicRecursiveASTVisitor {
llvm::SmallSetVector<TypoExpr *, 2> &TypoExprs;
public:
explicit FindTypoExprs(llvm::SmallSetVector<TypoExpr *, 2> &TypoExprs)
: TypoExprs(TypoExprs) {}
bool VisitTypoExpr(TypoExpr *TE) override {
TypoExprs.insert(TE);
return true;
}
};
class TransformTypos : public TreeTransform<TransformTypos> {
typedef TreeTransform<TransformTypos> BaseTransform;
VarDecl *InitDecl; // A decl to avoid as a correction because it is in the
// process of being initialized.
llvm::function_ref<ExprResult(Expr *)> ExprFilter;
llvm::SmallSetVector<TypoExpr *, 2> TypoExprs, AmbiguousTypoExprs;
llvm::SmallDenseMap<TypoExpr *, ExprResult, 2> TransformCache;
llvm::SmallDenseMap<OverloadExpr *, Expr *, 4> OverloadResolution;
/// Emit diagnostics for all of the TypoExprs encountered.
///
/// If the TypoExprs were successfully corrected, then the diagnostics should
/// suggest the corrections. Otherwise the diagnostics will not suggest
/// anything (having been passed an empty TypoCorrection).
///
/// If we've failed to correct due to ambiguous corrections, we need to
/// be sure to pass empty corrections and replacements. Otherwise it's
/// possible that the Consumer has a TypoCorrection that failed to ambiguity
/// and we don't want to report those diagnostics.
void EmitAllDiagnostics(bool IsAmbiguous) {
for (TypoExpr *TE : TypoExprs) {
auto &State = SemaRef.getTypoExprState(TE);
if (State.DiagHandler) {
TypoCorrection TC = IsAmbiguous
? TypoCorrection() : State.Consumer->getCurrentCorrection();
ExprResult Replacement = IsAmbiguous ? ExprError() : TransformCache[TE];
// Extract the NamedDecl from the transformed TypoExpr and add it to the
// TypoCorrection, replacing the existing decls. This ensures the right
// NamedDecl is used in diagnostics e.g. in the case where overload
// resolution was used to select one from several possible decls that
// had been stored in the TypoCorrection.
if (auto *ND = getDeclFromExpr(
Replacement.isInvalid() ? nullptr : Replacement.get()))
TC.setCorrectionDecl(ND);
State.DiagHandler(TC);
}
SemaRef.clearDelayedTypo(TE);
}
}
/// Try to advance the typo correction state of the first unfinished TypoExpr.
/// We allow advancement of the correction stream by removing it from the
/// TransformCache which allows `TransformTypoExpr` to advance during the
/// next transformation attempt.
///
/// Any substitution attempts for the previous TypoExprs (which must have been
/// finished) will need to be retried since it's possible that they will now
/// be invalid given the latest advancement.
///
/// We need to be sure that we're making progress - it's possible that the
/// tree is so malformed that the transform never makes it to the
/// `TransformTypoExpr`.
///
/// Returns true if there are any untried correction combinations.
bool CheckAndAdvanceTypoExprCorrectionStreams() {
for (auto *TE : TypoExprs) {
auto &State = SemaRef.getTypoExprState(TE);
TransformCache.erase(TE);
if (!State.Consumer->hasMadeAnyCorrectionProgress())
return false;
if (!State.Consumer->finished())
return true;
State.Consumer->resetCorrectionStream();
}
return false;
}
NamedDecl *getDeclFromExpr(Expr *E) {
if (auto *OE = dyn_cast_or_null<OverloadExpr>(E))
E = OverloadResolution[OE];
if (!E)
return nullptr;
if (auto *DRE = dyn_cast<DeclRefExpr>(E))
return DRE->getFoundDecl();
if (auto *ME = dyn_cast<MemberExpr>(E))
return ME->getFoundDecl();
// FIXME: Add any other expr types that could be seen by the delayed typo
// correction TreeTransform for which the corresponding TypoCorrection could
// contain multiple decls.
return nullptr;
}
ExprResult TryTransform(Expr *E) {
Sema::SFINAETrap Trap(SemaRef);
ExprResult Res = TransformExpr(E);
if (Trap.hasErrorOccurred() || Res.isInvalid())
return ExprError();
return ExprFilter(Res.get());
}
// Since correcting typos may intoduce new TypoExprs, this function
// checks for new TypoExprs and recurses if it finds any. Note that it will
// only succeed if it is able to correct all typos in the given expression.
ExprResult CheckForRecursiveTypos(ExprResult Res, bool &IsAmbiguous) {
if (Res.isInvalid()) {
return Res;
}
// Check to see if any new TypoExprs were created. If so, we need to recurse
// to check their validity.
Expr *FixedExpr = Res.get();
auto SavedTypoExprs = std::move(TypoExprs);
auto SavedAmbiguousTypoExprs = std::move(AmbiguousTypoExprs);
TypoExprs.clear();
AmbiguousTypoExprs.clear();
FindTypoExprs(TypoExprs).TraverseStmt(FixedExpr);
if (!TypoExprs.empty()) {
// Recurse to handle newly created TypoExprs. If we're not able to
// handle them, discard these TypoExprs.
ExprResult RecurResult =
RecursiveTransformLoop(FixedExpr, IsAmbiguous);
if (RecurResult.isInvalid()) {
Res = ExprError();
// Recursive corrections didn't work, wipe them away and don't add
// them to the TypoExprs set. Remove them from Sema's TypoExpr list
// since we don't want to clear them twice. Note: it's possible the
// TypoExprs were created recursively and thus won't be in our
// Sema's TypoExprs - they were created in our `RecursiveTransformLoop`.
auto &SemaTypoExprs = SemaRef.TypoExprs;
for (auto *TE : TypoExprs) {
TransformCache.erase(TE);
SemaRef.clearDelayedTypo(TE);
auto SI = find(SemaTypoExprs, TE);
if (SI != SemaTypoExprs.end()) {
SemaTypoExprs.erase(SI);
}
}
} else {
// TypoExpr is valid: add newly created TypoExprs since we were
// able to correct them.
Res = RecurResult;
SavedTypoExprs.set_union(TypoExprs);
}
}
TypoExprs = std::move(SavedTypoExprs);
AmbiguousTypoExprs = std::move(SavedAmbiguousTypoExprs);
return Res;
}
// Try to transform the given expression, looping through the correction
// candidates with `CheckAndAdvanceTypoExprCorrectionStreams`.
//
// If valid ambiguous typo corrections are seen, `IsAmbiguous` is set to
// true and this method immediately will return an `ExprError`.
ExprResult RecursiveTransformLoop(Expr *E, bool &IsAmbiguous) {
ExprResult Res;
auto SavedTypoExprs = std::move(SemaRef.TypoExprs);
SemaRef.TypoExprs.clear();
while (true) {
Res = CheckForRecursiveTypos(TryTransform(E), IsAmbiguous);
// Recursion encountered an ambiguous correction. This means that our
// correction itself is ambiguous, so stop now.
if (IsAmbiguous)
break;
// If the transform is still valid after checking for any new typos,
// it's good to go.
if (!Res.isInvalid())
break;
// The transform was invalid, see if we have any TypoExprs with untried
// correction candidates.
if (!CheckAndAdvanceTypoExprCorrectionStreams())
break;
}
// If we found a valid result, double check to make sure it's not ambiguous.
if (!IsAmbiguous && !Res.isInvalid() && !AmbiguousTypoExprs.empty()) {
auto SavedTransformCache =
llvm::SmallDenseMap<TypoExpr *, ExprResult, 2>(TransformCache);
// Ensure none of the TypoExprs have multiple typo correction candidates
// with the same edit length that pass all the checks and filters.
while (!AmbiguousTypoExprs.empty()) {
auto TE = AmbiguousTypoExprs.back();
// TryTransform itself can create new Typos, adding them to the TypoExpr map
// and invalidating our TypoExprState, so always fetch it instead of storing.
SemaRef.getTypoExprState(TE).Consumer->saveCurrentPosition();
TypoCorrection TC = SemaRef.getTypoExprState(TE).Consumer->peekNextCorrection();
TypoCorrection Next;
do {
// Fetch the next correction by erasing the typo from the cache and calling
// `TryTransform` which will iterate through corrections in
// `TransformTypoExpr`.
TransformCache.erase(TE);
ExprResult AmbigRes = CheckForRecursiveTypos(TryTransform(E), IsAmbiguous);
if (!AmbigRes.isInvalid() || IsAmbiguous) {
SemaRef.getTypoExprState(TE).Consumer->resetCorrectionStream();
SavedTransformCache.erase(TE);
Res = ExprError();
IsAmbiguous = true;
break;
}
} while ((Next = SemaRef.getTypoExprState(TE).Consumer->peekNextCorrection()) &&
Next.getEditDistance(false) == TC.getEditDistance(false));
if (IsAmbiguous)
break;
AmbiguousTypoExprs.remove(TE);
SemaRef.getTypoExprState(TE).Consumer->restoreSavedPosition();
TransformCache[TE] = SavedTransformCache[TE];
}
TransformCache = std::move(SavedTransformCache);
}
// Wipe away any newly created TypoExprs that we don't know about. Since we
// clear any invalid TypoExprs in `CheckForRecursiveTypos`, this is only
// possible if a `TypoExpr` is created during a transformation but then
// fails before we can discover it.
auto &SemaTypoExprs = SemaRef.TypoExprs;
for (auto Iterator = SemaTypoExprs.begin(); Iterator != SemaTypoExprs.end();) {
auto TE = *Iterator;
auto FI = find(TypoExprs, TE);
if (FI != TypoExprs.end()) {
Iterator++;
continue;
}
SemaRef.clearDelayedTypo(TE);
Iterator = SemaTypoExprs.erase(Iterator);
}
SemaRef.TypoExprs = std::move(SavedTypoExprs);
return Res;
}
public:
TransformTypos(Sema &SemaRef, VarDecl *InitDecl, llvm::function_ref<ExprResult(Expr *)> Filter)
: BaseTransform(SemaRef), InitDecl(InitDecl), ExprFilter(Filter) {}
ExprResult RebuildCallExpr(Expr *Callee, SourceLocation LParenLoc,
MultiExprArg Args,
SourceLocation RParenLoc,
Expr *ExecConfig = nullptr) {
auto Result = BaseTransform::RebuildCallExpr(Callee, LParenLoc, Args,
RParenLoc, ExecConfig);
if (auto *OE = dyn_cast<OverloadExpr>(Callee)) {
if (Result.isUsable()) {
Expr *ResultCall = Result.get();
if (auto *BE = dyn_cast<CXXBindTemporaryExpr>(ResultCall))
ResultCall = BE->getSubExpr();
if (auto *CE = dyn_cast<CallExpr>(ResultCall))
OverloadResolution[OE] = CE->getCallee();
}
}
return Result;
}
ExprResult TransformLambdaExpr(LambdaExpr *E) { return Owned(E); }
ExprResult TransformBlockExpr(BlockExpr *E) { return Owned(E); }
ExprResult Transform(Expr *E) {
bool IsAmbiguous = false;
ExprResult Res = RecursiveTransformLoop(E, IsAmbiguous);
if (!Res.isUsable())
FindTypoExprs(TypoExprs).TraverseStmt(E);
EmitAllDiagnostics(IsAmbiguous);
return Res;
}
ExprResult TransformTypoExpr(TypoExpr *E) {
// If the TypoExpr hasn't been seen before, record it. Otherwise, return the
// cached transformation result if there is one and the TypoExpr isn't the
// first one that was encountered.
auto &CacheEntry = TransformCache[E];
if (!TypoExprs.insert(E) && !CacheEntry.isUnset()) {
return CacheEntry;
}
auto &State = SemaRef.getTypoExprState(E);
assert(State.Consumer && "Cannot transform a cleared TypoExpr");
// For the first TypoExpr and an uncached TypoExpr, find the next likely
// typo correction and return it.
while (TypoCorrection TC = State.Consumer->getNextCorrection()) {
if (InitDecl && TC.getFoundDecl() == InitDecl)
continue;
// FIXME: If we would typo-correct to an invalid declaration, it's
// probably best to just suppress all errors from this typo correction.
ExprResult NE = State.RecoveryHandler ?
State.RecoveryHandler(SemaRef, E, TC) :
attemptRecovery(SemaRef, *State.Consumer, TC);
if (!NE.isInvalid()) {
// Check whether there may be a second viable correction with the same
// edit distance; if so, remember this TypoExpr may have an ambiguous
// correction so it can be more thoroughly vetted later.
TypoCorrection Next;
if ((Next = State.Consumer->peekNextCorrection()) &&
Next.getEditDistance(false) == TC.getEditDistance(false)) {
AmbiguousTypoExprs.insert(E);
} else {
AmbiguousTypoExprs.remove(E);
}
assert(!NE.isUnset() &&
"Typo was transformed into a valid-but-null ExprResult");
return CacheEntry = NE;
}
}
return CacheEntry = ExprError();
}
};
}
ExprResult
Sema::CorrectDelayedTyposInExpr(Expr *E, VarDecl *InitDecl,
bool RecoverUncorrectedTypos,
llvm::function_ref<ExprResult(Expr *)> Filter) {
// If the current evaluation context indicates there are uncorrected typos
// and the current expression isn't guaranteed to not have typos, try to
// resolve any TypoExpr nodes that might be in the expression.
if (E && !ExprEvalContexts.empty() && ExprEvalContexts.back().NumTypos &&
(E->isTypeDependent() || E->isValueDependent() ||
E->isInstantiationDependent())) {
auto TyposResolved = DelayedTypos.size();
auto Result = TransformTypos(*this, InitDecl, Filter).Transform(E);
TyposResolved -= DelayedTypos.size();
if (Result.isInvalid() || Result.get() != E) {
ExprEvalContexts.back().NumTypos -= TyposResolved;
if (Result.isInvalid() && RecoverUncorrectedTypos) {
struct TyposReplace : TreeTransform<TyposReplace> {
TyposReplace(Sema &SemaRef) : TreeTransform(SemaRef) {}
ExprResult TransformTypoExpr(clang::TypoExpr *E) {
return this->SemaRef.CreateRecoveryExpr(E->getBeginLoc(),
E->getEndLoc(), {});
}
} TT(*this);
return TT.TransformExpr(E);
}
return Result;
}
assert(TyposResolved == 0 && "Corrected typo but got same Expr back?");
}
return E;
}
ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC,
bool DiscardedValue, bool IsConstexpr,
bool IsTemplateArgument) {
ExprResult FullExpr = FE;
if (!FullExpr.get())
return ExprError();
if (!IsTemplateArgument && DiagnoseUnexpandedParameterPack(FullExpr.get()))
return ExprError();
if (DiscardedValue) {
// Top-level expressions default to 'id' when we're in a debugger.
if (getLangOpts().DebuggerCastResultToId &&
FullExpr.get()->getType() == Context.UnknownAnyTy) {
FullExpr = forceUnknownAnyToType(FullExpr.get(), Context.getObjCIdType());
if (FullExpr.isInvalid())
return ExprError();
}
FullExpr = CheckPlaceholderExpr(FullExpr.get());
if (FullExpr.isInvalid())
return ExprError();
FullExpr = IgnoredValueConversions(FullExpr.get());
if (FullExpr.isInvalid())
return ExprError();
DiagnoseUnusedExprResult(FullExpr.get(), diag::warn_unused_expr);
}
FullExpr = CorrectDelayedTyposInExpr(FullExpr.get(), /*InitDecl=*/nullptr,
/*RecoverUncorrectedTypos=*/true);
if (FullExpr.isInvalid())
return ExprError();
CheckCompletedExpr(FullExpr.get(), CC, IsConstexpr);
// At the end of this full expression (which could be a deeply nested
// lambda), if there is a potential capture within the nested lambda,
// have the outer capture-able lambda try and capture it.
// Consider the following code:
// void f(int, int);
// void f(const int&, double);
// void foo() {
// const int x = 10, y = 20;
// auto L = [=](auto a) {
// auto M = [=](auto b) {
// f(x, b); <-- requires x to be captured by L and M
// f(y, a); <-- requires y to be captured by L, but not all Ms
// };
// };
// }
// FIXME: Also consider what happens for something like this that involves
// the gnu-extension statement-expressions or even lambda-init-captures:
// void f() {
// const int n = 0;
// auto L = [&](auto a) {
// +n + ({ 0; a; });
// };
// }
//
// Here, we see +n, and then the full-expression 0; ends, so we don't
// capture n (and instead remove it from our list of potential captures),
// and then the full-expression +n + ({ 0; }); ends, but it's too late
// for us to see that we need to capture n after all.
LambdaScopeInfo *const CurrentLSI =
getCurLambda(/*IgnoreCapturedRegions=*/true);
// FIXME: PR 17877 showed that getCurLambda() can return a valid pointer
// even if CurContext is not a lambda call operator. Refer to that Bug Report
// for an example of the code that might cause this asynchrony.
// By ensuring we are in the context of a lambda's call operator
// we can fix the bug (we only need to check whether we need to capture
// if we are within a lambda's body); but per the comments in that
// PR, a proper fix would entail :
// "Alternative suggestion:
// - Add to Sema an integer holding the smallest (outermost) scope
// index that we are *lexically* within, and save/restore/set to
// FunctionScopes.size() in InstantiatingTemplate's
// constructor/destructor.
// - Teach the handful of places that iterate over FunctionScopes to
// stop at the outermost enclosing lexical scope."
DeclContext *DC = CurContext;
while (isa_and_nonnull<CapturedDecl>(DC))
DC = DC->getParent();
const bool IsInLambdaDeclContext = isLambdaCallOperator(DC);
if (IsInLambdaDeclContext && CurrentLSI &&
CurrentLSI->hasPotentialCaptures() && !FullExpr.isInvalid())
CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures(FE, CurrentLSI,
*this);
return MaybeCreateExprWithCleanups(FullExpr);
}
StmtResult Sema::ActOnFinishFullStmt(Stmt *FullStmt) {
if (!FullStmt) return StmtError();
return MaybeCreateStmtWithCleanups(FullStmt);
}
Sema::IfExistsResult
Sema::CheckMicrosoftIfExistsSymbol(Scope *S,
CXXScopeSpec &SS,
const DeclarationNameInfo &TargetNameInfo) {
DeclarationName TargetName = TargetNameInfo.getName();
if (!TargetName)
return IER_DoesNotExist;
// If the name itself is dependent, then the result is dependent.
if (TargetName.isDependentName())
return IER_Dependent;
// Do the redeclaration lookup in the current scope.
LookupResult R(*this, TargetNameInfo, Sema::LookupAnyName,
RedeclarationKind::NotForRedeclaration);
LookupParsedName(R, S, &SS, /*ObjectType=*/QualType());
R.suppressDiagnostics();
switch (R.getResultKind()) {
case LookupResult::Found:
case LookupResult::FoundOverloaded:
case LookupResult::FoundUnresolvedValue:
case LookupResult::Ambiguous:
return IER_Exists;
case LookupResult::NotFound:
return IER_DoesNotExist;
case LookupResult::NotFoundInCurrentInstantiation:
return IER_Dependent;
}
llvm_unreachable("Invalid LookupResult Kind!");
}
Sema::IfExistsResult
Sema::CheckMicrosoftIfExistsSymbol(Scope *S, SourceLocation KeywordLoc,
bool IsIfExists, CXXScopeSpec &SS,
UnqualifiedId &Name) {
DeclarationNameInfo TargetNameInfo = GetNameFromUnqualifiedId(Name);
// Check for an unexpanded parameter pack.
auto UPPC = IsIfExists ? UPPC_IfExists : UPPC_IfNotExists;
if (DiagnoseUnexpandedParameterPack(SS, UPPC) ||
DiagnoseUnexpandedParameterPack(TargetNameInfo, UPPC))
return IER_Error;
return CheckMicrosoftIfExistsSymbol(S, SS, TargetNameInfo);
}
concepts::Requirement *Sema::ActOnSimpleRequirement(Expr *E) {
return BuildExprRequirement(E, /*IsSimple=*/true,
/*NoexceptLoc=*/SourceLocation(),
/*ReturnTypeRequirement=*/{});
}
concepts::Requirement *Sema::ActOnTypeRequirement(
SourceLocation TypenameKWLoc, CXXScopeSpec &SS, SourceLocation NameLoc,
const IdentifierInfo *TypeName, TemplateIdAnnotation *TemplateId) {
assert(((!TypeName && TemplateId) || (TypeName && !TemplateId)) &&
"Exactly one of TypeName and TemplateId must be specified.");
TypeSourceInfo *TSI = nullptr;
if (TypeName) {
QualType T =
CheckTypenameType(ElaboratedTypeKeyword::Typename, TypenameKWLoc,
SS.getWithLocInContext(Context), *TypeName, NameLoc,
&TSI, /*DeducedTSTContext=*/false);
if (T.isNull())
return nullptr;
} else {
ASTTemplateArgsPtr ArgsPtr(TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
TypeResult T = ActOnTypenameType(CurScope, TypenameKWLoc, SS,
TemplateId->TemplateKWLoc,
TemplateId->Template, TemplateId->Name,
TemplateId->TemplateNameLoc,
TemplateId->LAngleLoc, ArgsPtr,
TemplateId->RAngleLoc);
if (T.isInvalid())
return nullptr;
if (GetTypeFromParser(T.get(), &TSI).isNull())
return nullptr;
}
return BuildTypeRequirement(TSI);
}
concepts::Requirement *
Sema::ActOnCompoundRequirement(Expr *E, SourceLocation NoexceptLoc) {
return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc,
/*ReturnTypeRequirement=*/{});
}
concepts::Requirement *
Sema::ActOnCompoundRequirement(
Expr *E, SourceLocation NoexceptLoc, CXXScopeSpec &SS,
TemplateIdAnnotation *TypeConstraint, unsigned Depth) {
// C++2a [expr.prim.req.compound] p1.3.3
// [..] the expression is deduced against an invented function template
// F [...] F is a void function template with a single type template
// parameter T declared with the constrained-parameter. Form a new
// cv-qualifier-seq cv by taking the union of const and volatile specifiers
// around the constrained-parameter. F has a single parameter whose
// type-specifier is cv T followed by the abstract-declarator. [...]
//
// The cv part is done in the calling function - we get the concept with
// arguments and the abstract declarator with the correct CV qualification and
// have to synthesize T and the single parameter of F.
auto &II = Context.Idents.get("expr-type");
auto *TParam = TemplateTypeParmDecl::Create(Context, CurContext,
SourceLocation(),
SourceLocation(), Depth,
/*Index=*/0, &II,
/*Typename=*/true,
/*ParameterPack=*/false,
/*HasTypeConstraint=*/true);
if (BuildTypeConstraint(SS, TypeConstraint, TParam,
/*EllipsisLoc=*/SourceLocation(),
/*AllowUnexpandedPack=*/true))
// Just produce a requirement with no type requirements.
return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, {});
auto *TPL = TemplateParameterList::Create(Context, SourceLocation(),
SourceLocation(),
ArrayRef<NamedDecl *>(TParam),
SourceLocation(),
/*RequiresClause=*/nullptr);
return BuildExprRequirement(
E, /*IsSimple=*/false, NoexceptLoc,
concepts::ExprRequirement::ReturnTypeRequirement(TPL));
}
concepts::ExprRequirement *
Sema::BuildExprRequirement(
Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) {
auto Status = concepts::ExprRequirement::SS_Satisfied;
ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr;
if (E->isInstantiationDependent() || E->getType()->isPlaceholderType() ||
ReturnTypeRequirement.isDependent())
Status = concepts::ExprRequirement::SS_Dependent;
else if (NoexceptLoc.isValid() && canThrow(E) == CanThrowResult::CT_Can)
Status = concepts::ExprRequirement::SS_NoexceptNotMet;
else if (ReturnTypeRequirement.isSubstitutionFailure())
Status = concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure;
else if (ReturnTypeRequirement.isTypeConstraint()) {
// C++2a [expr.prim.req]p1.3.3
// The immediately-declared constraint ([temp]) of decltype((E)) shall
// be satisfied.
TemplateParameterList *TPL =
ReturnTypeRequirement.getTypeConstraintTemplateParameterList();
QualType MatchedType =
Context.getReferenceQualifiedType(E).getCanonicalType();
llvm::SmallVector<TemplateArgument, 1> Args;
Args.push_back(TemplateArgument(MatchedType));
auto *Param = cast<TemplateTypeParmDecl>(TPL->getParam(0));
MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/false);
MLTAL.addOuterRetainedLevels(TPL->getDepth());
const TypeConstraint *TC = Param->getTypeConstraint();
assert(TC && "Type Constraint cannot be null here");
auto *IDC = TC->getImmediatelyDeclaredConstraint();
assert(IDC && "ImmediatelyDeclaredConstraint can't be null here.");
ExprResult Constraint = SubstExpr(IDC, MLTAL);
if (Constraint.isInvalid()) {
return new (Context) concepts::ExprRequirement(
createSubstDiagAt(IDC->getExprLoc(),
[&](llvm::raw_ostream &OS) {
IDC->printPretty(OS, /*Helper=*/nullptr,
getPrintingPolicy());
}),
IsSimple, NoexceptLoc, ReturnTypeRequirement);
}
SubstitutedConstraintExpr =
cast<ConceptSpecializationExpr>(Constraint.get());
if (!SubstitutedConstraintExpr->isSatisfied())
Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied;
}
return new (Context) concepts::ExprRequirement(E, IsSimple, NoexceptLoc,
ReturnTypeRequirement, Status,
SubstitutedConstraintExpr);
}
concepts::ExprRequirement *
Sema::BuildExprRequirement(
concepts::Requirement::SubstitutionDiagnostic *ExprSubstitutionDiagnostic,
bool IsSimple, SourceLocation NoexceptLoc,
concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) {
return new (Context) concepts::ExprRequirement(ExprSubstitutionDiagnostic,
IsSimple, NoexceptLoc,
ReturnTypeRequirement);
}
concepts::TypeRequirement *
Sema::BuildTypeRequirement(TypeSourceInfo *Type) {
return new (Context) concepts::TypeRequirement(Type);
}
concepts::TypeRequirement *
Sema::BuildTypeRequirement(
concepts::Requirement::SubstitutionDiagnostic *SubstDiag) {
return new (Context) concepts::TypeRequirement(SubstDiag);
}
concepts::Requirement *Sema::ActOnNestedRequirement(Expr *Constraint) {
return BuildNestedRequirement(Constraint);
}
concepts::NestedRequirement *
Sema::BuildNestedRequirement(Expr *Constraint) {
ConstraintSatisfaction Satisfaction;
if (!Constraint->isInstantiationDependent() &&
CheckConstraintSatisfaction(nullptr, AssociatedConstraint(Constraint),
/*TemplateArgs=*/{},
Constraint->getSourceRange(), Satisfaction))
return nullptr;
return new (Context) concepts::NestedRequirement(Context, Constraint,
Satisfaction);
}
concepts::NestedRequirement *
Sema::BuildNestedRequirement(StringRef InvalidConstraintEntity,
const ASTConstraintSatisfaction &Satisfaction) {
return new (Context) concepts::NestedRequirement(
InvalidConstraintEntity,
ASTConstraintSatisfaction::Rebuild(Context, Satisfaction));
}
RequiresExprBodyDecl *
Sema::ActOnStartRequiresExpr(SourceLocation RequiresKWLoc,
ArrayRef<ParmVarDecl *> LocalParameters,
Scope *BodyScope) {
assert(BodyScope);
RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create(Context, CurContext,
RequiresKWLoc);
PushDeclContext(BodyScope, Body);
for (ParmVarDecl *Param : LocalParameters) {
if (Param->getType()->isVoidType()) {
if (LocalParameters.size() > 1) {
Diag(Param->getBeginLoc(), diag::err_void_only_param);
Param->setType(Context.IntTy);
} else if (Param->getIdentifier()) {
Diag(Param->getBeginLoc(), diag::err_param_with_void_type);
Param->setType(Context.IntTy);
} else if (Param->getType().hasQualifiers()) {
Diag(Param->getBeginLoc(), diag::err_void_param_qualified);
}
} else if (Param->hasDefaultArg()) {
// C++2a [expr.prim.req] p4
// [...] A local parameter of a requires-expression shall not have a
// default argument. [...]
Diag(Param->getDefaultArgRange().getBegin(),
diag::err_requires_expr_local_parameter_default_argument);
// Ignore default argument and move on
} else if (Param->isExplicitObjectParameter()) {
// C++23 [dcl.fct]p6:
// An explicit-object-parameter-declaration is a parameter-declaration
// with a this specifier. An explicit-object-parameter-declaration
// shall appear only as the first parameter-declaration of a
// parameter-declaration-list of either:
// - a member-declarator that declares a member function, or
// - a lambda-declarator.
//
// The parameter-declaration-list of a requires-expression is not such
// a context.
Diag(Param->getExplicitObjectParamThisLoc(),
diag::err_requires_expr_explicit_object_parameter);
Param->setExplicitObjectParameterLoc(SourceLocation());
}
Param->setDeclContext(Body);
// If this has an identifier, add it to the scope stack.
if (Param->getIdentifier()) {
CheckShadow(BodyScope, Param);
PushOnScopeChains(Param, BodyScope);
}
}
return Body;
}
void Sema::ActOnFinishRequiresExpr() {
assert(CurContext && "DeclContext imbalance!");
CurContext = CurContext->getLexicalParent();
assert(CurContext && "Popped translation unit!");
}
ExprResult Sema::ActOnRequiresExpr(
SourceLocation RequiresKWLoc, RequiresExprBodyDecl *Body,
SourceLocation LParenLoc, ArrayRef<ParmVarDecl *> LocalParameters,
SourceLocation RParenLoc, ArrayRef<concepts::Requirement *> Requirements,
SourceLocation ClosingBraceLoc) {
auto *RE = RequiresExpr::Create(Context, RequiresKWLoc, Body, LParenLoc,
LocalParameters, RParenLoc, Requirements,
ClosingBraceLoc);
if (DiagnoseUnexpandedParameterPackInRequiresExpr(RE))
return ExprError();
return RE;
}