N3922: direct-list-initialization of an auto-typed variable no longer deduces a

std::initializer_list<T> type. Instead, the list must contain a single element
and the type is deduced from that.

In Clang 3.7, we warned by default on all the cases that would change meaning
due to this change. In Clang 3.8, we will support only the new rules -- per
the request in N3922, this change is applied as a Defect Report against earlier
versions of the C++ standard.

This change is not entirely trivial, because for lambda init-captures we
previously did not track the difference between direct-list-initialization and
copy-list-initialization. The difference was not previously observable, because
the two forms of initialization always did the same thing (the elements of the
initializer list were always copy-initialized regardless of the initialization
style used for the init-capture).


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@252688 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Richard Smith 2015-11-11 01:36:17 +00:00
parent b59606171d
commit 1fd6dc6f12
16 changed files with 247 additions and 203 deletions

View File

@ -3817,6 +3817,10 @@ public:
/// \brief Retrieve the set of initializers. /// \brief Retrieve the set of initializers.
Expr **getInits() { return reinterpret_cast<Expr **>(InitExprs.data()); } Expr **getInits() { return reinterpret_cast<Expr **>(InitExprs.data()); }
ArrayRef<Expr *> inits() {
return llvm::makeArrayRef(getInits(), getNumInits());
}
const Expr *getInit(unsigned Init) const { const Expr *getInit(unsigned Init) const {
assert(Init < getNumInits() && "Initializer access out of range!"); assert(Init < getNumInits() && "Initializer access out of range!");
return cast_or_null<Expr>(InitExprs[Init]); return cast_or_null<Expr>(InitExprs[Init]);
@ -4427,6 +4431,10 @@ public:
Expr **getExprs() { return reinterpret_cast<Expr **>(Exprs); } Expr **getExprs() { return reinterpret_cast<Expr **>(Exprs); }
ArrayRef<Expr *> exprs() {
return llvm::makeArrayRef(getExprs(), getNumExprs());
}
SourceLocation getLParenLoc() const { return LParenLoc; } SourceLocation getLParenLoc() const { return LParenLoc; }
SourceLocation getRParenLoc() const { return RParenLoc; } SourceLocation getRParenLoc() const { return RParenLoc; }

View File

@ -821,10 +821,6 @@ def warn_cxx98_compat_lambda : Warning<
def err_lambda_missing_parens : Error< def err_lambda_missing_parens : Error<
"lambda requires '()' before %select{'mutable'|return type|" "lambda requires '()' before %select{'mutable'|return type|"
"attribute specifier}0">; "attribute specifier}0">;
def warn_init_capture_direct_list_init : Warning<
"direct list initialization of a lambda init-capture will change meaning in "
"a future version of Clang; insert an '=' to avoid a change in behavior">,
InGroup<FutureCompat>;
// Availability attribute // Availability attribute
def err_expected_version : Error< def err_expected_version : Error<

View File

@ -1689,12 +1689,8 @@ def err_auto_var_init_no_expression : Error<
def err_auto_var_init_multiple_expressions : Error< def err_auto_var_init_multiple_expressions : Error<
"initializer for variable %0 with type %1 contains multiple expressions">; "initializer for variable %0 with type %1 contains multiple expressions">;
def err_auto_var_init_paren_braces : Error< def err_auto_var_init_paren_braces : Error<
"cannot deduce type for variable %0 with type %1 from " "cannot deduce type for variable %1 with type %2 from "
"parenthesized initializer list">; "%select{parenthesized|nested}0 initializer list">;
def warn_auto_var_direct_list_init : Warning<
"direct list initialization of a variable with a deduced type will change "
"meaning in a future version of Clang; insert an '=' to avoid a change in "
"behavior">, InGroup<FutureCompat>;
def err_auto_new_ctor_multiple_expressions : Error< def err_auto_new_ctor_multiple_expressions : Error<
"new expression for type %0 contains multiple constructor arguments">; "new expression for type %0 contains multiple constructor arguments">;
def err_auto_missing_trailing_return : Error< def err_auto_missing_trailing_return : Error<
@ -5819,8 +5815,8 @@ let CategoryName = "Lambda Issue" in {
def err_init_capture_multiple_expressions : Error< def err_init_capture_multiple_expressions : Error<
"initializer for lambda capture %0 contains multiple expressions">; "initializer for lambda capture %0 contains multiple expressions">;
def err_init_capture_paren_braces : Error< def err_init_capture_paren_braces : Error<
"cannot deduce type for lambda capture %0 from " "cannot deduce type for lambda capture %1 from "
"parenthesized initializer list">; "%select{parenthesized|nested}0 initializer list">;
def err_init_capture_deduction_failure : Error< def err_init_capture_deduction_failure : Error<
"cannot deduce type for lambda capture %0 from initializer of type %2">; "cannot deduce type for lambda capture %0 from initializer of type %2">;
def err_init_capture_deduction_failure_from_init_list : Error< def err_init_capture_deduction_failure_from_init_list : Error<

View File

@ -2265,6 +2265,13 @@ private:
SourceLocation LastLocation; SourceLocation LastLocation;
}; };
enum class LambdaCaptureInitKind {
NoInit, //!< [a]
CopyInit, //!< [a = b], [a = {b}]
DirectInit, //!< [a(b)]
ListInit //!< [a{b}]
};
/// \brief Represents a complete lambda introducer. /// \brief Represents a complete lambda introducer.
struct LambdaIntroducer { struct LambdaIntroducer {
/// \brief An individual capture in a lambda introducer. /// \brief An individual capture in a lambda introducer.
@ -2273,13 +2280,15 @@ struct LambdaIntroducer {
SourceLocation Loc; SourceLocation Loc;
IdentifierInfo *Id; IdentifierInfo *Id;
SourceLocation EllipsisLoc; SourceLocation EllipsisLoc;
LambdaCaptureInitKind InitKind;
ExprResult Init; ExprResult Init;
ParsedType InitCaptureType; ParsedType InitCaptureType;
LambdaCapture(LambdaCaptureKind Kind, SourceLocation Loc, LambdaCapture(LambdaCaptureKind Kind, SourceLocation Loc,
IdentifierInfo *Id, SourceLocation EllipsisLoc, IdentifierInfo *Id, SourceLocation EllipsisLoc,
ExprResult Init, ParsedType InitCaptureType) LambdaCaptureInitKind InitKind, ExprResult Init,
: Kind(Kind), Loc(Loc), Id(Id), EllipsisLoc(EllipsisLoc), Init(Init), ParsedType InitCaptureType)
InitCaptureType(InitCaptureType) {} : Kind(Kind), Loc(Loc), Id(Id), EllipsisLoc(EllipsisLoc),
InitKind(InitKind), Init(Init), InitCaptureType(InitCaptureType) {}
}; };
SourceRange Range; SourceRange Range;
@ -2295,10 +2304,11 @@ struct LambdaIntroducer {
SourceLocation Loc, SourceLocation Loc,
IdentifierInfo* Id, IdentifierInfo* Id,
SourceLocation EllipsisLoc, SourceLocation EllipsisLoc,
LambdaCaptureInitKind InitKind,
ExprResult Init, ExprResult Init,
ParsedType InitCaptureType) { ParsedType InitCaptureType) {
Captures.push_back(LambdaCapture(Kind, Loc, Id, EllipsisLoc, Init, Captures.push_back(LambdaCapture(Kind, Loc, Id, EllipsisLoc, InitKind, Init,
InitCaptureType)); InitCaptureType));
} }
}; };

View File

@ -4972,15 +4972,25 @@ public:
/// \brief Perform initialization analysis of the init-capture and perform /// \brief Perform initialization analysis of the init-capture and perform
/// any implicit conversions such as an lvalue-to-rvalue conversion if /// any implicit conversions such as an lvalue-to-rvalue conversion if
/// not being used to initialize a reference. /// not being used to initialize a reference.
QualType performLambdaInitCaptureInitialization(SourceLocation Loc, ParsedType actOnLambdaInitCaptureInitialization(
bool ByRef, IdentifierInfo *Id, Expr *&Init); SourceLocation Loc, bool ByRef, IdentifierInfo *Id,
LambdaCaptureInitKind InitKind, Expr *&Init) {
return ParsedType::make(buildLambdaInitCaptureInitialization(
Loc, ByRef, Id, InitKind != LambdaCaptureInitKind::CopyInit, Init));
}
QualType buildLambdaInitCaptureInitialization(SourceLocation Loc, bool ByRef,
IdentifierInfo *Id,
bool DirectInit, Expr *&Init);
/// \brief Create a dummy variable within the declcontext of the lambda's /// \brief Create a dummy variable within the declcontext of the lambda's
/// call operator, for name lookup purposes for a lambda init capture. /// call operator, for name lookup purposes for a lambda init capture.
/// ///
/// CodeGen handles emission of lambda captures, ignoring these dummy /// CodeGen handles emission of lambda captures, ignoring these dummy
/// variables appropriately. /// variables appropriately.
VarDecl *createLambdaInitCaptureVarDecl(SourceLocation Loc, VarDecl *createLambdaInitCaptureVarDecl(SourceLocation Loc,
QualType InitCaptureType, IdentifierInfo *Id, Expr *Init); QualType InitCaptureType,
IdentifierInfo *Id,
unsigned InitStyle, Expr *Init);
/// \brief Build the implicit field for an init-capture. /// \brief Build the implicit field for an init-capture.
FieldDecl *buildInitCaptureField(sema::LambdaScopeInfo *LSI, VarDecl *Var); FieldDecl *buildInitCaptureField(sema::LambdaScopeInfo *LSI, VarDecl *Var);
@ -6372,6 +6382,11 @@ public:
bool DeduceReturnType(FunctionDecl *FD, SourceLocation Loc, bool DeduceReturnType(FunctionDecl *FD, SourceLocation Loc,
bool Diagnose = true); bool Diagnose = true);
QualType deduceVarTypeFromInitializer(VarDecl *VDecl, DeclarationName Name,
QualType Type, TypeSourceInfo *TSI,
SourceRange Range, bool DirectInit,
Expr *Init);
TypeLoc getReturnTypeLoc(FunctionDecl *FD) const; TypeLoc getReturnTypeLoc(FunctionDecl *FD) const;
bool DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, bool DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD,

View File

@ -841,6 +841,7 @@ Optional<unsigned> Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro,
// Parse capture. // Parse capture.
LambdaCaptureKind Kind = LCK_ByCopy; LambdaCaptureKind Kind = LCK_ByCopy;
LambdaCaptureInitKind InitKind = LambdaCaptureInitKind::NoInit;
SourceLocation Loc; SourceLocation Loc;
IdentifierInfo *Id = nullptr; IdentifierInfo *Id = nullptr;
SourceLocation EllipsisLoc; SourceLocation EllipsisLoc;
@ -878,6 +879,8 @@ Optional<unsigned> Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro,
BalancedDelimiterTracker Parens(*this, tok::l_paren); BalancedDelimiterTracker Parens(*this, tok::l_paren);
Parens.consumeOpen(); Parens.consumeOpen();
InitKind = LambdaCaptureInitKind::DirectInit;
ExprVector Exprs; ExprVector Exprs;
CommaLocsTy Commas; CommaLocsTy Commas;
if (SkippedInits) { if (SkippedInits) {
@ -898,14 +901,13 @@ Optional<unsigned> Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro,
// to save the necessary state, and restore it later. // to save the necessary state, and restore it later.
EnterExpressionEvaluationContext EC(Actions, EnterExpressionEvaluationContext EC(Actions,
Sema::PotentiallyEvaluated); Sema::PotentiallyEvaluated);
bool HadEquals = TryConsumeToken(tok::equal);
if (TryConsumeToken(tok::equal))
InitKind = LambdaCaptureInitKind::CopyInit;
else
InitKind = LambdaCaptureInitKind::ListInit;
if (!SkippedInits) { if (!SkippedInits) {
// Warn on constructs that will change meaning when we implement N3922
if (!HadEquals && Tok.is(tok::l_brace)) {
Diag(Tok, diag::warn_init_capture_direct_list_init)
<< FixItHint::CreateInsertion(Tok.getLocation(), "=");
}
Init = ParseInitializer(); Init = ParseInitializer();
} else if (Tok.is(tok::l_brace)) { } else if (Tok.is(tok::l_brace)) {
BalancedDelimiterTracker Braces(*this, tok::l_brace); BalancedDelimiterTracker Braces(*this, tok::l_brace);
@ -993,19 +995,19 @@ Optional<unsigned> Parser::ParseLambdaIntroducer(LambdaIntroducer &Intro,
// If x was not const, the second use would require 'L' to capture, and // If x was not const, the second use would require 'L' to capture, and
// that would be an error. // that would be an error.
ParsedType InitCaptureParsedType; ParsedType InitCaptureType;
if (Init.isUsable()) { if (Init.isUsable()) {
// Get the pointer and store it in an lvalue, so we can use it as an // Get the pointer and store it in an lvalue, so we can use it as an
// out argument. // out argument.
Expr *InitExpr = Init.get(); Expr *InitExpr = Init.get();
// This performs any lvalue-to-rvalue conversions if necessary, which // This performs any lvalue-to-rvalue conversions if necessary, which
// can affect what gets captured in the containing decl-context. // can affect what gets captured in the containing decl-context.
QualType InitCaptureType = Actions.performLambdaInitCaptureInitialization( InitCaptureType = Actions.actOnLambdaInitCaptureInitialization(
Loc, Kind == LCK_ByRef, Id, InitExpr); Loc, Kind == LCK_ByRef, Id, InitKind, InitExpr);
Init = InitExpr; Init = InitExpr;
InitCaptureParsedType.set(InitCaptureType);
} }
Intro.addCapture(Kind, Loc, Id, EllipsisLoc, Init, InitCaptureParsedType); Intro.addCapture(Kind, Loc, Id, EllipsisLoc, InitKind, Init,
InitCaptureType);
} }
T.consumeClose(); T.consumeClose();

View File

@ -9012,6 +9012,96 @@ namespace {
} }
} }
QualType Sema::deduceVarTypeFromInitializer(VarDecl *VDecl,
DeclarationName Name, QualType Type,
TypeSourceInfo *TSI,
SourceRange Range, bool DirectInit,
Expr *Init) {
bool IsInitCapture = !VDecl;
assert((!VDecl || !VDecl->isInitCapture()) &&
"init captures are expected to be deduced prior to initialization");
ArrayRef<Expr *> DeduceInits = Init;
if (DirectInit) {
if (auto *PL = dyn_cast<ParenListExpr>(Init))
DeduceInits = PL->exprs();
else if (auto *IL = dyn_cast<InitListExpr>(Init))
DeduceInits = IL->inits();
}
// Deduction only works if we have exactly one source expression.
if (DeduceInits.empty()) {
// It isn't possible to write this directly, but it is possible to
// end up in this situation with "auto x(some_pack...);"
Diag(Init->getLocStart(), IsInitCapture
? diag::err_init_capture_no_expression
: diag::err_auto_var_init_no_expression)
<< Name << Type << Range;
return QualType();
}
if (DeduceInits.size() > 1) {
Diag(DeduceInits[1]->getLocStart(),
IsInitCapture ? diag::err_init_capture_multiple_expressions
: diag::err_auto_var_init_multiple_expressions)
<< Name << Type << Range;
return QualType();
}
Expr *DeduceInit = DeduceInits[0];
if (DirectInit && isa<InitListExpr>(DeduceInit)) {
Diag(Init->getLocStart(), IsInitCapture
? diag::err_init_capture_paren_braces
: diag::err_auto_var_init_paren_braces)
<< isa<InitListExpr>(Init) << Name << Type << Range;
return QualType();
}
// Expressions default to 'id' when we're in a debugger.
bool DefaultedAnyToId = false;
if (getLangOpts().DebuggerCastResultToId &&
Init->getType() == Context.UnknownAnyTy && !IsInitCapture) {
ExprResult Result = forceUnknownAnyToType(Init, Context.getObjCIdType());
if (Result.isInvalid()) {
return QualType();
}
Init = Result.get();
DefaultedAnyToId = true;
}
QualType DeducedType;
if (DeduceAutoType(TSI, DeduceInit, DeducedType) == DAR_Failed) {
if (!IsInitCapture)
DiagnoseAutoDeductionFailure(VDecl, DeduceInit);
else if (isa<InitListExpr>(Init))
Diag(Range.getBegin(),
diag::err_init_capture_deduction_failure_from_init_list)
<< Name
<< (DeduceInit->getType().isNull() ? TSI->getType()
: DeduceInit->getType())
<< DeduceInit->getSourceRange();
else
Diag(Range.getBegin(), diag::err_init_capture_deduction_failure)
<< Name << TSI->getType()
<< (DeduceInit->getType().isNull() ? TSI->getType()
: DeduceInit->getType())
<< DeduceInit->getSourceRange();
}
// Warn if we deduced 'id'. 'auto' usually implies type-safety, but using
// 'id' instead of a specific object type prevents most of our usual
// checks.
// We only want to warn outside of template instantiations, though:
// inside a template, the 'id' could have come from a parameter.
if (ActiveTemplateInstantiations.empty() && !DefaultedAnyToId &&
!IsInitCapture && !DeducedType.isNull() && DeducedType->isObjCIdType()) {
SourceLocation Loc = TSI->getTypeLoc().getBeginLoc();
Diag(Loc, diag::warn_auto_var_is_id) << Name << Range;
}
return DeducedType;
}
/// AddInitializerToDecl - Adds the initializer Init to the /// AddInitializerToDecl - Adds the initializer Init to the
/// declaration dcl. If DirectInit is true, this is C++ direct /// declaration dcl. If DirectInit is true, this is C++ direct
/// initialization rather than copy initialization. /// initialization rather than copy initialization.
@ -9039,79 +9129,27 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
RealDecl->setInvalidDecl(); RealDecl->setInvalidDecl();
return; return;
} }
ParenListExpr *CXXDirectInit = dyn_cast<ParenListExpr>(Init);
// C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for. // C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for.
if (TypeMayContainAuto && VDecl->getType()->isUndeducedType()) { if (TypeMayContainAuto && VDecl->getType()->isUndeducedType()) {
// Attempt typo correction early so that the type of the init expression can // Attempt typo correction early so that the type of the init expression can
// be deduced based on the chosen correction:if the original init contains a // be deduced based on the chosen correction if the original init contains a
// TypoExpr. // TypoExpr.
ExprResult Res = CorrectDelayedTyposInExpr(Init, VDecl); ExprResult Res = CorrectDelayedTyposInExpr(Init, VDecl);
if (!Res.isUsable()) { if (!Res.isUsable()) {
RealDecl->setInvalidDecl(); RealDecl->setInvalidDecl();
return; return;
} }
Init = Res.get();
if (Res.get() != Init) { QualType DeducedType = deduceVarTypeFromInitializer(
Init = Res.get(); VDecl, VDecl->getDeclName(), VDecl->getType(),
if (CXXDirectInit) VDecl->getTypeSourceInfo(), VDecl->getSourceRange(), DirectInit, Init);
CXXDirectInit = dyn_cast<ParenListExpr>(Init);
}
Expr *DeduceInit = Init;
// Initializer could be a C++ direct-initializer. Deduction only works if it
// contains exactly one expression.
if (CXXDirectInit) {
if (CXXDirectInit->getNumExprs() == 0) {
// It isn't possible to write this directly, but it is possible to
// end up in this situation with "auto x(some_pack...);"
Diag(CXXDirectInit->getLocStart(),
VDecl->isInitCapture() ? diag::err_init_capture_no_expression
: diag::err_auto_var_init_no_expression)
<< VDecl->getDeclName() << VDecl->getType()
<< VDecl->getSourceRange();
RealDecl->setInvalidDecl();
return;
} else if (CXXDirectInit->getNumExprs() > 1) {
Diag(CXXDirectInit->getExpr(1)->getLocStart(),
VDecl->isInitCapture()
? diag::err_init_capture_multiple_expressions
: diag::err_auto_var_init_multiple_expressions)
<< VDecl->getDeclName() << VDecl->getType()
<< VDecl->getSourceRange();
RealDecl->setInvalidDecl();
return;
} else {
DeduceInit = CXXDirectInit->getExpr(0);
if (isa<InitListExpr>(DeduceInit))
Diag(CXXDirectInit->getLocStart(),
diag::err_auto_var_init_paren_braces)
<< VDecl->getDeclName() << VDecl->getType()
<< VDecl->getSourceRange();
}
}
// Expressions default to 'id' when we're in a debugger.
bool DefaultedToAuto = false;
if (getLangOpts().DebuggerCastResultToId &&
Init->getType() == Context.UnknownAnyTy) {
ExprResult Result = forceUnknownAnyToType(Init, Context.getObjCIdType());
if (Result.isInvalid()) {
VDecl->setInvalidDecl();
return;
}
Init = Result.get();
DefaultedToAuto = true;
}
QualType DeducedType;
if (DeduceAutoType(VDecl->getTypeSourceInfo(), DeduceInit, DeducedType) ==
DAR_Failed)
DiagnoseAutoDeductionFailure(VDecl, DeduceInit);
if (DeducedType.isNull()) { if (DeducedType.isNull()) {
RealDecl->setInvalidDecl(); RealDecl->setInvalidDecl();
return; return;
} }
VDecl->setType(DeducedType); VDecl->setType(DeducedType);
assert(VDecl->isLinkageValid()); assert(VDecl->isLinkageValid());
@ -9119,38 +9157,18 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
if (getLangOpts().ObjCAutoRefCount && inferObjCARCLifetime(VDecl)) if (getLangOpts().ObjCAutoRefCount && inferObjCARCLifetime(VDecl))
VDecl->setInvalidDecl(); VDecl->setInvalidDecl();
// Warn if we deduced 'id'. 'auto' usually implies type-safety, but using
// 'id' instead of a specific object type prevents most of our usual checks.
// We only want to warn outside of template instantiations, though:
// inside a template, the 'id' could have come from a parameter.
if (ActiveTemplateInstantiations.empty() && !DefaultedToAuto &&
DeducedType->isObjCIdType()) {
SourceLocation Loc =
VDecl->getTypeSourceInfo()->getTypeLoc().getBeginLoc();
Diag(Loc, diag::warn_auto_var_is_id)
<< VDecl->getDeclName() << DeduceInit->getSourceRange();
}
// If this is a redeclaration, check that the type we just deduced matches // If this is a redeclaration, check that the type we just deduced matches
// the previously declared type. // the previously declared type.
if (VarDecl *Old = VDecl->getPreviousDecl()) { if (VarDecl *Old = VDecl->getPreviousDecl()) {
// We never need to merge the type, because we cannot form an incomplete // We never need to merge the type, because we cannot form an incomplete
// array of auto, nor deduce such a type. // array of auto, nor deduce such a type.
MergeVarDeclTypes(VDecl, Old, /*MergeTypeWithPrevious*/false); MergeVarDeclTypes(VDecl, Old, /*MergeTypeWithPrevious*/ false);
} }
// Check the deduced type is valid for a variable declaration. // Check the deduced type is valid for a variable declaration.
CheckVariableDeclarationType(VDecl); CheckVariableDeclarationType(VDecl);
if (VDecl->isInvalidDecl()) if (VDecl->isInvalidDecl())
return; return;
// If all looks well, warn if this is a case that will change meaning when
// we implement N3922.
if (DirectInit && !CXXDirectInit && isa<InitListExpr>(Init)) {
Diag(Init->getLocStart(),
diag::warn_auto_var_direct_list_init)
<< FixItHint::CreateInsertion(Init->getLocStart(), "=");
}
} }
// dllimport cannot be used on variable definitions. // dllimport cannot be used on variable definitions.
@ -9262,17 +9280,18 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
} }
// Perform the initialization. // Perform the initialization.
ParenListExpr *CXXDirectInit = dyn_cast<ParenListExpr>(Init);
if (!VDecl->isInvalidDecl()) { if (!VDecl->isInvalidDecl()) {
InitializedEntity Entity = InitializedEntity::InitializeVariable(VDecl); InitializedEntity Entity = InitializedEntity::InitializeVariable(VDecl);
InitializationKind Kind InitializationKind Kind =
= DirectInit ? DirectInit
CXXDirectInit ? InitializationKind::CreateDirect(VDecl->getLocation(), ? CXXDirectInit
Init->getLocStart(), ? InitializationKind::CreateDirect(VDecl->getLocation(),
Init->getLocEnd()) Init->getLocStart(),
: InitializationKind::CreateDirectList( Init->getLocEnd())
VDecl->getLocation()) : InitializationKind::CreateDirectList(VDecl->getLocation())
: InitializationKind::CreateCopy(VDecl->getLocation(), : InitializationKind::CreateCopy(VDecl->getLocation(),
Init->getLocStart()); Init->getLocStart());
MultiExprArg Args = Init; MultiExprArg Args = Init;
if (CXXDirectInit) if (CXXDirectInit)
@ -9336,7 +9355,7 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
if (VDecl->getType().getObjCLifetime() == Qualifiers::OCL_Strong && if (VDecl->getType().getObjCLifetime() == Qualifiers::OCL_Strong &&
!Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, !Diags.isIgnored(diag::warn_arc_repeated_use_of_weak,
Init->getLocStart())) Init->getLocStart()))
getCurFunction()->markSafeWeakUse(Init); getCurFunction()->markSafeWeakUse(Init);
} }
// The initialization is usually a full-expression. // The initialization is usually a full-expression.

View File

@ -699,18 +699,11 @@ void Sema::deduceClosureReturnType(CapturingScopeInfo &CSI) {
} }
} }
QualType Sema::performLambdaInitCaptureInitialization(SourceLocation Loc, QualType Sema::buildLambdaInitCaptureInitialization(SourceLocation Loc,
bool ByRef, bool ByRef,
IdentifierInfo *Id, IdentifierInfo *Id,
Expr *&Init) { bool IsDirectInit,
Expr *&Init) {
// We do not need to distinguish between direct-list-initialization
// and copy-list-initialization here, because we will always deduce
// std::initializer_list<T>, and direct- and copy-list-initialization
// always behave the same for such a type.
// FIXME: We should model whether an '=' was present.
const bool IsDirectInit = isa<ParenListExpr>(Init) || isa<InitListExpr>(Init);
// Create an 'auto' or 'auto&' TypeSourceInfo that we can use to // Create an 'auto' or 'auto&' TypeSourceInfo that we can use to
// deduce against. // deduce against.
QualType DeductType = Context.getAutoDeductType(); QualType DeductType = Context.getAutoDeductType();
@ -723,50 +716,16 @@ QualType Sema::performLambdaInitCaptureInitialization(SourceLocation Loc,
} }
TypeSourceInfo *TSI = TLB.getTypeSourceInfo(Context, DeductType); TypeSourceInfo *TSI = TLB.getTypeSourceInfo(Context, DeductType);
// Are we a non-list direct initialization? // Deduce the type of the init capture.
ParenListExpr *CXXDirectInit = dyn_cast<ParenListExpr>(Init); QualType DeducedType = deduceVarTypeFromInitializer(
/*VarDecl*/nullptr, DeclarationName(Id), DeductType, TSI,
Expr *DeduceInit = Init; SourceRange(Loc, Loc), IsDirectInit, Init);
// Initializer could be a C++ direct-initializer. Deduction only works if it
// contains exactly one expression.
if (CXXDirectInit) {
if (CXXDirectInit->getNumExprs() == 0) {
Diag(CXXDirectInit->getLocStart(), diag::err_init_capture_no_expression)
<< DeclarationName(Id) << TSI->getType() << Loc;
return QualType();
} else if (CXXDirectInit->getNumExprs() > 1) {
Diag(CXXDirectInit->getExpr(1)->getLocStart(),
diag::err_init_capture_multiple_expressions)
<< DeclarationName(Id) << TSI->getType() << Loc;
return QualType();
} else {
DeduceInit = CXXDirectInit->getExpr(0);
if (isa<InitListExpr>(DeduceInit))
Diag(CXXDirectInit->getLocStart(), diag::err_init_capture_paren_braces)
<< DeclarationName(Id) << Loc;
}
}
// Now deduce against the initialization expression and store the deduced
// type below.
QualType DeducedType;
if (DeduceAutoType(TSI, DeduceInit, DeducedType) == DAR_Failed) {
if (isa<InitListExpr>(Init))
Diag(Loc, diag::err_init_capture_deduction_failure_from_init_list)
<< DeclarationName(Id)
<< (DeduceInit->getType().isNull() ? TSI->getType()
: DeduceInit->getType())
<< DeduceInit->getSourceRange();
else
Diag(Loc, diag::err_init_capture_deduction_failure)
<< DeclarationName(Id) << TSI->getType()
<< (DeduceInit->getType().isNull() ? TSI->getType()
: DeduceInit->getType())
<< DeduceInit->getSourceRange();
}
if (DeducedType.isNull()) if (DeducedType.isNull())
return QualType(); return QualType();
// Are we a non-list direct initialization?
ParenListExpr *CXXDirectInit = dyn_cast<ParenListExpr>(Init);
// Perform initialization analysis and ensure any implicit conversions // Perform initialization analysis and ensure any implicit conversions
// (such as lvalue-to-rvalue) are enforced. // (such as lvalue-to-rvalue) are enforced.
InitializedEntity Entity = InitializedEntity Entity =
@ -803,9 +762,10 @@ QualType Sema::performLambdaInitCaptureInitialization(SourceLocation Loc,
return DeducedType; return DeducedType;
} }
VarDecl *Sema::createLambdaInitCaptureVarDecl(SourceLocation Loc, VarDecl *Sema::createLambdaInitCaptureVarDecl(SourceLocation Loc,
QualType InitCaptureType, IdentifierInfo *Id, Expr *Init) { QualType InitCaptureType,
IdentifierInfo *Id,
unsigned InitStyle, Expr *Init) {
TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(InitCaptureType, TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(InitCaptureType,
Loc); Loc);
// Create a dummy variable representing the init-capture. This is not actually // Create a dummy variable representing the init-capture. This is not actually
@ -816,6 +776,8 @@ VarDecl *Sema::createLambdaInitCaptureVarDecl(SourceLocation Loc,
Loc, Id, InitCaptureType, TSI, SC_Auto); Loc, Id, InitCaptureType, TSI, SC_Auto);
NewVD->setInitCapture(true); NewVD->setInitCapture(true);
NewVD->setReferenced(true); NewVD->setReferenced(true);
// FIXME: Pass in a VarDecl::InitializationStyle.
NewVD->setInitStyle(static_cast<VarDecl::InitializationStyle>(InitStyle));
NewVD->markUsed(Context); NewVD->markUsed(Context);
NewVD->setInit(Init); NewVD->setInit(Init);
return NewVD; return NewVD;
@ -1014,8 +976,23 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
// in this case. // in this case.
if (C->InitCaptureType.get().isNull()) if (C->InitCaptureType.get().isNull())
continue; continue;
Var = createLambdaInitCaptureVarDecl(C->Loc, C->InitCaptureType.get(),
C->Id, C->Init.get()); unsigned InitStyle;
switch (C->InitKind) {
case LambdaCaptureInitKind::NoInit:
llvm_unreachable("not an init-capture?");
case LambdaCaptureInitKind::CopyInit:
InitStyle = VarDecl::CInit;
break;
case LambdaCaptureInitKind::DirectInit:
InitStyle = VarDecl::CallInit;
break;
case LambdaCaptureInitKind::ListInit:
InitStyle = VarDecl::ListInit;
break;
}
Var = createLambdaInitCaptureVarDecl(C->Loc, C->InitCaptureType.get(),
C->Id, InitStyle, C->Init.get());
// C++1y [expr.prim.lambda]p11: // C++1y [expr.prim.lambda]p11:
// An init-capture behaves as if it declares and explicitly // An init-capture behaves as if it declares and explicitly
// captures a variable [...] whose declarative region is the // captures a variable [...] whose declarative region is the
@ -1023,6 +1000,9 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
if (Var) if (Var)
PushOnScopeChains(Var, CurScope, false); PushOnScopeChains(Var, CurScope, false);
} else { } else {
assert(C->InitKind == LambdaCaptureInitKind::NoInit &&
"init capture has valid but null init?");
// C++11 [expr.prim.lambda]p8: // C++11 [expr.prim.lambda]p8:
// If a lambda-capture includes a capture-default that is &, the // If a lambda-capture includes a capture-default that is &, the
// identifiers in the lambda-capture shall not be preceded by &. // identifiers in the lambda-capture shall not be preceded by &.

View File

@ -9623,9 +9623,10 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
VarDecl *OldVD = C->getCapturedVar(); VarDecl *OldVD = C->getCapturedVar();
QualType NewInitCaptureType = QualType NewInitCaptureType =
getSema().performLambdaInitCaptureInitialization(C->getLocation(), getSema().buildLambdaInitCaptureInitialization(
OldVD->getType()->isReferenceType(), OldVD->getIdentifier(), C->getLocation(), OldVD->getType()->isReferenceType(),
NewExprInit); OldVD->getIdentifier(),
C->getCapturedVar()->getInitStyle() != VarDecl::CInit, NewExprInit);
NewExprInitResult = NewExprInit; NewExprInitResult = NewExprInit;
InitCaptureExprsAndTypes[C - E->capture_begin()] = InitCaptureExprsAndTypes[C - E->capture_begin()] =
std::make_pair(NewExprInitResult, NewInitCaptureType); std::make_pair(NewExprInitResult, NewInitCaptureType);
@ -9732,8 +9733,8 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
} }
VarDecl *OldVD = C->getCapturedVar(); VarDecl *OldVD = C->getCapturedVar();
VarDecl *NewVD = getSema().createLambdaInitCaptureVarDecl( VarDecl *NewVD = getSema().createLambdaInitCaptureVarDecl(
OldVD->getLocation(), InitExprTypePair.second, OldVD->getLocation(), InitExprTypePair.second, OldVD->getIdentifier(),
OldVD->getIdentifier(), Init.get()); OldVD->getInitStyle(), Init.get());
if (!NewVD) if (!NewVD)
Invalid = true; Invalid = true;
else { else {

View File

@ -7,9 +7,9 @@ namespace dr1346 { // dr1346: 3.5
auto a(1); // expected-error 0-1{{extension}} auto a(1); // expected-error 0-1{{extension}}
auto b(1, 2); // expected-error {{multiple expressions}} expected-error 0-1{{extension}} auto b(1, 2); // expected-error {{multiple expressions}} expected-error 0-1{{extension}}
#if __cplusplus >= 201103L #if __cplusplus >= 201103L
auto c({}); // expected-error {{parenthesized initializer list}} expected-error {{cannot deduce}} auto c({}); // expected-error {{parenthesized initializer list}}
auto d({1}); // expected-error {{parenthesized initializer list}} expected-error {{<initializer_list>}} auto d({1}); // expected-error {{parenthesized initializer list}}
auto e({1, 2}); // expected-error {{parenthesized initializer list}} expected-error {{<initializer_list>}} auto e({1, 2}); // expected-error {{parenthesized initializer list}}
#endif #endif
template<typename...Ts> void f(Ts ...ts) { // expected-error 0-1{{extension}} template<typename...Ts> void f(Ts ...ts) { // expected-error 0-1{{extension}}
auto x(ts...); // expected-error {{empty}} expected-error 0-1{{extension}} auto x(ts...); // expected-error {{empty}} expected-error 0-1{{extension}}
@ -21,9 +21,9 @@ namespace dr1346 { // dr1346: 3.5
[a(1)] {} (); // expected-error 0-1{{extension}} [a(1)] {} (); // expected-error 0-1{{extension}}
[b(1, 2)] {} (); // expected-error {{multiple expressions}} expected-error 0-1{{extension}} [b(1, 2)] {} (); // expected-error {{multiple expressions}} expected-error 0-1{{extension}}
#if __cplusplus >= 201103L #if __cplusplus >= 201103L
[c({})] {} (); // expected-error {{parenthesized initializer list}} expected-error {{cannot deduce}} expected-error 0-1{{extension}} [c({})] {} (); // expected-error {{parenthesized initializer list}} expected-error 0-1{{extension}}
[d({1})] {} (); // expected-error {{parenthesized initializer list}} expected-error {{<initializer_list>}} expected-error 0-1{{extension}} [d({1})] {} (); // expected-error {{parenthesized initializer list}} expected-error 0-1{{extension}}
[e({1, 2})] {} (); // expected-error {{parenthesized initializer list}} expected-error {{<initializer_list>}} expected-error 0-1{{extension}} [e({1, 2})] {} (); // expected-error {{parenthesized initializer list}} expected-error 0-1{{extension}}
#endif #endif
} }
#endif #endif

View File

@ -48,7 +48,8 @@ auto bad_init_2 = [a(1, 2)] {}; // expected-error {{initializer for lambda captu
auto bad_init_3 = [&a(void_fn())] {}; // expected-error {{cannot form a reference to 'void'}} auto bad_init_3 = [&a(void_fn())] {}; // expected-error {{cannot form a reference to 'void'}}
auto bad_init_4 = [a(void_fn())] {}; // expected-error {{has incomplete type 'void'}} auto bad_init_4 = [a(void_fn())] {}; // expected-error {{has incomplete type 'void'}}
auto bad_init_5 = [a(overload_fn)] {}; // expected-error {{cannot deduce type for lambda capture 'a' from initializer of type '<overloaded function}} auto bad_init_5 = [a(overload_fn)] {}; // expected-error {{cannot deduce type for lambda capture 'a' from initializer of type '<overloaded function}}
auto bad_init_6 = [a{overload_fn}] {}; // expected-error {{cannot deduce type for lambda capture 'a' from initializer list}} expected-warning {{will change meaning in a future version of Clang}} auto bad_init_6 = [a{overload_fn}] {}; // expected-error {{cannot deduce type for lambda capture 'a' from initializer list}}
auto bad_init_7 = [a{{1}}] {}; // expected-error {{cannot deduce type for lambda capture 'a' from nested initializer list}}
template<typename...T> void pack_1(T...t) { (void)[a(t...)] {}; } // expected-error {{initializer missing for lambda capture 'a'}} template<typename...T> void pack_1(T...t) { (void)[a(t...)] {}; } // expected-error {{initializer missing for lambda capture 'a'}}
template void pack_1<>(); // expected-note {{instantiation of}} template void pack_1<>(); // expected-note {{instantiation of}}
@ -61,7 +62,7 @@ auto a = [a(4), b = 5, &c = static_cast<const int&&>(0)] {
using T = decltype(c); using T = decltype(c);
using T = const int &; using T = const int &;
}; };
auto b = [a{0}] {}; // expected-error {{include <initializer_list>}} expected-warning {{will change meaning in a future version of Clang}} auto b = [a{0}] {}; // OK, per N3922
struct S { S(); S(S&&); }; struct S { S(); S(S&&); };
template<typename T> struct remove_reference { typedef T type; }; template<typename T> struct remove_reference { typedef T type; };

View File

@ -61,7 +61,7 @@ class C {
int z; int z;
void init_capture() { void init_capture() {
[n(0)] () mutable -> int { return ++n; }; // expected-warning{{extension}} [n(0)] () mutable -> int { return ++n; }; // expected-warning{{extension}}
[n{0}] { return; }; // expected-error {{<initializer_list>}} expected-warning{{extension}} expected-warning{{will change meaning in a future version}} [n{0}] { return; }; // expected-warning{{extension}}
[n = 0] { return ++n; }; // expected-error {{captured by copy in a non-mutable}} expected-warning{{extension}} [n = 0] { return ++n; }; // expected-error {{captured by copy in a non-mutable}} expected-warning{{extension}}
[n = {0}] { return; }; // expected-error {{<initializer_list>}} expected-warning{{extension}} [n = {0}] { return; }; // expected-error {{<initializer_list>}} expected-warning{{extension}}
[a([&b = z]{})](){}; // expected-warning 2{{extension}} [a([&b = z]{})](){}; // expected-warning 2{{extension}}

View File

@ -21,7 +21,7 @@ class C {
[foo(bar)] () {}; [foo(bar)] () {};
[foo = bar] () {}; [foo = bar] () {};
[foo{bar}] () {}; // expected-error {{<initializer_list>}} expected-warning {{will change meaning}} [foo{bar}] () {};
[foo = {bar}] () {}; // expected-error {{<initializer_list>}} [foo = {bar}] () {}; // expected-error {{<initializer_list>}}
[foo(bar) baz] () {}; // expected-error {{called object type 'int' is not a function}} [foo(bar) baz] () {}; // expected-error {{called object type 'int' is not a function}}

View File

@ -117,8 +117,10 @@ void argument_deduction() {
void auto_deduction() { void auto_deduction() {
auto l = {1, 2, 3, 4}; auto l = {1, 2, 3, 4};
auto l2 {1, 2, 3, 4}; // expected-warning {{will change meaning in a future version of Clang}} auto l2 {1, 2, 3, 4}; // expected-error {{initializer for variable 'l2' with type 'auto' contains multiple expressions}}
auto l3 {1};
static_assert(same_type<decltype(l), std::initializer_list<int>>::value, ""); static_assert(same_type<decltype(l), std::initializer_list<int>>::value, "");
static_assert(same_type<decltype(l3), int>::value, "");
auto bl = {1, 2.0}; // expected-error {{cannot deduce}} auto bl = {1, 2.0}; // expected-error {{cannot deduce}}
for (int i : {1, 2, 3, 4}) {} for (int i : {1, 2, 3, 4}) {}
@ -190,7 +192,7 @@ namespace rdar11948732 {
} }
namespace PR14272 { namespace PR14272 {
auto x { { 0, 0 } }; // expected-error {{cannot deduce actual type for variable 'x' with type 'auto' from initializer list}} auto x { { 0, 0 } }; // expected-error {{cannot deduce type for variable 'x' with type 'auto' from nested initializer list}}
} }
namespace initlist_of_array { namespace initlist_of_array {

View File

@ -190,3 +190,9 @@ int run() {
} }
} }
namespace N3922 {
struct X { X(); explicit X(const X&); int n; };
auto a = [x{X()}] { return x.n; }; // ok
auto b = [x = {X()}] {}; // expected-error{{<initializer_list>}}
}

View File

@ -564,33 +564,33 @@ as the draft C++1z standard evolves.</p>
<tr> <tr>
<td>New <tt>auto</tt> rules for direct-list-initialization <td>New <tt>auto</tt> rules for direct-list-initialization
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3922.html">N3922</a></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3922.html">N3922</a></td>
<td class="none" align="center">No</td> <td class="svn" align="center">Clang 3.8 <a href="#n3922">(7)</a></td>
</tr> </tr>
<!-- Urbana papers --> <!-- Urbana papers -->
<tr> <tr>
<td>Fold expressions</td> <td>Fold expressions</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4295.html">N4295</a></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4295.html">N4295</a></td>
<td class="svn" align="center">Clang 3.6</td> <td class="full" align="center">Clang 3.6</td>
</tr> </tr>
<tr> <tr>
<td><tt>u8</tt> character literals</td> <td><tt>u8</tt> character literals</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4267.html">N4267</a></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4267.html">N4267</a></td>
<td class="svn" align="center">Clang 3.6</td> <td class="full" align="center">Clang 3.6</td>
</tr> </tr>
<tr> <tr>
<td>Nested namespace definition</td> <td>Nested namespace definition</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4230.html">N4230</a></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4230.html">N4230</a></td>
<td class="svn" align="center">Clang 3.6</td> <td class="full" align="center">Clang 3.6</td>
</tr> </tr>
<tr> <tr>
<td>Attributes for namespaces and enumerators</td> <td>Attributes for namespaces and enumerators</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4266.html">N4266</a></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4266.html">N4266</a></td>
<td class="svn" align="center">Clang 3.6</td> <td class="full" align="center">Clang 3.6</td>
</tr> </tr>
<tr> <tr>
<td>Allow constant evaluation for all non-type template arguments</td> <td>Allow constant evaluation for all non-type template arguments</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4268.html">N4268</a></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4268.html">N4268</a></td>
<td class="svn" align="center">Clang 3.6</td> <td class="full" align="center">Clang 3.6</td>
</tr> </tr>
<!-- Kona papers --> <!-- Kona papers -->
<tr> <tr>
@ -615,6 +615,14 @@ as the draft C++1z standard evolves.</p>
</tr> </tr>
</table> </table>
<p>
<span id="n3922">(7): This is a backwards-incompatible change that is applied to
all language versions that allow type deduction from <tt>auto</tt>
(per the request of the C++ committee).
In Clang 3.7, a warning is emitted for all cases that would change meaning.
</span>
</p>
<h2 id="ts">Technical specifications and standing documents</h2> <h2 id="ts">Technical specifications and standing documents</h2>
<p>ISO C++ also publishes a number of documents describing additional language <p>ISO C++ also publishes a number of documents describing additional language
@ -636,7 +644,7 @@ Clang version they became available:</p>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="svn" align="center"> <td class="full" align="center">
Clang 3.6 (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4200">N4200</a>)</a> Clang 3.6 (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4200">N4200</a>)</a>
</td> </td>
</tr> </tr>