mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-29 15:26:07 +00:00
Bring inheriting constructor implementation up-to-date with current defect
reports, and implement implicit definition of inheriting constructors. Remaining missing features: inheriting constructor templates, implicit exception specifications for inheriting constructors, inheriting constructors from dependent bases. llvm-svn: 177320
This commit is contained in:
parent
260e28d65a
commit
c2bc61b006
@ -2863,6 +2863,9 @@ public:
|
||||
assert(i < NumArgs && "Invalid argument number!");
|
||||
return arg_type_begin()[i];
|
||||
}
|
||||
ArrayRef<QualType> getArgTypes() const {
|
||||
return ArrayRef<QualType>(arg_type_begin(), arg_type_end());
|
||||
}
|
||||
|
||||
ExtProtoInfo getExtProtoInfo() const {
|
||||
ExtProtoInfo EPI;
|
||||
|
@ -254,19 +254,20 @@ def note_using_decl_constructor_conflict_previous_ctor : Note<
|
||||
"previous constructor">;
|
||||
def note_using_decl_constructor_conflict_previous_using : Note<
|
||||
"previously inherited here">;
|
||||
def warn_using_decl_constructor_ellipsis : Warning<
|
||||
"inheriting constructor does not inherit ellipsis">,
|
||||
InGroup<DiagGroup<"inherited-variadic-ctor">>;
|
||||
def note_using_decl_constructor_ellipsis : Note<
|
||||
"constructor declared with ellipsis here">;
|
||||
def err_using_decl_can_not_refer_to_class_member : Error<
|
||||
"using declaration can not refer to class member">;
|
||||
def err_using_decl_can_not_refer_to_namespace : Error<
|
||||
"using declaration can not refer to namespace">;
|
||||
def err_using_decl_constructor : Error<
|
||||
"using declaration can not refer to a constructor">;
|
||||
def err_using_decl_constructor_unsupported : Error<
|
||||
"inheriting constructors are not supported">;
|
||||
// FIXME: Replace the above error with this warning if support for
|
||||
// inheriting constructors is implemented.
|
||||
//def warn_cxx98_compat_using_decl_constructor : Warning<
|
||||
// "inheriting constructors are incompatible with C++98">,
|
||||
// InGroup<CXX98Compat>, DefaultIgnore;
|
||||
def warn_cxx98_compat_using_decl_constructor : Warning<
|
||||
"inheriting constructors are incompatible with C++98">,
|
||||
InGroup<CXX98Compat>, DefaultIgnore;
|
||||
def err_using_decl_destructor : Error<
|
||||
"using declaration can not refer to a destructor">;
|
||||
def err_using_decl_template_id : Error<
|
||||
@ -1104,9 +1105,11 @@ def note_member_synthesized_at : Note<
|
||||
"implicit default %select{constructor|copy constructor|move constructor|copy "
|
||||
"assignment operator|move assignment operator|destructor}0 for %1 first "
|
||||
"required here">;
|
||||
def note_inhctor_synthesized_at : Note<
|
||||
"inheriting constructor for %0 first required here">;
|
||||
def err_missing_default_ctor : Error<
|
||||
"%select{|implicit default }0constructor for %1 must explicitly initialize "
|
||||
"the %select{base class|member}2 %3 which does not have a default "
|
||||
"%select{|implicit default |inheriting }0constructor for %1 must explicitly "
|
||||
"initialize the %select{base class|member}2 %3 which does not have a default "
|
||||
"constructor">;
|
||||
|
||||
def err_illegal_union_or_anon_struct_member : Error<
|
||||
@ -2274,8 +2277,8 @@ def err_uninitialized_member_for_assign : Error<
|
||||
"non-static %select{reference|const}1 member %2 can't use default "
|
||||
"assignment operator">;
|
||||
def err_uninitialized_member_in_ctor : Error<
|
||||
"%select{|implicit default }0constructor for %1 must explicitly initialize "
|
||||
"the %select{reference|const}2 member %3">;
|
||||
"%select{|implicit default |inheriting }0constructor for %1 must explicitly "
|
||||
"initialize the %select{reference|const}2 member %3">;
|
||||
def err_default_arg_makes_ctor_special : Error<
|
||||
"addition of default argument on redeclaration makes this constructor a "
|
||||
"%select{default|copy|move}0 constructor">;
|
||||
|
@ -3597,6 +3597,11 @@ public:
|
||||
ImplicitExceptionSpecification
|
||||
ComputeDefaultedDtorExceptionSpec(CXXMethodDecl *MD);
|
||||
|
||||
/// \brief Determine what sort of exception specification an inheriting
|
||||
/// constructor of a class will have.
|
||||
ImplicitExceptionSpecification
|
||||
ComputeInheritingCtorExceptionSpec(CXXMethodDecl *MD);
|
||||
|
||||
/// \brief Evaluate the implicit exception specification for a defaulted
|
||||
/// special member function.
|
||||
void EvaluateImplicitExceptionSpec(SourceLocation Loc, CXXMethodDecl *MD);
|
||||
@ -3649,11 +3654,15 @@ public:
|
||||
void AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl,
|
||||
CXXDestructorDecl *Destructor);
|
||||
|
||||
/// \brief Declare all inherited constructors for the given class.
|
||||
/// \brief Declare all inheriting constructors for the given class.
|
||||
///
|
||||
/// \param ClassDecl The class declaration into which the inherited
|
||||
/// \param ClassDecl The class declaration into which the inheriting
|
||||
/// constructors will be added.
|
||||
void DeclareInheritedConstructors(CXXRecordDecl *ClassDecl);
|
||||
void DeclareInheritingConstructors(CXXRecordDecl *ClassDecl);
|
||||
|
||||
/// \brief Define the specified inheriting constructor.
|
||||
void DefineInheritingConstructor(SourceLocation UseLoc,
|
||||
CXXConstructorDecl *Constructor);
|
||||
|
||||
/// \brief Declare the implicit copy constructor for the given class.
|
||||
///
|
||||
|
@ -2642,9 +2642,10 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo,
|
||||
}
|
||||
|
||||
// Create a static_cast\<T&&>(expr).
|
||||
static Expr *CastForMoving(Sema &SemaRef, Expr *E) {
|
||||
QualType ExprType = E->getType();
|
||||
QualType TargetType = SemaRef.Context.getRValueReferenceType(ExprType);
|
||||
static Expr *CastForMoving(Sema &SemaRef, Expr *E, QualType T = QualType()) {
|
||||
if (T.isNull()) T = E->getType();
|
||||
QualType TargetType = SemaRef.BuildReferenceType(
|
||||
T, /*SpelledAsLValue*/false, SourceLocation(), DeclarationName());
|
||||
SourceLocation ExprLoc = E->getLocStart();
|
||||
TypeSourceInfo *TargetLoc = SemaRef.Context.getTrivialTypeSourceInfo(
|
||||
TargetType, ExprLoc);
|
||||
@ -2659,7 +2660,8 @@ static Expr *CastForMoving(Sema &SemaRef, Expr *E) {
|
||||
enum ImplicitInitializerKind {
|
||||
IIK_Default,
|
||||
IIK_Copy,
|
||||
IIK_Move
|
||||
IIK_Move,
|
||||
IIK_Inherit
|
||||
};
|
||||
|
||||
static bool
|
||||
@ -2675,6 +2677,35 @@ BuildImplicitBaseInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor,
|
||||
ExprResult BaseInit;
|
||||
|
||||
switch (ImplicitInitKind) {
|
||||
case IIK_Inherit: {
|
||||
const CXXRecordDecl *Inherited =
|
||||
Constructor->getInheritedConstructor()->getParent();
|
||||
const CXXRecordDecl *Base = BaseSpec->getType()->getAsCXXRecordDecl();
|
||||
if (Base && Inherited->getCanonicalDecl() == Base->getCanonicalDecl()) {
|
||||
// C++11 [class.inhctor]p8:
|
||||
// Each expression in the expression-list is of the form
|
||||
// static_cast<T&&>(p), where p is the name of the corresponding
|
||||
// constructor parameter and T is the declared type of p.
|
||||
SmallVector<Expr*, 16> Args;
|
||||
for (unsigned I = 0, E = Constructor->getNumParams(); I != E; ++I) {
|
||||
ParmVarDecl *PD = Constructor->getParamDecl(I);
|
||||
ExprResult ArgExpr =
|
||||
SemaRef.BuildDeclRefExpr(PD, PD->getType().getNonReferenceType(),
|
||||
VK_LValue, SourceLocation());
|
||||
if (ArgExpr.isInvalid())
|
||||
return true;
|
||||
Args.push_back(CastForMoving(SemaRef, ArgExpr.take(), PD->getType()));
|
||||
}
|
||||
|
||||
InitializationKind InitKind = InitializationKind::CreateDirect(
|
||||
Constructor->getLocation(), SourceLocation(), SourceLocation());
|
||||
InitializationSequence InitSeq(SemaRef, InitEntity, InitKind,
|
||||
Args.data(), Args.size());
|
||||
BaseInit = InitSeq.Perform(SemaRef, InitEntity, InitKind, Args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Fall through.
|
||||
case IIK_Default: {
|
||||
InitializationKind InitKind
|
||||
= InitializationKind::CreateDefault(Constructor->getLocation());
|
||||
@ -2897,7 +2928,8 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor,
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(ImplicitInitKind == IIK_Default && "Unhandled implicit init kind!");
|
||||
assert((ImplicitInitKind == IIK_Default || ImplicitInitKind == IIK_Inherit) &&
|
||||
"Unhandled implicit init kind!");
|
||||
|
||||
QualType FieldBaseElementType =
|
||||
SemaRef.Context.getBaseElementType(Field->getType());
|
||||
@ -2988,6 +3020,8 @@ struct BaseAndFieldInfo {
|
||||
IIK = IIK_Copy;
|
||||
else if (Generated && Ctor->isMoveConstructor())
|
||||
IIK = IIK_Move;
|
||||
else if (Ctor->getInheritedConstructor())
|
||||
IIK = IIK_Inherit;
|
||||
else
|
||||
IIK = IIK_Default;
|
||||
}
|
||||
@ -2999,6 +3033,7 @@ struct BaseAndFieldInfo {
|
||||
return true;
|
||||
|
||||
case IIK_Default:
|
||||
case IIK_Inherit:
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3226,7 +3261,7 @@ bool Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, bool AnyErrors,
|
||||
// If we're not generating the implicit copy/move constructor, then we'll
|
||||
// handle anonymous struct/union fields based on their individual
|
||||
// indirect fields.
|
||||
if (F->isAnonymousStructOrUnion() && Info.IIK == IIK_Default)
|
||||
if (F->isAnonymousStructOrUnion() && !Info.isImplicitCopyOrMove())
|
||||
continue;
|
||||
|
||||
if (CollectFieldInitializer(*this, Info, F))
|
||||
@ -3235,7 +3270,7 @@ bool Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, bool AnyErrors,
|
||||
}
|
||||
|
||||
// Beyond this point, we only consider default initialization.
|
||||
if (Info.IIK != IIK_Default)
|
||||
if (Info.isImplicitCopyOrMove())
|
||||
continue;
|
||||
|
||||
if (IndirectFieldDecl *F = dyn_cast<IndirectFieldDecl>(*Mem)) {
|
||||
@ -4062,14 +4097,14 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
|
||||
}
|
||||
}
|
||||
|
||||
// Declare inherited constructors. We do this eagerly here because:
|
||||
// - The standard requires an eager diagnostic for conflicting inherited
|
||||
// Declare inheriting constructors. We do this eagerly here because:
|
||||
// - The standard requires an eager diagnostic for conflicting inheriting
|
||||
// constructors from different classes.
|
||||
// - The lazy declaration of the other implicit constructors is so as to not
|
||||
// waste space and performance on classes that are not meant to be
|
||||
// instantiated (e.g. meta-functions). This doesn't apply to classes that
|
||||
// have inherited constructors.
|
||||
DeclareInheritedConstructors(Record);
|
||||
// have inheriting constructors.
|
||||
DeclareInheritingConstructors(Record);
|
||||
}
|
||||
|
||||
/// Is the special member function which would be selected to perform the
|
||||
@ -4185,7 +4220,9 @@ computeImplicitExceptionSpec(Sema &S, SourceLocation Loc, CXXMethodDecl *MD) {
|
||||
case Sema::CXXInvalid:
|
||||
break;
|
||||
}
|
||||
llvm_unreachable("only special members have implicit exception specs");
|
||||
assert(cast<CXXConstructorDecl>(MD)->getInheritedConstructor() &&
|
||||
"only special members have implicit exception specs");
|
||||
return S.ComputeInheritingCtorExceptionSpec(cast<CXXConstructorDecl>(MD));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -4194,10 +4231,7 @@ updateExceptionSpec(Sema &S, FunctionDecl *FD, const FunctionProtoType *FPT,
|
||||
FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
|
||||
ExceptSpec.getEPI(EPI);
|
||||
const FunctionProtoType *NewFPT = cast<FunctionProtoType>(
|
||||
S.Context.getFunctionType(FPT->getResultType(),
|
||||
ArrayRef<QualType>(FPT->arg_type_begin(),
|
||||
FPT->getNumArgs()),
|
||||
EPI));
|
||||
S.Context.getFunctionType(FPT->getResultType(), FPT->getArgTypes(), EPI));
|
||||
FD->setType(QualType(NewFPT, 0));
|
||||
}
|
||||
|
||||
@ -5644,10 +5678,7 @@ QualType Sema::CheckConstructorDeclarator(Declarator &D, QualType R,
|
||||
EPI.TypeQuals = 0;
|
||||
EPI.RefQualifier = RQ_None;
|
||||
|
||||
return Context.getFunctionType(Context.VoidTy,
|
||||
ArrayRef<QualType>(Proto->arg_type_begin(),
|
||||
Proto->getNumArgs()),
|
||||
EPI);
|
||||
return Context.getFunctionType(Context.VoidTy, Proto->getArgTypes(), EPI);
|
||||
}
|
||||
|
||||
/// CheckConstructor - Checks a fully-formed constructor for
|
||||
@ -6519,9 +6550,7 @@ Decl *Sema::ActOnUsingDeclaration(Scope *S,
|
||||
// C++11 inheriting constructors.
|
||||
Diag(Name.getLocStart(),
|
||||
getLangOpts().CPlusPlus11 ?
|
||||
// FIXME: Produce warn_cxx98_compat_using_decl_constructor
|
||||
// instead once inheriting constructors work.
|
||||
diag::err_using_decl_constructor_unsupported :
|
||||
diag::warn_cxx98_compat_using_decl_constructor :
|
||||
diag::err_using_decl_constructor)
|
||||
<< SS.getRange();
|
||||
|
||||
@ -6545,7 +6574,7 @@ Decl *Sema::ActOnUsingDeclaration(Scope *S,
|
||||
if (!TargetName)
|
||||
return 0;
|
||||
|
||||
// Warn about using declarations.
|
||||
// Warn about access declarations.
|
||||
// TODO: store that the declaration was written without 'using' and
|
||||
// talk about access decls instead of using decls in the
|
||||
// diagnostics.
|
||||
@ -7471,6 +7500,13 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(SourceLocation Loc,
|
||||
return ExceptSpec;
|
||||
}
|
||||
|
||||
Sema::ImplicitExceptionSpecification
|
||||
Sema::ComputeInheritingCtorExceptionSpec(CXXMethodDecl *MD) {
|
||||
ImplicitExceptionSpecification ExceptSpec(*this);
|
||||
// FIXME: Compute the exception spec.
|
||||
return ExceptSpec;
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// RAII object to register a special member as being currently declared.
|
||||
struct DeclaringSpecialMember {
|
||||
@ -7597,7 +7633,7 @@ void Sema::ActOnFinishDelayedMemberInitializers(Decl *D) {
|
||||
CheckDelayedExplicitlyDefaultedMemberExceptionSpecs();
|
||||
}
|
||||
|
||||
void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) {
|
||||
void Sema::DeclareInheritingConstructors(CXXRecordDecl *ClassDecl) {
|
||||
// We start with an initial pass over the base classes to collect those that
|
||||
// inherit constructors from. If there are none, we can forgo all further
|
||||
// processing.
|
||||
@ -7612,6 +7648,8 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) {
|
||||
// If we inherit constructors from anything that is dependent, just
|
||||
// abort processing altogether. We'll get another chance for the
|
||||
// instantiations.
|
||||
// FIXME: We need to ensure that any call to a constructor of this class
|
||||
// is considered instantiation-dependent in this case.
|
||||
return;
|
||||
}
|
||||
BasesToInheritFrom.push_back(Base->castAs<RecordType>());
|
||||
@ -7620,18 +7658,19 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) {
|
||||
if (BasesToInheritFrom.empty())
|
||||
return;
|
||||
|
||||
// FIXME: Constructor templates.
|
||||
|
||||
// Now collect the constructors that we already have in the current class.
|
||||
// Those take precedence over inherited constructors.
|
||||
// C++0x [class.inhctor]p3: [...] a constructor is implicitly declared [...]
|
||||
// C++11 [class.inhctor]p3: [...] a constructor is implicitly declared [...]
|
||||
// unless there is a user-declared constructor with the same signature in
|
||||
// the class where the using-declaration appears.
|
||||
llvm::SmallSet<const Type *, 8> ExistingConstructors;
|
||||
for (CXXRecordDecl::ctor_iterator CtorIt = ClassDecl->ctor_begin(),
|
||||
CtorE = ClassDecl->ctor_end();
|
||||
CtorIt != CtorE; ++CtorIt) {
|
||||
CtorIt != CtorE; ++CtorIt)
|
||||
ExistingConstructors.insert(
|
||||
Context.getCanonicalType(CtorIt->getType()).getTypePtr());
|
||||
}
|
||||
|
||||
DeclarationName CreatedCtorName =
|
||||
Context.DeclarationNames.getCXXConstructorName(
|
||||
@ -7663,62 +7702,71 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) {
|
||||
SourceLocation UsingLoc = UD ? UD->getLocation() :
|
||||
ClassDecl->getLocation();
|
||||
|
||||
// C++0x [class.inhctor]p1: The candidate set of inherited constructors
|
||||
// from the class X named in the using-declaration consists of actual
|
||||
// constructors and notional constructors that result from the
|
||||
// transformation of defaulted parameters as follows:
|
||||
// - all non-template default constructors of X, and
|
||||
// C++11 [class.inhctor]p1:
|
||||
// The candidate set of inherited constructors from the class X named in
|
||||
// the using-declaration consists of actual constructors and notional
|
||||
// constructors that result from the transformation of defaulted
|
||||
// parameters as follows:
|
||||
// - all non-template constructors of X, and
|
||||
// - for each non-template constructor of X that has at least one
|
||||
// parameter with a default argument, the set of constructors that
|
||||
// results from omitting any ellipsis parameter specification and
|
||||
// successively omitting parameters with a default argument from the
|
||||
// end of the parameter-type-list.
|
||||
// end of the parameter-type-list, and
|
||||
// FIXME: ...also constructor templates.
|
||||
CXXConstructorDecl *BaseCtor = *CtorIt;
|
||||
bool CanBeCopyOrMove = BaseCtor->isCopyOrMoveConstructor();
|
||||
const FunctionProtoType *BaseCtorType =
|
||||
BaseCtor->getType()->getAs<FunctionProtoType>();
|
||||
|
||||
for (unsigned params = BaseCtor->getMinRequiredArguments(),
|
||||
maxParams = BaseCtor->getNumParams();
|
||||
params <= maxParams; ++params) {
|
||||
// Determine whether this would be a copy or move constructor for the
|
||||
// derived class.
|
||||
if (BaseCtorType->getNumArgs() >= 1 &&
|
||||
BaseCtorType->getArgType(0)->isReferenceType() &&
|
||||
Context.hasSameUnqualifiedType(
|
||||
BaseCtorType->getArgType(0)->getPointeeType(),
|
||||
Context.getTagDeclType(ClassDecl)))
|
||||
CanBeCopyOrMove = true;
|
||||
|
||||
ArrayRef<QualType> ArgTypes(BaseCtorType->getArgTypes());
|
||||
FunctionProtoType::ExtProtoInfo EPI = BaseCtorType->getExtProtoInfo();
|
||||
// Core issue (no number yet): the ellipsis is always discarded.
|
||||
if (EPI.Variadic) {
|
||||
Diag(UsingLoc, diag::warn_using_decl_constructor_ellipsis);
|
||||
Diag(BaseCtor->getLocation(),
|
||||
diag::note_using_decl_constructor_ellipsis);
|
||||
EPI.Variadic = false;
|
||||
}
|
||||
|
||||
for (unsigned Params = BaseCtor->getMinRequiredArguments(),
|
||||
MaxParams = BaseCtor->getNumParams();
|
||||
Params <= MaxParams; ++Params) {
|
||||
// Skip default constructors. They're never inherited.
|
||||
if (params == 0)
|
||||
if (Params == 0)
|
||||
continue;
|
||||
// Skip copy and move constructors for the same reason.
|
||||
if (CanBeCopyOrMove && params == 1)
|
||||
|
||||
// Skip copy and move constructors for both base and derived class
|
||||
// for the same reason.
|
||||
if (CanBeCopyOrMove && Params == 1)
|
||||
continue;
|
||||
|
||||
// Build up a function type for this particular constructor.
|
||||
// FIXME: The working paper does not consider that the exception spec
|
||||
// for the inheriting constructor might be larger than that of the
|
||||
// source. This code doesn't yet, either. When it does, this code will
|
||||
// need to be delayed until after exception specifications and in-class
|
||||
// member initializers are attached.
|
||||
const Type *NewCtorType;
|
||||
if (params == maxParams)
|
||||
NewCtorType = BaseCtorType;
|
||||
else {
|
||||
SmallVector<QualType, 16> Args;
|
||||
for (unsigned i = 0; i < params; ++i) {
|
||||
Args.push_back(BaseCtorType->getArgType(i));
|
||||
}
|
||||
FunctionProtoType::ExtProtoInfo ExtInfo =
|
||||
BaseCtorType->getExtProtoInfo();
|
||||
ExtInfo.Variadic = false;
|
||||
NewCtorType = Context.getFunctionType(BaseCtorType->getResultType(),
|
||||
Args, ExtInfo)
|
||||
.getTypePtr();
|
||||
}
|
||||
QualType NewCtorType =
|
||||
Context.getFunctionType(Context.VoidTy, ArgTypes.slice(0, Params),
|
||||
EPI);
|
||||
const Type *CanonicalNewCtorType =
|
||||
Context.getCanonicalType(NewCtorType);
|
||||
Context.getCanonicalType(NewCtorType).getTypePtr();
|
||||
|
||||
// Now that we have the type, first check if the class already has a
|
||||
// constructor with this signature.
|
||||
// C++11 [class.inhctor]p3:
|
||||
// ... a constructor is implicitly declared with the same constructor
|
||||
// characteristics unless there is a user-declared constructor with
|
||||
// the same signature in the class where the using-declaration appears
|
||||
if (ExistingConstructors.count(CanonicalNewCtorType))
|
||||
continue;
|
||||
|
||||
// Then we check if we have already declared an inherited constructor
|
||||
// with this signature.
|
||||
// C++11 [class.inhctor]p7:
|
||||
// If two using-declarations declare inheriting constructors with the
|
||||
// same signature, the program is ill-formed
|
||||
std::pair<ConstructorToSourceMap::iterator, bool> result =
|
||||
InheritedConstructors.insert(std::make_pair(
|
||||
CanonicalNewCtorType,
|
||||
@ -7740,35 +7788,47 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) {
|
||||
diag::note_using_decl_constructor_conflict_previous_ctor);
|
||||
Diag(PrevCtor->getLocation(),
|
||||
diag::note_using_decl_constructor_conflict_previous_using);
|
||||
} else {
|
||||
// Core issue (no number): if the same inheriting constructor is
|
||||
// produced by multiple base class constructors from the same base
|
||||
// class, the inheriting constructor is defined as deleted.
|
||||
result.first->second.second->setDeletedAsWritten();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// OK, we're there, now add the constructor.
|
||||
// C++0x [class.inhctor]p8: [...] that would be performed by a
|
||||
// user-written inline constructor [...]
|
||||
DeclarationNameInfo DNI(CreatedCtorName, UsingLoc);
|
||||
CXXConstructorDecl *NewCtor = CXXConstructorDecl::Create(
|
||||
Context, ClassDecl, UsingLoc, DNI, QualType(NewCtorType, 0),
|
||||
Context, ClassDecl, UsingLoc, DNI, NewCtorType,
|
||||
/*TInfo=*/0, BaseCtor->isExplicit(), /*Inline=*/true,
|
||||
/*ImplicitlyDeclared=*/true,
|
||||
// FIXME: Due to a defect in the standard, we treat inherited
|
||||
// constructors as constexpr even if that makes them ill-formed.
|
||||
/*Constexpr=*/BaseCtor->isConstexpr());
|
||||
/*ImplicitlyDeclared=*/true, /*Constexpr=*/BaseCtor->isConstexpr());
|
||||
NewCtor->setAccess(BaseCtor->getAccess());
|
||||
|
||||
// Build an unevaluated exception specification for this constructor.
|
||||
EPI.ExceptionSpecType = EST_Unevaluated;
|
||||
EPI.ExceptionSpecDecl = NewCtor;
|
||||
NewCtor->setType(Context.getFunctionType(Context.VoidTy,
|
||||
ArgTypes.slice(0, Params),
|
||||
EPI));
|
||||
|
||||
// Build up the parameter decls and add them.
|
||||
SmallVector<ParmVarDecl *, 16> ParamDecls;
|
||||
for (unsigned i = 0; i < params; ++i) {
|
||||
ParamDecls.push_back(ParmVarDecl::Create(Context, NewCtor,
|
||||
UsingLoc, UsingLoc,
|
||||
/*IdentifierInfo=*/0,
|
||||
BaseCtorType->getArgType(i),
|
||||
/*TInfo=*/0, SC_None,
|
||||
SC_None, /*DefaultArg=*/0));
|
||||
for (unsigned i = 0; i < Params; ++i) {
|
||||
ParmVarDecl *PD = ParmVarDecl::Create(Context, NewCtor,
|
||||
UsingLoc, UsingLoc,
|
||||
/*IdentifierInfo=*/0,
|
||||
BaseCtorType->getArgType(i),
|
||||
/*TInfo=*/0, SC_None,
|
||||
SC_None, /*DefaultArg=*/0);
|
||||
PD->setScopeInfo(0, i);
|
||||
PD->setImplicit();
|
||||
ParamDecls.push_back(PD);
|
||||
}
|
||||
NewCtor->setParams(ParamDecls);
|
||||
NewCtor->setInheritedConstructor(BaseCtor);
|
||||
if (BaseCtor->isDeleted())
|
||||
NewCtor->setDeletedAsWritten();
|
||||
|
||||
ClassDecl->addDecl(NewCtor);
|
||||
result.first->second.second = NewCtor;
|
||||
@ -7777,6 +7837,35 @@ void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) {
|
||||
}
|
||||
}
|
||||
|
||||
void Sema::DefineInheritingConstructor(SourceLocation CurrentLocation,
|
||||
CXXConstructorDecl *Constructor) {
|
||||
CXXRecordDecl *ClassDecl = Constructor->getParent();
|
||||
assert(Constructor->getInheritedConstructor() &&
|
||||
!Constructor->doesThisDeclarationHaveABody() &&
|
||||
!Constructor->isDeleted());
|
||||
|
||||
SynthesizedFunctionScope Scope(*this, Constructor);
|
||||
DiagnosticErrorTrap Trap(Diags);
|
||||
if (SetCtorInitializers(Constructor, /*AnyErrors=*/false) ||
|
||||
Trap.hasErrorOccurred()) {
|
||||
Diag(CurrentLocation, diag::note_inhctor_synthesized_at)
|
||||
<< Context.getTagDeclType(ClassDecl);
|
||||
Constructor->setInvalidDecl();
|
||||
return;
|
||||
}
|
||||
|
||||
SourceLocation Loc = Constructor->getLocation();
|
||||
Constructor->setBody(new (Context) CompoundStmt(Loc));
|
||||
|
||||
Constructor->setUsed();
|
||||
MarkVTableUsed(CurrentLocation, ClassDecl);
|
||||
|
||||
if (ASTMutationListener *L = getASTMutationListener()) {
|
||||
L->CompletedImplicitDefinition(Constructor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Sema::ImplicitExceptionSpecification
|
||||
Sema::ComputeDefaultedDtorExceptionSpec(CXXMethodDecl *MD) {
|
||||
CXXRecordDecl *ClassDecl = MD->getParent();
|
||||
|
@ -10501,6 +10501,9 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) {
|
||||
if (!Constructor->isUsed(false))
|
||||
DefineImplicitMoveConstructor(Loc, Constructor);
|
||||
}
|
||||
} else if (Constructor->getInheritedConstructor()) {
|
||||
if (!Constructor->isUsed(false))
|
||||
DefineInheritingConstructor(Loc, Constructor);
|
||||
}
|
||||
|
||||
MarkVTableUsed(Loc, Constructor->getParent());
|
||||
|
@ -5825,7 +5825,8 @@ bool InitializationSequence::Diagnose(Sema &S,
|
||||
= cast<CXXConstructorDecl>(S.CurContext);
|
||||
if (Entity.getKind() == InitializedEntity::EK_Base) {
|
||||
S.Diag(Kind.getLocation(), diag::err_missing_default_ctor)
|
||||
<< Constructor->isImplicit()
|
||||
<< (Constructor->getInheritedConstructor() ? 2 :
|
||||
Constructor->isImplicit() ? 1 : 0)
|
||||
<< S.Context.getTypeDeclType(Constructor->getParent())
|
||||
<< /*base=*/0
|
||||
<< Entity.getType();
|
||||
@ -5837,7 +5838,8 @@ bool InitializationSequence::Diagnose(Sema &S,
|
||||
<< S.Context.getTagDeclType(BaseDecl);
|
||||
} else {
|
||||
S.Diag(Kind.getLocation(), diag::err_missing_default_ctor)
|
||||
<< Constructor->isImplicit()
|
||||
<< (Constructor->getInheritedConstructor() ? 2 :
|
||||
Constructor->isImplicit() ? 1 : 0)
|
||||
<< S.Context.getTypeDeclType(Constructor->getParent())
|
||||
<< /*member=*/1
|
||||
<< Entity.getName();
|
||||
@ -5898,7 +5900,8 @@ bool InitializationSequence::Diagnose(Sema &S,
|
||||
// initialized.
|
||||
CXXConstructorDecl *Constructor = cast<CXXConstructorDecl>(S.CurContext);
|
||||
S.Diag(Kind.getLocation(), diag::err_uninitialized_member_in_ctor)
|
||||
<< Constructor->isImplicit()
|
||||
<< (Constructor->getInheritedConstructor() ? 2 :
|
||||
Constructor->isImplicit() ? 1 : 0)
|
||||
<< S.Context.getTypeDeclType(Constructor->getParent())
|
||||
<< /*const=*/1
|
||||
<< Entity.getName();
|
||||
|
@ -9,49 +9,49 @@ struct B1 {
|
||||
B1(int);
|
||||
};
|
||||
|
||||
using B1::B1; // expected-error {{using declaration can not refer to class member}} expected-error {{not supported}}
|
||||
using B1::B1; // expected-error {{using declaration can not refer to class member}}
|
||||
|
||||
// C++0x [namespace.udecl]p10:
|
||||
// C++11 [namespace.udecl]p10:
|
||||
// A using-declaration is a declaration and can therefore be used repeatedly
|
||||
// where (and only where) multiple declarations are allowed.
|
||||
|
||||
struct I1 : B1 {
|
||||
using B1::B1; // expected-note {{previous using declaration}} expected-error {{not supported}}
|
||||
using B1::B1; // expected-error {{redeclaration of using decl}} expected-error {{not supported}}
|
||||
using B1::B1; // expected-note {{previous using declaration}}
|
||||
using B1::B1; // expected-error {{redeclaration of using decl}}
|
||||
};
|
||||
|
||||
// C++0x [namespace.udecl]p3:
|
||||
// C++11 [namespace.udecl]p3:
|
||||
// In a using declaration used as a member-declaration, the nested-name-
|
||||
// specifier shall name a base class of the class being defined.
|
||||
// If such a using-declaration names a constructor, the nested-name-specifier
|
||||
// shall name a direct base class of the class being defined.
|
||||
|
||||
struct D1 : I1 {
|
||||
using B1::B1; // expected-error {{'B1' is not a direct base of 'D1', can not inherit constructors}} expected-error {{not supported}}
|
||||
using B1::B1; // expected-error {{'B1' is not a direct base of 'D1', can not inherit constructors}}
|
||||
};
|
||||
|
||||
template<typename T> struct A {};
|
||||
|
||||
template<typename T> struct B : A<bool>, A<char> {
|
||||
using A<T>::A; // expected-error {{'A<double>::', which is not a base class of 'B<double>'}} expected-error {{not supported}}
|
||||
using A<T>::A; // expected-error {{'A<double>::', which is not a base class of 'B<double>'}}
|
||||
};
|
||||
B<bool> bb;
|
||||
B<char> bc;
|
||||
B<double> bd; // expected-note {{here}}
|
||||
|
||||
template<typename T> struct C : A<T> {
|
||||
using A<bool>::A; // expected-error {{'A<bool>::', which is not a base class of 'C<char>'}} expected-error {{not supported}}
|
||||
using A<bool>::A; // expected-error {{'A<bool>::', which is not a base class of 'C<char>'}}
|
||||
};
|
||||
C<bool> cb;
|
||||
C<char> cc; // expected-note {{here}}
|
||||
|
||||
template<typename T> struct D : A<T> {};
|
||||
template<typename T> struct E : D<T> {
|
||||
using A<bool>::A; // expected-error {{'A<bool>' is not a direct base of 'E<bool>', can not inherit}} expected-error {{not supported}}
|
||||
using A<bool>::A; // expected-error {{'A<bool>' is not a direct base of 'E<bool>', can not inherit}}
|
||||
};
|
||||
E<bool> eb; // expected-note {{here}}
|
||||
|
||||
template<typename T> struct F : D<bool> {
|
||||
using A<T>::A; // expected-error {{'A<bool>' is not a direct base of 'F<bool>'}} expected-error {{not supported}}
|
||||
using A<T>::A; // expected-error {{'A<bool>' is not a direct base of 'F<bool>'}}
|
||||
};
|
||||
F<bool> fb; // expected-note {{here}}
|
||||
|
31
clang/test/CXX/special/class.inhctor/p1.cpp
Normal file
31
clang/test/CXX/special/class.inhctor/p1.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
// RUN: %clang_cc1 -std=c++11 -verify %s
|
||||
// Per a core issue (no number yet), an ellipsis is always dropped.
|
||||
struct A {
|
||||
A(...); // expected-note {{here}}
|
||||
A(int = 0, int = 0, int = 0, int = 0, ...); // expected-note 5{{here}}
|
||||
A(int = 0, int = 0, ...); // expected-note {{here}}
|
||||
};
|
||||
|
||||
struct B : A { // expected-note 3{{candidate}}
|
||||
using A::A; // expected-warning 3{{inheriting constructor does not inherit ellipsis}} expected-note 4{{candidate}} expected-note 2{{deleted}}
|
||||
};
|
||||
|
||||
B b0{};
|
||||
// expected-error@-1 {{call to implicitly-deleted default constructor}}
|
||||
// expected-note@9 {{default constructor of 'B' is implicitly deleted because base class 'A' has multiple default constructors}}
|
||||
|
||||
B b1{1};
|
||||
// FIXME: explain why the inheriting constructor was deleted
|
||||
// expected-error@-2 {{call to implicitly-deleted function of 'B'}}
|
||||
|
||||
B b2{1,2};
|
||||
// expected-error@-1 {{call to implicitly-deleted function of 'B'}}
|
||||
|
||||
B b3{1,2,3};
|
||||
// ok
|
||||
|
||||
B b4{1,2,3,4};
|
||||
// ok
|
||||
|
||||
B b5{1,2,3,4,5};
|
||||
// expected-error@-1 {{no matching constructor for initialization of 'B'}}
|
87
clang/test/CXX/special/class.inhctor/p2.cpp
Normal file
87
clang/test/CXX/special/class.inhctor/p2.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
// RUN: %clang_cc1 -std=c++11 -verify %s
|
||||
|
||||
template<int> struct X {};
|
||||
|
||||
// Constructor characteristics are:
|
||||
// - the template parameter list [FIXME]
|
||||
// - the parameter-type-list
|
||||
// - absence or presence of explicit
|
||||
// - absence or presence of constexpr
|
||||
struct A {
|
||||
A(X<0>) {} // expected-note 2{{here}}
|
||||
constexpr A(X<1>) {}
|
||||
explicit A(X<2>) {} // expected-note 3{{here}}
|
||||
explicit constexpr A(X<3>) {} // expected-note 2{{here}}
|
||||
};
|
||||
|
||||
A a0 { X<0>{} };
|
||||
A a0i = { X<0>{} };
|
||||
constexpr A a0c { X<0>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}}
|
||||
constexpr A a0ic = { X<0>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}}
|
||||
|
||||
A a1 { X<1>{} };
|
||||
A a1i = { X<1>{} };
|
||||
constexpr A a1c { X<1>{} };
|
||||
constexpr A a1ic = { X<1>{} };
|
||||
|
||||
A a2 { X<2>{} };
|
||||
A a2i = { X<2>{} }; // expected-error {{constructor is explicit}}
|
||||
constexpr A a2c { X<2>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}}
|
||||
constexpr A a2ic = { X<2>{} }; // expected-error {{constructor is explicit}}
|
||||
|
||||
A a3 { X<3>{} };
|
||||
A a3i = { X<3>{} }; // expected-error {{constructor is explicit}}
|
||||
constexpr A a3c { X<3>{} };
|
||||
constexpr A a3ic = { X<3>{} }; // expected-error {{constructor is explicit}}
|
||||
|
||||
|
||||
struct B : A {
|
||||
using A::A; // expected-note 7{{here}}
|
||||
};
|
||||
|
||||
B b0 { X<0>{} };
|
||||
B b0i = { X<0>{} };
|
||||
constexpr B b0c { X<0>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}}
|
||||
constexpr B b0ic = { X<0>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}}
|
||||
|
||||
B b1 { X<1>{} };
|
||||
B b1i = { X<1>{} };
|
||||
constexpr B b1c { X<1>{} };
|
||||
constexpr B b1ic = { X<1>{} };
|
||||
|
||||
B b2 { X<2>{} };
|
||||
B b2i = { X<2>{} }; // expected-error {{constructor is explicit}}
|
||||
constexpr B b2c { X<2>{} }; // expected-error {{must be initialized by a constant expression}} expected-note {{non-constexpr}}
|
||||
constexpr B b2ic = { X<2>{} }; // expected-error {{constructor is explicit}}
|
||||
|
||||
B b3 { X<3>{} };
|
||||
B b3i = { X<3>{} }; // expected-error {{constructor is explicit}}
|
||||
constexpr B b3c { X<3>{} };
|
||||
constexpr B b3ic = { X<3>{} }; // expected-error {{constructor is explicit}}
|
||||
|
||||
|
||||
// 'constexpr' is OK even if the constructor doesn't obey the constraints.
|
||||
struct NonLiteral { NonLiteral(); };
|
||||
struct NonConstexpr { NonConstexpr(); constexpr NonConstexpr(int); }; // expected-note {{here}}
|
||||
struct Constexpr { constexpr Constexpr(int) {} };
|
||||
|
||||
struct BothNonLiteral : NonLiteral, Constexpr { using Constexpr::Constexpr; }; // expected-note {{base class 'NonLiteral' of non-literal type}}
|
||||
constexpr BothNonLiteral bothNL{42}; // expected-error {{constexpr variable cannot have non-literal type 'const BothNonLiteral'}}
|
||||
|
||||
struct BothNonConstexpr : NonConstexpr, Constexpr { using Constexpr::Constexpr; }; // expected-note {{non-constexpr constructor 'NonConstexpr}}
|
||||
constexpr BothNonConstexpr bothNC{42}; // expected-error {{must be initialized by a constant expression}} expected-note {{in call to 'BothNonConstexpr(42)'}}
|
||||
|
||||
|
||||
struct ConstexprEval {
|
||||
constexpr ConstexprEval(int a, const char *p) : k(p[a]) {}
|
||||
char k;
|
||||
};
|
||||
struct ConstexprEval2 {
|
||||
char k2 = 'x';
|
||||
};
|
||||
struct ConstexprEval3 : ConstexprEval, ConstexprEval2 {
|
||||
using ConstexprEval::ConstexprEval;
|
||||
};
|
||||
constexpr ConstexprEval3 ce{4, "foobar"};
|
||||
static_assert(ce.k == 'a', "");
|
||||
static_assert(ce.k2 == 'x', "");
|
@ -5,7 +5,7 @@ struct B1 {
|
||||
B1(int, int);
|
||||
};
|
||||
struct D1 : B1 {
|
||||
using B1::B1; // expected-error {{not supported}}
|
||||
using B1::B1;
|
||||
};
|
||||
D1 d1a(1), d1b(1, 1);
|
||||
|
||||
@ -15,7 +15,7 @@ struct B2 {
|
||||
explicit B2(int, int = 0, int = 0);
|
||||
};
|
||||
struct D2 : B2 { // expected-note 2 {{candidate constructor}}
|
||||
using B2::B2; // expected-error {{not supported}}
|
||||
using B2::B2;
|
||||
};
|
||||
D2 d2a(1), d2b(1, 1), d2c(1, 1, 1);
|
||||
|
||||
@ -25,18 +25,18 @@ struct B3 {
|
||||
B3(void*); // expected-note {{inherited from here}}
|
||||
};
|
||||
struct D3 : B3 { // expected-note 2 {{candidate constructor}}
|
||||
using B3::B3; // expected-note {{candidate constructor (inherited)}} expected-error {{not supported}}
|
||||
using B3::B3; // expected-note {{candidate constructor (inherited)}}
|
||||
};
|
||||
D3 fd3() { return 1; } // expected-error {{no viable conversion}}
|
||||
|
||||
template<typename T> struct T1 : B1 {
|
||||
using B1::B1; // expected-error {{not supported}}
|
||||
using B1::B1;
|
||||
};
|
||||
template<typename T> struct T2 : T1<T> {
|
||||
using T1<int>::T1; // expected-error {{not supported}}
|
||||
using T1<int>::T1;
|
||||
};
|
||||
template<typename T> struct T3 : T1<int> {
|
||||
using T1<T>::T1; // expected-error {{not supported}}
|
||||
using T1<T>::T1;
|
||||
};
|
||||
struct U {
|
||||
friend T1<int>::T1(int);
|
||||
|
70
clang/test/CXX/special/class.inhctor/p4.cpp
Normal file
70
clang/test/CXX/special/class.inhctor/p4.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
// RUN: %clang_cc1 -std=c++11 -verify %s
|
||||
|
||||
template<int> struct X {};
|
||||
|
||||
// A[n inheriting] constructor [...] has the same access as the corresponding
|
||||
// constructor [in the base class].
|
||||
struct A {
|
||||
public:
|
||||
A(X<0>) {}
|
||||
protected:
|
||||
A(X<1>) {}
|
||||
private:
|
||||
A(X<2>) {} // expected-note {{declared private here}}
|
||||
friend class FA;
|
||||
};
|
||||
|
||||
struct B : A {
|
||||
using A::A; // expected-error {{private constructor}} expected-note {{implicitly declared protected here}}
|
||||
friend class FB;
|
||||
};
|
||||
|
||||
B b0{X<0>{}};
|
||||
B b1{X<1>{}}; // expected-error {{calling a protected constructor}}
|
||||
B b2{X<2>{}}; // expected-note {{first required here}}
|
||||
|
||||
struct C : B {
|
||||
C(X<0> x) : B(x) {}
|
||||
C(X<1> x) : B(x) {}
|
||||
};
|
||||
|
||||
struct FB {
|
||||
B b0{X<0>{}};
|
||||
B b1{X<1>{}};
|
||||
};
|
||||
|
||||
struct FA : A {
|
||||
using A::A; // expected-note 2{{here}}
|
||||
};
|
||||
FA fa0{X<0>{}};
|
||||
FA fa1{X<1>{}}; // expected-error {{calling a protected constructor}}
|
||||
FA fa2{X<2>{}}; // expected-error {{calling a private constructor}}
|
||||
|
||||
|
||||
// It is deleted if the corresponding constructor [...] is deleted.
|
||||
struct G {
|
||||
G(int) = delete;
|
||||
};
|
||||
struct H : G {
|
||||
using G::G; // expected-note {{marked deleted here}}
|
||||
};
|
||||
H h(5); // expected-error {{call to implicitly-deleted function of 'H'}}
|
||||
|
||||
|
||||
// Core defect: It is also deleted if multiple base constructors generate the
|
||||
// same signature.
|
||||
namespace DRnnnn {
|
||||
struct A {
|
||||
constexpr A(int, float = 0) {}
|
||||
explicit A(int, int = 0) {}
|
||||
|
||||
A(int, int, int = 0) = delete;
|
||||
};
|
||||
struct B : A {
|
||||
// FIXME: produce notes indicating why it was deleted
|
||||
using A::A; // expected-note {{here}}
|
||||
};
|
||||
|
||||
constexpr B b0(0, 0.0f); // ok, constexpr
|
||||
B b1(0, 1); // expected-error {{call to implicitly-deleted}}
|
||||
}
|
@ -8,12 +8,12 @@ struct B2 {
|
||||
B2(int); // expected-note {{conflicting constructor}}
|
||||
};
|
||||
struct D1 : B1, B2 {
|
||||
using B1::B1; // expected-note {{inherited here}} expected-error {{not supported}}
|
||||
using B2::B2; // expected-error {{already inherited constructor with the same signature}} expected-error {{not supported}}
|
||||
using B1::B1; // expected-note {{inherited here}}
|
||||
using B2::B2; // expected-error {{already inherited constructor with the same signature}}
|
||||
};
|
||||
struct D2 : B1, B2 {
|
||||
using B1::B1; // expected-error {{not supported}}
|
||||
using B2::B2; // expected-error {{not supported}}
|
||||
using B1::B1;
|
||||
using B2::B2;
|
||||
D2(int);
|
||||
};
|
||||
|
||||
@ -22,8 +22,8 @@ template<typename T> struct B3 {
|
||||
};
|
||||
template<typename T> struct B4 : B3<T>, B1 {
|
||||
B4();
|
||||
using B3<T>::B3; // expected-note {{inherited here}} expected-error {{not supported}}
|
||||
using B1::B1; // expected-error {{already inherited}} expected-error {{not supported}}
|
||||
using B3<T>::B3; // expected-note {{inherited here}}
|
||||
using B1::B1; // expected-error {{already inherited}}
|
||||
};
|
||||
B4<char> b4c;
|
||||
B4<int> b4i; // expected-note {{here}}
|
||||
|
21
clang/test/CXX/special/class.inhctor/p8.cpp
Normal file
21
clang/test/CXX/special/class.inhctor/p8.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
// RUN: %clang_cc1 -std=c++11 -verify %s
|
||||
|
||||
// expected-no-diagnostics
|
||||
struct A {
|
||||
constexpr A(const int&) : rval(false) {}
|
||||
constexpr A(const int&&) : rval(true) {}
|
||||
bool rval;
|
||||
};
|
||||
struct B : A {
|
||||
using A::A;
|
||||
};
|
||||
|
||||
constexpr int k = 0;
|
||||
constexpr A a0{0};
|
||||
constexpr A a1{k};
|
||||
constexpr B b0{0};
|
||||
// This performs static_cast<(const int&)&&>(k), so calls the A(const int&)
|
||||
// constructor.
|
||||
constexpr B b1{k};
|
||||
|
||||
static_assert(a0.rval && !a1.rval && b0.rval && !b1.rval, "");
|
@ -1,11 +1,18 @@
|
||||
// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s
|
||||
|
||||
// XFAIL: *
|
||||
|
||||
// PR12219
|
||||
struct A { A(int); virtual ~A(); };
|
||||
struct B : A { using A::A; ~B(); };
|
||||
B::~B() {}
|
||||
|
||||
B b(123);
|
||||
|
||||
// CHECK: define void @_ZN1BD0Ev
|
||||
// CHECK: define void @_ZN1BD1Ev
|
||||
// CHECK: define void @_ZN1BD2Ev
|
||||
|
||||
// CHECK: define linkonce_odr void @_ZN1BC1Ei(
|
||||
// CHECK: call void @_ZN1BC2Ei(
|
||||
|
||||
// CHECK: define linkonce_odr void @_ZN1BC2Ei(
|
||||
// CHECK: call void @_ZN1AC2Ei(
|
||||
|
Loading…
x
Reference in New Issue
Block a user