95 changes: 50 additions & 45 deletions clang/lib/Parse/ParseExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
bool MemberOfUnknownSpecialization;
if (!Actions.isTemplateName(getCurScope(), SS, /*hasTemplateKeyword=*/false,
TemplateName, ObjectType, EnteringContext,
Template, MemberOfUnknownSpecialization))
Template, MemberOfUnknownSpecialization,
/*Disambiguation=*/false, /*MayBeNNS=*/true))
return;

FixDigraph(*this, PP, Next, SecondToken, tok::unknown,
Expand Down Expand Up @@ -353,7 +354,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
TemplateTy Template;
TemplateNameKind TNK = Actions.ActOnTemplateName(
getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
EnteringContext, Template, /*AllowInjectedClassName*/ true);
EnteringContext, Template, /*AllowInjectedClassName*/ true,
/*MayBeNNS=*/true);
if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateKWLoc,
TemplateName, false))
return true;
Expand Down Expand Up @@ -405,7 +407,6 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
: TemplateId->TemplateNameLoc;
SS.SetInvalid(SourceRange(StartLoc, CCLoc));
}

continue;
}

Expand Down Expand Up @@ -528,18 +529,19 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
UnqualifiedId TemplateName;
TemplateName.setIdentifier(&II, Tok.getLocation());
bool MemberOfUnknownSpecialization;
if (TemplateNameKind TNK = Actions.isTemplateName(getCurScope(), SS,
/*hasTemplateKeyword=*/false,
TemplateName,
ObjectType,
EnteringContext,
Template,
MemberOfUnknownSpecialization)) {
if (TemplateNameKind TNK = Actions.isTemplateName(
getCurScope(), SS,
/*hasTemplateKeyword=*/false, TemplateName, ObjectType,
EnteringContext, Template, MemberOfUnknownSpecialization,
/*Disambiguation=*/false,
/*MayBeNNS=*/true)) {
// If lookup didn't find anything, we treat the name as a template-name
// anyway. C++20 requires this, and in prior language modes it improves
// error recovery. But before we commit to this, check that we actually
// have something that looks like a template-argument-list next.
if (!IsTypename && TNK == TNK_Undeclared_template &&
if (!IsTypename &&
(TNK == TNK_Undeclared_template ||
(!HasScopeSpecifier && ObjectType)) &&
isTemplateArgumentList(1) == TPResult::False)
break;

Expand All @@ -566,11 +568,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
// member of an unknown specialization. However, this will only
// parse correctly as a template, so suggest the keyword 'template'
// before 'getAs' and treat this as a dependent template name.
unsigned DiagID = diag::err_missing_dependent_template_keyword;
if (getLangOpts().MicrosoftExt)
DiagID = diag::warn_missing_dependent_template_keyword;

Diag(Tok.getLocation(), DiagID)
Diag(Tok.getLocation(), diag::ext_missing_dependent_template_keyword)
<< II.getName()
<< FixItHint::CreateInsertion(Tok.getLocation(), "template ");
}
Expand Down Expand Up @@ -1920,12 +1918,12 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
// argument list. This affects examples such as
// void f(auto *p) { p->~X<int>(); }
// ... but there's no ambiguity, and nowhere to write 'template' in such an
// example, so we accept it anyway.
if (Tok.is(tok::less) &&
ParseUnqualifiedIdTemplateId(
SS, ObjectType, Base && Base->containsErrors(), SourceLocation(),
Name, NameLoc, false, SecondTypeName,
/*AssumeTemplateId=*/true))
// example, so we accept it anyway
if (Tok.is(tok::less) && ParseUnqualifiedIdTemplateId(
SS, ObjectType, Base && Base->containsErrors(),
/*TemplateKWLoc=*/SourceLocation(), TildeLoc,
Name, NameLoc, false, SecondTypeName,
/*AssumeTemplateId=*/true))
return ExprError();

return Actions.ActOnPseudoDestructorExpr(getCurScope(), Base, OpLoc, OpKind,
Expand Down Expand Up @@ -2532,8 +2530,9 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS, DeclaratorContext Context) {
/// \returns true if a parse error occurred, false otherwise.
bool Parser::ParseUnqualifiedIdTemplateId(
CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
SourceLocation TemplateKWLoc, IdentifierInfo *Name, SourceLocation NameLoc,
bool EnteringContext, UnqualifiedId &Id, bool AssumeTemplateId) {
SourceLocation TemplateKWLoc, SourceLocation TildeLoc, IdentifierInfo *Name,
SourceLocation NameLoc, bool EnteringContext, UnqualifiedId &Id,
bool AssumeTemplateId) {
assert(Tok.is(tok::less) && "Expected '<' to finish parsing a template-id");

TemplateTy Template;
Expand All @@ -2547,13 +2546,14 @@ bool Parser::ParseUnqualifiedIdTemplateId(
// this template-id is used to form a nested-name-specifier or not.
TNK = Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc, Id,
ObjectType, EnteringContext, Template,
/*AllowInjectedClassName*/ true);
/*AllowInjectedClassName=*/true,
TildeLoc.isValid());
} else {
bool MemberOfUnknownSpecialization;
TNK = Actions.isTemplateName(getCurScope(), SS,
TemplateKWLoc.isValid(), Id,
ObjectType, EnteringContext, Template,
MemberOfUnknownSpecialization);
TNK = Actions.isTemplateName(
getCurScope(), SS, TemplateKWLoc.isValid(), Id, ObjectType,
EnteringContext, Template, MemberOfUnknownSpecialization,
/*Disambiguation=*/false, TildeLoc.isValid());
// If lookup found nothing but we're assuming that this is a template
// name, double-check that makes sense syntactically before committing
// to it.
Expand All @@ -2580,13 +2580,13 @@ bool Parser::ParseUnqualifiedIdTemplateId(
else
Name += Id.Identifier->getName();
}
Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword)
Diag(Id.StartLocation, diag::ext_missing_dependent_template_keyword)
<< Name
<< FixItHint::CreateInsertion(Id.StartLocation, "template ");
}
TNK = Actions.ActOnTemplateName(
getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext,
Template, /*AllowInjectedClassName*/ true);
Template, /*AllowInjectedClassName=*/true, TildeLoc.isValid());
} else if (TNK == TNK_Non_template) {
return false;
}
Expand All @@ -2611,14 +2611,16 @@ bool Parser::ParseUnqualifiedIdTemplateId(
bool MemberOfUnknownSpecialization;
TemplateName.setIdentifier(Name, NameLoc);
if (ObjectType) {
TNK = Actions.ActOnTemplateName(
getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
EnteringContext, Template, /*AllowInjectedClassName*/ true);
TNK = Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc,
TemplateName, ObjectType, EnteringContext,
Template, /*AllowInjectedClassName=*/true,
/*MayBeNNS=*/true);
} else {
TNK = Actions.isTemplateName(getCurScope(), SS, TemplateKWLoc.isValid(),
TemplateName, ObjectType,
EnteringContext, Template,
MemberOfUnknownSpecialization);
TemplateName, ObjectType, EnteringContext,
Template, MemberOfUnknownSpecialization,
/*Disambiguation=*/false,
/*MayBeNNS=*/true);

if (TNK == TNK_Non_template && !Id.DestructorName.get()) {
Diag(NameLoc, diag::err_destructor_template_id)
Expand Down Expand Up @@ -2680,7 +2682,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(
if (Id.getKind() == UnqualifiedIdKind::IK_ConstructorName)
Id.setConstructorName(Type.get(), NameLoc, RAngleLoc);
else
Id.setDestructorName(Id.StartLocation, Type.get(), RAngleLoc);
Id.setDestructorName(TildeLoc, Type.get(), RAngleLoc);

return false;
}
Expand Down Expand Up @@ -3028,8 +3030,9 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
if (Tok.is(tok::less))
return ParseUnqualifiedIdTemplateId(
SS, ObjectType, ObjectHadErrors,
TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), Id, IdLoc,
EnteringContext, Result, TemplateSpecified);
TemplateKWLoc ? *TemplateKWLoc : SourceLocation(),
/*TildeLoc=*/SourceLocation(), Id, IdLoc, EnteringContext, Result,
TemplateSpecified);

if (TemplateSpecified) {
TemplateNameKind TNK =
Expand Down Expand Up @@ -3124,13 +3127,15 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
Tok.is(tok::less))
return ParseUnqualifiedIdTemplateId(
SS, ObjectType, ObjectHadErrors,
TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr,
SourceLocation(), EnteringContext, Result, TemplateSpecified);
TemplateKWLoc ? *TemplateKWLoc : SourceLocation(),
/*TildeLoc=*/SourceLocation(), /*Name=*/nullptr,
/*NameLoc=*/SourceLocation(), EnteringContext, Result,
TemplateSpecified);
else if (TemplateSpecified &&
Actions.ActOnTemplateName(
getCurScope(), SS, *TemplateKWLoc, Result, ObjectType,
EnteringContext, Template,
/*AllowInjectedClassName*/ true) == TNK_Non_template)
/*AllowInjectedClassName=*/true) == TNK_Non_template)
return true;

return false;
Expand Down Expand Up @@ -3220,8 +3225,8 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
Result.setDestructorName(TildeLoc, nullptr, ClassNameLoc);
return ParseUnqualifiedIdTemplateId(
SS, ObjectType, ObjectHadErrors,
TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), ClassName,
ClassNameLoc, EnteringContext, Result, TemplateSpecified);
TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), TildeLoc,
ClassName, ClassNameLoc, EnteringContext, Result, TemplateSpecified);
}

// Note that this is a destructor name.
Expand Down
11 changes: 7 additions & 4 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,18 +114,21 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts,
// here because we don't want to allow arbitrary orderings.
ParsedAttributes CXX11Attrs(AttrFactory);
MaybeParseCXX11Attributes(CXX11Attrs, /*MightBeObjCMessageSend*/ true);
ParsedAttributes GNUAttrs(AttrFactory);
ParsedAttributes GNUOrMSAttrs(AttrFactory);
if (getLangOpts().OpenCL)
MaybeParseGNUAttributes(GNUAttrs);
MaybeParseGNUAttributes(GNUOrMSAttrs);

if (getLangOpts().HLSL)
MaybeParseMicrosoftAttributes(GNUOrMSAttrs);

StmtResult Res = ParseStatementOrDeclarationAfterAttributes(
Stmts, StmtCtx, TrailingElseLoc, CXX11Attrs, GNUAttrs);
Stmts, StmtCtx, TrailingElseLoc, CXX11Attrs, GNUOrMSAttrs);
MaybeDestroyTemplateIds();

// Attributes that are left should all go on the statement, so concatenate the
// two lists.
ParsedAttributes Attrs(AttrFactory);
takeAndConcatenateAttrs(CXX11Attrs, GNUAttrs, Attrs);
takeAndConcatenateAttrs(CXX11Attrs, GNUOrMSAttrs, Attrs);

assert((Attrs.empty() || Res.isInvalid() || Res.isUsable()) &&
"attributes on empty statement");
Expand Down
195 changes: 91 additions & 104 deletions clang/lib/Sema/SemaCXXScopeSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,29 +356,41 @@ bool Sema::isAcceptableNestedNameSpecifier(const NamedDecl *SD,
return false;
}

NamedDecl *Sema::FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS) {
if (!S || !NNS)
return nullptr;
/// If the given nested-name-specifier begins with a bare identifier
/// (e.g., Base::), perform name lookup for that identifier as a
/// nested-name-specifier within the given scope, and return the result of that
/// name lookup.
bool Sema::LookupFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS,
UnresolvedSetImpl &R) {
if (!S)
return false;

while (NNS->getPrefix())
NNS = NNS->getPrefix();

if (NNS->getKind() != NestedNameSpecifier::Identifier)
return nullptr;

LookupResult Found(*this, NNS->getAsIdentifier(), SourceLocation(),
LookupNestedNameSpecifierName);
// FIXME: This is a rather nasty hack! Ideally we should get the results
// from LookupTemplateName/BuildCXXNestedNameSpecifier.
const IdentifierInfo *II = NNS->getAsIdentifier();
if (!II) {
if (const auto *DTST =
dyn_cast_if_present<DependentTemplateSpecializationType>(
NNS->getAsType()))
II = DTST->getIdentifier();
else
return false;
}
assert(II && "Missing first qualifier in scope");
LookupResult Found(*this, II, SourceLocation(),
NNS->getAsIdentifier() ? LookupNestedNameSpecifierName
: LookupOrdinaryName);
LookupName(Found, S);
assert(!Found.isAmbiguous() && "Cannot handle ambiguities here yet");

if (!Found.isSingleResult())
return nullptr;

NamedDecl *Result = Found.getFoundDecl();
if (isAcceptableNestedNameSpecifier(Result))
return Result;
if (Found.empty())
return false;

return nullptr;
R.addAllDecls(Found.asUnresolvedSet().pairs());
Found.suppressDiagnostics();
return true;
}

namespace {
Expand Down Expand Up @@ -407,112 +419,82 @@ class NestedNameSpecifierValidatorCCC final

bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
bool EnteringContext, CXXScopeSpec &SS,
NamedDecl *ScopeLookupResult,
bool ErrorRecoveryLookup,
bool *IsCorrectedToColon,
bool OnlyNamespace) {
if (IdInfo.Identifier->isEditorPlaceholder())
return true;
if (IsCorrectedToColon)
*IsCorrectedToColon = false;

QualType ObjectType = GetTypeFromParser(IdInfo.ObjectType);
LookupResult Found(*this, IdInfo.Identifier, IdInfo.IdentifierLoc,
OnlyNamespace ? LookupNamespaceName
: LookupNestedNameSpecifierName);
QualType ObjectType = GetTypeFromParser(IdInfo.ObjectType);

// Determine where to perform name lookup
DeclContext *LookupCtx = nullptr;
bool isDependent = false;
if (IsCorrectedToColon)
*IsCorrectedToColon = false;
if (!ObjectType.isNull()) {
// This nested-name-specifier occurs in a member access expression, e.g.,
// x->B::f, and we are looking into the type of the object.
assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist");
LookupCtx = computeDeclContext(ObjectType);
isDependent = ObjectType->isDependentType();
} else if (SS.isSet()) {
// This nested-name-specifier occurs after another nested-name-specifier,
// so look into the context associated with the prior nested-name-specifier.
LookupCtx = computeDeclContext(SS, EnteringContext);
isDependent = isDependentScopeSpecifier(SS);
Found.setContextRange(SS.getRange());
// C++ [basic.lookup.qual.general]p3:
// Qualified name lookup in a class, namespace, or enumeration performs a
// search of the scope associated with it except as specified below.
LookupParsedName(Found, S, &SS, ObjectType,
/*AllowBuiltinCreation=*/false, EnteringContext);

// C++ [basic.lookup.qual.general]p3:
// [...] Unless otherwise specified, a qualified name undergoes qualified
// name lookup in its lookup context from the point where it appears unless
// the lookup context either is dependent and is not the current
// instantiation or is not a class or class template.
if (Found.wasNotFoundInCurrentInstantiation()) {
// Don't speculate if we're just trying to improve error recovery.
if (ErrorRecoveryLookup)
return true;

// The lookup context is dependent and either:
// - it is not the current instantiation, or
// - it is the current instantiation, it has at least one dependent base
// class, and qualified lookup found nothing.
// Build a dependent nested-name-specifier. We will lookup the name again
// during instantiation.
SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc, IdInfo.CCLoc);
return false;
}

bool ObjectTypeSearchedInScope = false;
if (LookupCtx) {
// Perform "qualified" name lookup into the declaration context we
// computed, which is either the type of the base of a member access
// expression or the declaration context associated with a prior
// nested-name-specifier.

// The declaration context must be complete.
if (!LookupCtx->isDependentContext() &&
RequireCompleteDeclContext(SS, LookupCtx))
return true;

LookupQualifiedName(Found, LookupCtx);

if (!ObjectType.isNull() && Found.empty()) {
// 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 -> operator is
// looked up both in the context of the entire postfix-expression and in
// the scope of the class of the object expression. If the name is found
// only in the scope of the class of the object expression, the name
// shall refer to a class-name. If the name is found only in the
// context of the entire postfix-expression, the name shall refer to a
// class-name or namespace-name. [...]
//
// Qualified name lookup into a class will not find a namespace-name,
// so we do not need to diagnose that case specifically. However,
// this qualified name lookup may find nothing. In that case, perform
// unqualified name lookup in the given scope (if available) or
// reconstruct the result from when name lookup was performed at template
// definition time.
if (S)
LookupName(Found, S);
else if (ScopeLookupResult)
Found.addDecl(ScopeLookupResult);

ObjectTypeSearchedInScope = true;
// C++ [basic.lookup.qual.general]p2:
// A member-qualified name is the (unique) component name, if any, of
// - an unqualified-id or
// - a nested-name-specifier of the form type-name :: or namespace-name ::
// in the id-expression of a class member access expression.
//
// C++ [basic.lookup.qual.general]p3:
// [...] If nothing is found by qualified lookup for a member-qualified
// name that is the terminal name of a nested-name-specifier and is not
// dependent, it undergoes unqualified lookup.
//
// In 'x.A::B::y', 'A' will undergo unqualified lookup if qualified lookup
// in the type of 'x' finds nothing. If the lookup context is dependent,
// we perform the unqualified lookup in the template definition context
// and store the results so we can replicate the lookup during instantiation.
if (Found.empty() && !ObjectType.isNull()) {
if (S) {
LookupName(Found, S);
} else if (!SS.getUnqualifiedLookups().empty()) {
Found.addAllDecls(SS.getUnqualifiedLookups());
Found.resolveKind();
}
} else if (!isDependent) {
// Perform unqualified name lookup in the current scope.
LookupName(Found, S);
ObjectTypeSearchedInScope = true;
}

if (Found.isAmbiguous())
return true;

// If we performed lookup into a dependent context and did not find anything,
// that's fine: just build a dependent nested-name-specifier.
if (Found.empty() && isDependent &&
!(LookupCtx && LookupCtx->isRecord() &&
(!cast<CXXRecordDecl>(LookupCtx)->hasDefinition() ||
!cast<CXXRecordDecl>(LookupCtx)->hasAnyDependentBases()))) {
// Don't speculate if we're just trying to improve error recovery.
if (ErrorRecoveryLookup)
return true;

// We were not able to compute the declaration context for a dependent
// base object type or prior nested-name-specifier, so this
// nested-name-specifier refers to an unknown specialization. Just build
// a dependent nested-name-specifier.
SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc, IdInfo.CCLoc);
return false;
}

if (Found.empty() && !ErrorRecoveryLookup) {
// If identifier is not found as class-name-or-namespace-name, but is found
// as other entity, don't look for typos.
LookupResult R(*this, Found.getLookupNameInfo(), LookupOrdinaryName);
if (LookupCtx)
LookupQualifiedName(R, LookupCtx);
else if (S && !isDependent)
LookupName(R, S);
LookupParsedName(R, S, &SS, ObjectType,
/*AllowBuiltinCreation=*/false, EnteringContext);

if (!R.empty()) {
// Don't diagnose problems with this speculative lookup.
R.suppressDiagnostics();
Expand All @@ -539,6 +521,11 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
}
}

DeclContext *LookupCtx =
SS.isSet()
? computeDeclContext(SS, EnteringContext)
: (!ObjectType.isNull() ? computeDeclContext(ObjectType) : nullptr);

if (Found.empty() && !ErrorRecoveryLookup && !getLangOpts().MSVCCompat) {
// We haven't found anything, and we're not recovering from a
// different kind of error, so look for typos.
Expand Down Expand Up @@ -594,14 +581,14 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
// scope, reconstruct the result from the template instantiation itself.
//
// Note that C++11 does *not* perform this redundant lookup.
NamedDecl *OuterDecl;
NamedDecl *OuterDecl = nullptr;
if (S) {
LookupResult FoundOuter(*this, IdInfo.Identifier, IdInfo.IdentifierLoc,
LookupNestedNameSpecifierName);
LookupName(FoundOuter, S);
OuterDecl = FoundOuter.getAsSingle<NamedDecl>();
} else
OuterDecl = ScopeLookupResult;
} else if (!SS.getUnqualifiedLookups().empty())
OuterDecl = SS.getUnqualifiedLookups().front().getDecl();

if (isAcceptableNestedNameSpecifier(OuterDecl) &&
OuterDecl->getCanonicalDecl() != SD->getCanonicalDecl() &&
Expand Down Expand Up @@ -779,7 +766,7 @@ bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
return true;

return BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS,
/*ScopeLookupResult=*/nullptr, false,
/*ErrorRecoveryLookup=*/false,
IsCorrectedToColon, OnlyNamespace);
}

Expand Down Expand Up @@ -840,7 +827,7 @@ bool Sema::IsInvalidUnlessNestedName(Scope *S, CXXScopeSpec &SS,
return false;

return !BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS,
/*ScopeLookupResult=*/nullptr, true);
/*ErrorRecoveryLookup=*/true);
}

bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/SemaCoroutine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,8 @@ static ExprResult buildMemberCall(Sema &S, Expr *Base, SourceLocation Loc,
// FIXME: Fix BuildMemberReferenceExpr to take a const CXXScopeSpec&.
CXXScopeSpec SS;
ExprResult Result = S.BuildMemberReferenceExpr(
Base, Base->getType(), Loc, /*IsPtr=*/false, SS,
SourceLocation(), nullptr, NameInfo, /*TemplateArgs=*/nullptr,
Base, Base->getType(), Loc, /*IsPtr=*/false, SS, SourceLocation(),
NameInfo, /*TemplateArgs=*/nullptr,
/*Scope=*/nullptr);
if (Result.isInvalid())
return ExprError();
Expand Down
11 changes: 10 additions & 1 deletion clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1676,6 +1676,15 @@ bool Sema::CheckRedeclarationExported(NamedDecl *New, NamedDecl *Old) {
if (IsOldExported)
return false;

// If the Old declaration are not attached to named modules
// and the New declaration are attached to global module.
// It should be fine to allow the export since it doesn't change
// the linkage of declarations. See
// https://github.com/llvm/llvm-project/issues/98583 for details.
if (!Old->isInNamedModule() && New->getOwningModule() &&
New->getOwningModule()->isImplicitGlobalModule())
return false;

assert(IsNewExported);

auto Lk = Old->getFormalLinkage();
Expand Down Expand Up @@ -10094,7 +10103,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
// check at the end of the TU (or when the PMF starts) to see that we
// have a definition at that point.
if (isInline && !D.isFunctionDefinition() && getLangOpts().CPlusPlus20 &&
NewFD->hasOwningModule() && NewFD->getOwningModule()->isNamedModule()) {
NewFD->isInNamedModule()) {
PendingInlineFuncDecls.insert(NewFD);
}
}
Expand Down
47 changes: 23 additions & 24 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1275,9 +1275,11 @@ static bool checkTupleLikeDecomposition(Sema &S,
if (UseMemberGet) {
// if [lookup of member get] finds at least one declaration, the
// initializer is e.get<i-1>().
E = S.BuildMemberReferenceExpr(E.get(), DecompType, Loc, false,
CXXScopeSpec(), SourceLocation(), nullptr,
MemberGet, &Args, nullptr);
E = S.BuildMemberReferenceExpr(E.get(), DecompType, Loc,
/*IsArrow=*/false,
/*SS=*/CXXScopeSpec(),
/*TemplateKWLoc=*/SourceLocation(),
MemberGet, &Args, /*S=*/nullptr);
if (E.isInvalid())
return true;

Expand Down Expand Up @@ -4901,16 +4903,12 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor,
MemberLookup.addDecl(Indirect ? cast<ValueDecl>(Indirect)
: cast<ValueDecl>(Field), AS_public);
MemberLookup.resolveKind();
ExprResult CtorArg
= SemaRef.BuildMemberReferenceExpr(MemberExprBase,
ParamType, Loc,
/*IsArrow=*/false,
SS,
/*TemplateKWLoc=*/SourceLocation(),
/*FirstQualifierInScope=*/nullptr,
MemberLookup,
/*TemplateArgs=*/nullptr,
/*S*/nullptr);
ExprResult CtorArg = SemaRef.BuildMemberReferenceExpr(
MemberExprBase, ParamType, Loc,
/*IsArrow=*/false, SS,
/*TemplateKWLoc=*/SourceLocation(), MemberLookup,
/*TemplateArgs=*/nullptr,
/*S=*/nullptr);
if (CtorArg.isInvalid())
return true;

Expand Down Expand Up @@ -14336,8 +14334,10 @@ class MemberBuilder: public ExprBuilder {
public:
Expr *build(Sema &S, SourceLocation Loc) const override {
return assertNotNull(S.BuildMemberReferenceExpr(
Builder.build(S, Loc), Type, Loc, IsArrow, SS, SourceLocation(),
nullptr, MemberLookup, nullptr, nullptr).get());
Builder.build(S, Loc), Type, Loc, IsArrow, SS,
/*TemplateKwLoc=*/SourceLocation(), MemberLookup,
/*TemplateArgs=*/nullptr, /*S=*/nullptr)
.get());
}

MemberBuilder(const ExprBuilder &Builder, QualType Type, bool IsArrow,
Expand Down Expand Up @@ -14543,13 +14543,11 @@ buildSingleCopyAssignRecursively(Sema &S, SourceLocation Loc, QualType T,
Loc);

// Create the reference to operator=.
ExprResult OpEqualRef
= S.BuildMemberReferenceExpr(To.build(S, Loc), T, Loc, /*IsArrow=*/false,
SS, /*TemplateKWLoc=*/SourceLocation(),
/*FirstQualifierInScope=*/nullptr,
OpLookup,
/*TemplateArgs=*/nullptr, /*S*/nullptr,
/*SuppressQualifierCheck=*/true);
ExprResult OpEqualRef = S.BuildMemberReferenceExpr(
To.build(S, Loc), T, Loc, /*IsArrow=*/false, SS,
/*TemplateKWLoc=*/SourceLocation(), OpLookup,
/*TemplateArgs=*/nullptr, /*S*/ nullptr,
/*SuppressQualifierCheck=*/true);
if (OpEqualRef.isInvalid())
return StmtError();

Expand Down Expand Up @@ -17155,8 +17153,9 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message,

auto BuildExpr = [&](LookupResult &LR) {
ExprResult Res = BuildMemberReferenceExpr(
Message, Message->getType(), Message->getBeginLoc(), false,
CXXScopeSpec(), SourceLocation(), nullptr, LR, nullptr, nullptr);
Message, Message->getType(), Message->getBeginLoc(), /*IsArrow=*/false,
/*SS=*/CXXScopeSpec(), /*TemplateKWLoc=*/SourceLocation(), LR,
/*TemplateArgs=*/nullptr, /*S=*/nullptr);
if (Res.isInvalid())
return ExprError();
Res = BuildCallExpr(nullptr, Res.get(), Loc, std::nullopt, Loc, nullptr,
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2624,7 +2624,7 @@ recoverFromMSUnqualifiedLookup(Sema &S, ASTContext &Context,
return CXXDependentScopeMemberExpr::Create(
Context, /*This=*/nullptr, ThisType, /*IsArrow=*/true,
/*Op=*/SourceLocation(), NestedNameSpecifierLoc(), TemplateKWLoc,
/*FirstQualifierFoundInScope=*/nullptr, NameInfo, TemplateArgs);
/*UnqualifiedLookups=*/std::nullopt, NameInfo, TemplateArgs);
}

// Synthesize a fake NNS that points to the derived class. This will
Expand Down
74 changes: 33 additions & 41 deletions clang/lib/Sema/SemaExprMember.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,11 +552,9 @@ static Decl *FindGetterSetterNameDecl(const ObjCObjectPointerType *QIdTy,
}

ExprResult
Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType,
bool IsArrow, SourceLocation OpLoc,
const CXXScopeSpec &SS,
Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType, bool IsArrow,
SourceLocation OpLoc, const CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
NamedDecl *FirstQualifierInScope,
const DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *TemplateArgs) {
// Even in dependent contexts, try to diagnose base expressions with
Expand Down Expand Up @@ -590,8 +588,8 @@ Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType,
// must have pointer type, and the accessed type is the pointee.
return CXXDependentScopeMemberExpr::Create(
Context, BaseExpr, BaseType, IsArrow, OpLoc,
SS.getWithLocInContext(Context), TemplateKWLoc, FirstQualifierInScope,
NameInfo, TemplateArgs);
SS.getWithLocInContext(Context), TemplateKWLoc,
SS.getUnqualifiedLookups(), NameInfo, TemplateArgs);
}

/// We know that the given qualified member reference points only to
Expand Down Expand Up @@ -767,8 +765,9 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
R.addDecl(ND);
R.resolveKind();
return SemaRef.BuildMemberReferenceExpr(
BaseExpr, BaseExpr->getType(), OpLoc, IsArrow, SS, SourceLocation(),
nullptr, R, nullptr, nullptr);
BaseExpr, BaseExpr->getType(), OpLoc, IsArrow, SS,
/*TemplateKWLoc=*/SourceLocation(), R, /*TemplateArgs=*/nullptr,
/*S=*/nullptr);
},
Sema::CTK_ErrorRecovery, DC);

Expand All @@ -784,7 +783,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
ExprResult Sema::BuildMemberReferenceExpr(
Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow,
CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo,
const DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *TemplateArgs, const Scope *S,
ActOnMemberAccessExtraArgs *ExtraArgs) {
LookupResult R(*this, NameInfo, LookupMemberName);
Expand Down Expand Up @@ -828,10 +827,9 @@ ExprResult Sema::BuildMemberReferenceExpr(
if (SS.isInvalid())
return ExprError();

return BuildMemberReferenceExpr(Base, BaseType,
OpLoc, IsArrow, SS, TemplateKWLoc,
FirstQualifierInScope, R, TemplateArgs, S,
false, ExtraArgs);
return BuildMemberReferenceExpr(Base, BaseType, OpLoc, IsArrow, SS,
TemplateKWLoc, R, TemplateArgs, S,
/*SuppressQualifierCheck=*/false, ExtraArgs);
}

ExprResult
Expand Down Expand Up @@ -969,17 +967,11 @@ static bool IsInFnTryBlockHandler(const Scope *S) {
return false;
}

ExprResult
Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
SourceLocation OpLoc, bool IsArrow,
const CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
NamedDecl *FirstQualifierInScope,
LookupResult &R,
const TemplateArgumentListInfo *TemplateArgs,
const Scope *S,
bool SuppressQualifierCheck,
ActOnMemberAccessExtraArgs *ExtraArgs) {
ExprResult Sema::BuildMemberReferenceExpr(
Expr *BaseExpr, QualType BaseExprType, SourceLocation OpLoc, bool IsArrow,
const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, LookupResult &R,
const TemplateArgumentListInfo *TemplateArgs, const Scope *S,
bool SuppressQualifierCheck, ActOnMemberAccessExtraArgs *ExtraArgs) {
assert(!SS.isInvalid() && "nested-name-specifier cannot be invalid");
// If the member wasn't found in the current instantiation, or if the
// arrow operator was used with a dependent non-pointer object expression,
Expand All @@ -989,8 +981,8 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
(SS.isSet() ? SS.getScopeRep()->isDependent()
: BaseExprType->isDependentType())))
return ActOnDependentMemberExpr(BaseExpr, BaseExprType, IsArrow, OpLoc, SS,
TemplateKWLoc, FirstQualifierInScope,
R.getLookupNameInfo(), TemplateArgs);
TemplateKWLoc, R.getLookupNameInfo(),
TemplateArgs);

QualType BaseType = BaseExprType;
if (IsArrow) {
Expand Down Expand Up @@ -1195,9 +1187,9 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,

// Non-dependent member, but dependent template arguments.
if (!VDecl.get())
return ActOnDependentMemberExpr(
BaseExpr, BaseExpr->getType(), IsArrow, OpLoc, SS, TemplateKWLoc,
FirstQualifierInScope, MemberNameInfo, TemplateArgs);
return ActOnDependentMemberExpr(BaseExpr, BaseExpr->getType(), IsArrow,
OpLoc, SS, TemplateKWLoc, MemberNameInfo,
TemplateArgs);

VarDecl *Var = cast<VarDecl>(VDecl.get());
if (!Var->getTemplateSpecializationKind())
Expand Down Expand Up @@ -1763,24 +1755,25 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base,
const TemplateArgumentListInfo *TemplateArgs;
DecomposeUnqualifiedId(Id, TemplateArgsBuffer,
NameInfo, TemplateArgs);

bool IsArrow = (OpKind == tok::arrow);
bool IsArrow = OpKind == tok::arrow;

if (getLangOpts().HLSL && IsArrow)
return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 2);

NamedDecl *FirstQualifierInScope
= (!SS.isSet() ? nullptr : FindFirstQualifierInScope(S, SS.getScopeRep()));

UnresolvedSet<4> UnqualifiedLookups;
if (SS.isValid() &&
LookupFirstQualifierInScope(S, SS.getScopeRep(), UnqualifiedLookups)) {
SS.setUnqualifiedLookups(UnqualifiedLookups.pairs());
}
// This is a postfix expression, so get rid of ParenListExprs.
ExprResult Result = MaybeConvertParenListExprToParenExpr(S, Base);
if (Result.isInvalid()) return ExprError();
Base = Result.get();

ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl};
ExprResult Res = BuildMemberReferenceExpr(
Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc,
FirstQualifierInScope, NameInfo, TemplateArgs, S, &ExtraArgs);
Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, NameInfo,
TemplateArgs, S, &ExtraArgs);

if (!Res.isInvalid() && isa<MemberExpr>(Res.get()))
CheckMemberAccessOfNoDeref(cast<MemberExpr>(Res.get()));
Expand Down Expand Up @@ -1924,9 +1917,8 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS,
baseExpr = BuildCXXThisExpr(loc, ThisTy, /*IsImplicit=*/true);
}

return BuildMemberReferenceExpr(
baseExpr, ThisTy,
/*OpLoc=*/SourceLocation(),
/*IsArrow=*/!getLangOpts().HLSL, SS, TemplateKWLoc,
/*FirstQualifierInScope=*/nullptr, R, TemplateArgs, S);
return BuildMemberReferenceExpr(baseExpr, ThisTy,
/*OpLoc=*/SourceLocation(),
/*IsArrow=*/!getLangOpts().HLSL, SS,
TemplateKWLoc, R, TemplateArgs, S);
}
2 changes: 2 additions & 0 deletions clang/lib/Sema/SemaLambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,8 @@ void Sema::ActOnLambdaClosureParameters(
AddTemplateParametersToLambdaCallOperator(LSI->CallOperator, LSI->Lambda,
TemplateParams);
LSI->Lambda->setLambdaIsGeneric(true);
LSI->ContainsUnexpandedParameterPack |=
TemplateParams->containsUnexpandedParameterPack();
}
LSI->AfterParameterList = true;
}
Expand Down
145 changes: 71 additions & 74 deletions clang/lib/Sema/SemaOpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9044,84 +9044,81 @@ void SemaOpenMP::ActOnOpenMPLoopInitialization(SourceLocation ForLoc,
assert(getLangOpts().OpenMP && "OpenMP is not active.");
assert(Init && "Expected loop in canonical form.");
unsigned AssociatedLoops = DSAStack->getAssociatedLoops();
if (AssociatedLoops > 0 &&
isOpenMPLoopDirective(DSAStack->getCurrentDirective())) {
DSAStack->loopStart();
OpenMPIterationSpaceChecker ISC(SemaRef, /*SupportsNonRectangular=*/true,
*DSAStack, ForLoc);
if (!ISC.checkAndSetInit(Init, /*EmitDiags=*/false)) {
if (ValueDecl *D = ISC.getLoopDecl()) {
auto *VD = dyn_cast<VarDecl>(D);
DeclRefExpr *PrivateRef = nullptr;
if (!VD) {
if (VarDecl *Private = isOpenMPCapturedDecl(D)) {
VD = Private;
} else {
PrivateRef = buildCapture(SemaRef, D, ISC.getLoopDeclRefExpr(),
/*WithInit=*/false);
VD = cast<VarDecl>(PrivateRef->getDecl());
}
}
DSAStack->addLoopControlVariable(D, VD);
const Decl *LD = DSAStack->getPossiblyLoopCounter();
if (LD != D->getCanonicalDecl()) {
DSAStack->resetPossibleLoopCounter();
if (auto *Var = dyn_cast_or_null<VarDecl>(LD))
SemaRef.MarkDeclarationsReferencedInExpr(buildDeclRefExpr(
SemaRef, const_cast<VarDecl *>(Var),
Var->getType().getNonLValueExprType(getASTContext()), ForLoc,
/*RefersToCapture=*/true));
}
OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
// OpenMP [2.14.1.1, Data-sharing Attribute Rules for Variables
// Referenced in a Construct, C/C++]. The loop iteration variable in the
// associated for-loop of a simd construct with just one associated
// for-loop may be listed in a linear clause with a constant-linear-step
// that is the increment of the associated for-loop. The loop iteration
// variable(s) in the associated for-loop(s) of a for or parallel for
// construct may be listed in a private or lastprivate clause.
DSAStackTy::DSAVarData DVar =
DSAStack->getTopDSA(D, /*FromParent=*/false);
// If LoopVarRefExpr is nullptr it means the corresponding loop variable
// is declared in the loop and it is predetermined as a private.
Expr *LoopDeclRefExpr = ISC.getLoopDeclRefExpr();
OpenMPClauseKind PredeterminedCKind =
isOpenMPSimdDirective(DKind)
? (DSAStack->hasMutipleLoops() ? OMPC_lastprivate : OMPC_linear)
: OMPC_private;
if (((isOpenMPSimdDirective(DKind) && DVar.CKind != OMPC_unknown &&
DVar.CKind != PredeterminedCKind && DVar.RefExpr &&
(getLangOpts().OpenMP <= 45 || (DVar.CKind != OMPC_lastprivate &&
DVar.CKind != OMPC_private))) ||
((isOpenMPWorksharingDirective(DKind) || DKind == OMPD_taskloop ||
DKind == OMPD_master_taskloop || DKind == OMPD_masked_taskloop ||
DKind == OMPD_parallel_master_taskloop ||
DKind == OMPD_parallel_masked_taskloop ||
isOpenMPDistributeDirective(DKind)) &&
!isOpenMPSimdDirective(DKind) && DVar.CKind != OMPC_unknown &&
DVar.CKind != OMPC_private && DVar.CKind != OMPC_lastprivate)) &&
(DVar.CKind != OMPC_private || DVar.RefExpr)) {
Diag(Init->getBeginLoc(), diag::err_omp_loop_var_dsa)
<< getOpenMPClauseName(DVar.CKind)
<< getOpenMPDirectiveName(DKind)
<< getOpenMPClauseName(PredeterminedCKind);
if (DVar.RefExpr == nullptr)
DVar.CKind = PredeterminedCKind;
reportOriginalDsa(SemaRef, DSAStack, D, DVar,
/*IsLoopIterVar=*/true);
} else if (LoopDeclRefExpr) {
// Make the loop iteration variable private (for worksharing
// constructs), linear (for simd directives with the only one
// associated loop) or lastprivate (for simd directives with several
// collapsed or ordered loops).
if (DVar.CKind == OMPC_unknown)
DSAStack->addDSA(D, LoopDeclRefExpr, PredeterminedCKind,
PrivateRef);
OpenMPDirectiveKind DKind = DSAStack->getCurrentDirective();
if (AssociatedLoops == 0 || !isOpenMPLoopDirective(DKind))
return;

DSAStack->loopStart();
OpenMPIterationSpaceChecker ISC(SemaRef, /*SupportsNonRectangular=*/true,
*DSAStack, ForLoc);
if (!ISC.checkAndSetInit(Init, /*EmitDiags=*/false)) {
if (ValueDecl *D = ISC.getLoopDecl()) {
auto *VD = dyn_cast<VarDecl>(D);
DeclRefExpr *PrivateRef = nullptr;
if (!VD) {
if (VarDecl *Private = isOpenMPCapturedDecl(D)) {
VD = Private;
} else {
PrivateRef = buildCapture(SemaRef, D, ISC.getLoopDeclRefExpr(),
/*WithInit=*/false);
VD = cast<VarDecl>(PrivateRef->getDecl());
}
}
DSAStack->addLoopControlVariable(D, VD);
const Decl *LD = DSAStack->getPossiblyLoopCounter();
if (LD != D->getCanonicalDecl()) {
DSAStack->resetPossibleLoopCounter();
if (auto *Var = dyn_cast_or_null<VarDecl>(LD))
SemaRef.MarkDeclarationsReferencedInExpr(buildDeclRefExpr(
SemaRef, const_cast<VarDecl *>(Var),
Var->getType().getNonLValueExprType(getASTContext()), ForLoc,
/*RefersToCapture=*/true));
}
// OpenMP [2.14.1.1, Data-sharing Attribute Rules for Variables
// Referenced in a Construct, C/C++]. The loop iteration variable in the
// associated for-loop of a simd construct with just one associated
// for-loop may be listed in a linear clause with a constant-linear-step
// that is the increment of the associated for-loop. The loop iteration
// variable(s) in the associated for-loop(s) of a for or parallel for
// construct may be listed in a private or lastprivate clause.
DSAStackTy::DSAVarData DVar =
DSAStack->getTopDSA(D, /*FromParent=*/false);
// If LoopVarRefExpr is nullptr it means the corresponding loop variable
// is declared in the loop and it is predetermined as a private.
Expr *LoopDeclRefExpr = ISC.getLoopDeclRefExpr();
OpenMPClauseKind PredeterminedCKind =
isOpenMPSimdDirective(DKind)
? (DSAStack->hasMutipleLoops() ? OMPC_lastprivate : OMPC_linear)
: OMPC_private;
if (((isOpenMPSimdDirective(DKind) && DVar.CKind != OMPC_unknown &&
DVar.CKind != PredeterminedCKind && DVar.RefExpr &&
(getLangOpts().OpenMP <= 45 ||
(DVar.CKind != OMPC_lastprivate && DVar.CKind != OMPC_private))) ||
((isOpenMPWorksharingDirective(DKind) || DKind == OMPD_taskloop ||
DKind == OMPD_master_taskloop || DKind == OMPD_masked_taskloop ||
DKind == OMPD_parallel_master_taskloop ||
DKind == OMPD_parallel_masked_taskloop ||
isOpenMPDistributeDirective(DKind)) &&
!isOpenMPSimdDirective(DKind) && DVar.CKind != OMPC_unknown &&
DVar.CKind != OMPC_private && DVar.CKind != OMPC_lastprivate)) &&
(DVar.CKind != OMPC_private || DVar.RefExpr)) {
Diag(Init->getBeginLoc(), diag::err_omp_loop_var_dsa)
<< getOpenMPClauseName(DVar.CKind) << getOpenMPDirectiveName(DKind)
<< getOpenMPClauseName(PredeterminedCKind);
if (DVar.RefExpr == nullptr)
DVar.CKind = PredeterminedCKind;
reportOriginalDsa(SemaRef, DSAStack, D, DVar, /*IsLoopIterVar=*/true);
} else if (LoopDeclRefExpr) {
// Make the loop iteration variable private (for worksharing
// constructs), linear (for simd directives with the only one
// associated loop) or lastprivate (for simd directives with several
// collapsed or ordered loops).
if (DVar.CKind == OMPC_unknown)
DSAStack->addDSA(D, LoopDeclRefExpr, PredeterminedCKind, PrivateRef);
}
}
DSAStack->setAssociatedLoops(AssociatedLoops - 1);
}
DSAStack->setAssociatedLoops(AssociatedLoops - 1);
}

namespace {
Expand Down
12 changes: 5 additions & 7 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16043,13 +16043,11 @@ Sema::BuildForRangeBeginEndCall(SourceLocation Loc,

CandidateSet->clear(OverloadCandidateSet::CSK_Normal);
if (!MemberLookup.empty()) {
ExprResult MemberRef =
BuildMemberReferenceExpr(Range, Range->getType(), Loc,
/*IsPtr=*/false, CXXScopeSpec(),
/*TemplateKWLoc=*/SourceLocation(),
/*FirstQualifierInScope=*/nullptr,
MemberLookup,
/*TemplateArgs=*/nullptr, S);
ExprResult MemberRef = BuildMemberReferenceExpr(
Range, Range->getType(), Loc,
/*IsPtr=*/false, /*SS=*/CXXScopeSpec(),
/*TemplateKWLoc=*/SourceLocation(), MemberLookup,
/*TemplateArgs=*/nullptr, S);
if (MemberRef.isInvalid()) {
*CallExpr = ExprError();
return FRS_DiagnosticIssued;
Expand Down
8 changes: 5 additions & 3 deletions clang/lib/Sema/SemaStmtAsm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,8 @@ Sema::LookupInlineAsmVarDeclField(Expr *E, StringRef Member,
return CXXDependentScopeMemberExpr::Create(
Context, E, T, /*IsArrow=*/false, AsmLoc, NestedNameSpecifierLoc(),
SourceLocation(),
/*FirstQualifierFoundInScope=*/nullptr, NameInfo, /*TemplateArgs=*/nullptr);
/*UnqualifiedLookups=*/std::nullopt, NameInfo,
/*TemplateArgs=*/nullptr);
}

const RecordType *RT = T->getAs<RecordType>();
Expand All @@ -923,8 +924,9 @@ Sema::LookupInlineAsmVarDeclField(Expr *E, StringRef Member,

// Make an Expr to thread through OpDecl.
ExprResult Result = BuildMemberReferenceExpr(
E, E->getType(), AsmLoc, /*IsArrow=*/false, CXXScopeSpec(),
SourceLocation(), nullptr, FieldResult, nullptr, nullptr);
E, E->getType(), AsmLoc, /*IsArrow=*/false, /*SS=*/CXXScopeSpec(),
/*TemplateKWLoc*/ SourceLocation(), FieldResult,
/*TemplateArgs=*/nullptr, /*S=*/nullptr);

return Result;
}
Expand Down
36 changes: 36 additions & 0 deletions clang/lib/Sema/SemaStmtAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/Sema/DelayedDiagnostic.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/ParsedAttr.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaInternal.h"
#include "llvm/ADT/StringExtras.h"
Expand Down Expand Up @@ -584,6 +585,39 @@ static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A,
return ::new (S.Context) OpenCLUnrollHintAttr(S.Context, A, UnrollFactor);
}

static Attr *handleHLSLLoopHintAttr(Sema &S, Stmt *St, const ParsedAttr &A,
SourceRange Range) {

if (A.getSemanticSpelling() == HLSLLoopHintAttr::Spelling::Microsoft_loop &&
!A.checkAtMostNumArgs(S, 0))
return nullptr;

unsigned UnrollFactor = 0;
if (A.getNumArgs() == 1) {

if (A.isArgIdent(0)) {
S.Diag(A.getLoc(), diag::err_attribute_argument_type)
<< A << AANT_ArgumentIntegerConstant << A.getRange();
return nullptr;
}

Expr *E = A.getArgAsExpr(0);

if (S.CheckLoopHintExpr(E, St->getBeginLoc(),
/*AllowZero=*/false))
return nullptr;

std::optional<llvm::APSInt> ArgVal = E->getIntegerConstantExpr(S.Context);
// CheckLoopHintExpr handles non int const cases
assert(ArgVal != std::nullopt && "ArgVal should be an integer constant.");
int Val = ArgVal->getSExtValue();
// CheckLoopHintExpr handles negative and zero cases
assert(Val > 0 && "Val should be a positive integer greater than zero.");
UnrollFactor = static_cast<unsigned>(Val);
}
return ::new (S.Context) HLSLLoopHintAttr(S.Context, A, UnrollFactor);
}

static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
SourceRange Range) {
if (A.isInvalid() || A.getKind() == ParsedAttr::IgnoredAttribute)
Expand Down Expand Up @@ -618,6 +652,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
return handleFallThroughAttr(S, St, A, Range);
case ParsedAttr::AT_LoopHint:
return handleLoopHintAttr(S, St, A, Range);
case ParsedAttr::AT_HLSLLoopHint:
return handleHLSLLoopHintAttr(S, St, A, Range);
case ParsedAttr::AT_OpenCLUnrollHint:
return handleOpenCLUnrollHint(S, St, A, Range);
case ParsedAttr::AT_Suppress:
Expand Down
225 changes: 112 additions & 113 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,12 @@ bool Sema::hasAnyAcceptableTemplateNames(LookupResult &R,
return false;
}

TemplateNameKind Sema::isTemplateName(Scope *S,
CXXScopeSpec &SS,
bool hasTemplateKeyword,
const UnqualifiedId &Name,
ParsedType ObjectTypePtr,
bool EnteringContext,
TemplateTy &TemplateResult,
bool &MemberOfUnknownSpecialization,
bool Disambiguation) {
TemplateNameKind
Sema::isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword,
const UnqualifiedId &Name, ParsedType ObjectTypePtr,
bool EnteringContext, TemplateTy &TemplateResult,
bool &MemberOfUnknownSpecialization, bool Disambiguation,
bool MayBeNNS) {
assert(getLangOpts().CPlusPlus && "No template names in C!");

DeclarationName TName;
Expand Down Expand Up @@ -213,8 +210,9 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
if (LookupTemplateName(R, S, SS, ObjectType, EnteringContext,
/*RequiredTemplate=*/SourceLocation(),
&AssumedTemplate,
/*AllowTypoCorrection=*/!Disambiguation))
/*AllowTypoCorrection=*/!Disambiguation, MayBeNNS))
return TNK_Non_template;

MemberOfUnknownSpecialization = R.wasNotFoundInCurrentInstantiation();

if (AssumedTemplate != AssumedTemplateKind::None) {
Expand Down Expand Up @@ -380,7 +378,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
QualType ObjectType, bool EnteringContext,
RequiredTemplateKind RequiredTemplate,
AssumedTemplateKind *ATK,
bool AllowTypoCorrection) {
bool AllowTypoCorrection, bool MayBeNNS) {
if (ATK)
*ATK = AssumedTemplateKind::None;

Expand All @@ -389,92 +387,89 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,

Found.setTemplateNameLookup(true);

// Determine where to perform name lookup
DeclContext *LookupCtx = nullptr;
bool IsDependent = false;
if (!ObjectType.isNull()) {
// This nested-name-specifier occurs in a member access expression, e.g.,
// x->B::f, and we are looking into the type of the object.
assert(SS.isEmpty() && "ObjectType and scope specifier cannot coexist");
LookupCtx = computeDeclContext(ObjectType);
IsDependent = !LookupCtx && ObjectType->isDependentType();
assert((IsDependent || !ObjectType->isIncompleteType() ||
!ObjectType->getAs<TagType>() ||
ObjectType->castAs<TagType>()->isBeingDefined()) &&
"Caller should have completed object type");

// Template names cannot appear inside an Objective-C class or object type
// or a vector type.
//
// FIXME: This is wrong. For example:
//
// template<typename T> using Vec = T __attribute__((ext_vector_type(4)));
// Vec<int> vi;
// vi.Vec<int>::~Vec<int>();
//
// ... should be accepted but we will not treat 'Vec' as a template name
// here. The right thing to do would be to check if the name is a valid
// vector component name, and look up a template name if not. And similarly
// for lookups into Objective-C class and object types, where the same
// problem can arise.
if (ObjectType->isObjCObjectOrInterfaceType() ||
ObjectType->isVectorType()) {
Found.clear();
return false;
}
} else if (SS.isNotEmpty()) {
// This nested-name-specifier occurs after another nested-name-specifier,
// so long into the context associated with the prior nested-name-specifier.
LookupCtx = computeDeclContext(SS, EnteringContext);
IsDependent = !LookupCtx && isDependentScopeSpecifier(SS);

// The declaration context must be complete.
if (LookupCtx && RequireCompleteDeclContext(SS, LookupCtx))
return true;
// Template names cannot appear inside an Objective-C class or object type
// or a vector type.
//
// FIXME: This is wrong. For example:
//
// template<typename T> using Vec = T __attribute__((ext_vector_type(4)));
// Vec<int> vi;
// vi.Vec<int>::~Vec<int>();
//
// ... should be accepted but we will not treat 'Vec' as a template name
// here. The right thing to do would be to check if the name is a valid
// vector component name, and look up a template name if not. And similarly
// for lookups into Objective-C class and object types, where the same
// problem can arise.
if (!ObjectType.isNull() && (ObjectType->isVectorType() ||
ObjectType->isObjCObjectOrInterfaceType())) {
Found.clear();
return false;
}

LookupParsedName(Found, S, &SS, ObjectType,
/*AllowBuiltinCreation=*/false, EnteringContext);

// C++ [basic.lookup.qual.general]p3:
// [...] Unless otherwise specified, a qualified name undergoes qualified
// name lookup in its lookup context from the point where it appears unless
// the lookup context either is dependent and is not the current
// instantiation or is not a class or class template.
//
// The lookup context is dependent and either:
// - it is not the current instantiation, or
// - it is the current instantiation, it has at least one dependent base
// class, and qualified lookup found nothing.
//
// If this is a member-qualified name that is the terminal name of a
// nested-name-specifier, we perform unqualified lookup and store the results
// so we can replicate the lookup during instantiation. The results of the
// unqualified loookup are *not* used to determine whether '<' is interpreted
// as the delimiter of a template-argument-list.
//
// For example:
//
// template<typename T>
// struct A {
// int x;
// };
//
// template<typename T>
// using B = A<T>;
//
// template<typename T>
// void f(A<T> a, A<int> b) {
// a.B<T>::x; // error: missing 'template' before 'B'
// b.B<int>::x; // ok, lookup context is not dependent
// }
if (Found.wasNotFoundInCurrentInstantiation())
return false;

bool ObjectTypeSearchedInScope = false;
bool AllowFunctionTemplatesInLookup = true;
if (LookupCtx) {
// Perform "qualified" name lookup into the declaration context we
// computed, which is either the type of the base of a member access
// expression or the declaration context associated with a prior
// nested-name-specifier.
LookupQualifiedName(Found, LookupCtx);

// FIXME: The C++ standard does not clearly specify what happens in the
// case where the object type is dependent, and implementations vary. In
// Clang, we treat a name after a . or -> as a template-name if lookup
// finds a non-dependent member or member of the current instantiation that
// is a type template, or finds no such members and lookup in the context
// of the postfix-expression finds a type template. In the latter case, the
// name is nonetheless dependent, and we may resolve it to a member of an
// unknown specialization when we come to instantiate the template.
IsDependent |= Found.wasNotFoundInCurrentInstantiation();
}

if (SS.isEmpty() && (ObjectType.isNull() || Found.empty())) {
// C++ [basic.lookup.classref]p1:
// In a class member access expression (5.2.5), if the . or -> token is
// immediately followed by an identifier followed by a <, the
// identifier must be looked up to determine whether the < is the
// beginning of a template argument list (14.2) or a less-than operator.
// The identifier is first looked up in the class of the object
// expression. If the identifier is not found, it is then looked up in
// the context of the entire postfix-expression and shall name a class
// template.
if (S)
LookupName(Found, S);

if (!ObjectType.isNull()) {
// FIXME: We should filter out all non-type templates here, particularly
// variable templates and concepts. But the exclusion of alias templates
// and template template parameters is a wording defect.
AllowFunctionTemplatesInLookup = false;
ObjectTypeSearchedInScope = true;
// C++ [basic.lookup.qual.general]p2:
// A member-qualified name is the (unique) component name, if any, of
// - an unqualified-id or
// - a nested-name-specifier of the form type-name :: or namespace-name ::
// in the id-expression of a class member access expression.
//
// C++ [basic.lookup.qual.general]p3:
// [...] If nothing is found by qualified lookup for a member-qualified
// name that is the terminal name of a nested-name-specifier and is not
// dependent, it undergoes unqualified lookup.
//
// In 'x.A::B::y', 'A' will undergo unqualified lookup if qualified lookup
// in the type of 'x' finds nothing. If the lookup context is dependent,
// we perform the unqualified lookup in the template definition context
// and store the results so we can replicate the lookup during instantiation.
if (MayBeNNS && Found.empty() && !ObjectType.isNull()) {
if (S) {
LookupName(Found, S);
} else if (!SS.getUnqualifiedLookups().empty()) {
Found.addAllDecls(SS.getUnqualifiedLookups());
Found.resolveKind();
}

IsDependent |= Found.wasNotFoundInCurrentInstantiation();
ObjectTypeSearchedInScope = true;
}

if (Found.isAmbiguous())
Expand All @@ -494,7 +489,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
getLangOpts().CPlusPlus20 && llvm::all_of(Found, [](NamedDecl *ND) {
return isa<FunctionDecl>(ND->getUnderlyingDecl());
});
if (AllFunctions || (Found.empty() && !IsDependent)) {
if (AllFunctions || Found.empty()) {
// If lookup found any functions, or if this is a name that can only be
// used for a function, then strongly assume this is a function
// template-id.
Expand All @@ -506,11 +501,15 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
}
}

if (Found.empty() && !IsDependent && AllowTypoCorrection) {
if (Found.empty() && AllowTypoCorrection) {
// If we did not find any names, and this is not a disambiguation, attempt
// to correct any typos.
DeclarationName Name = Found.getLookupName();
Found.clear();
DeclContext *LookupCtx =
SS.isSet()
? computeDeclContext(SS, EnteringContext)
: (!ObjectType.isNull() ? computeDeclContext(ObjectType) : nullptr);
// Simple filter callback that, for keywords, only accepts the C++ *_cast
DefaultFilterCCC FilterCCC{};
FilterCCC.WantTypeSpecifiers = false;
Expand Down Expand Up @@ -543,13 +542,8 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,

NamedDecl *ExampleLookupResult =
Found.empty() ? nullptr : Found.getRepresentativeDecl();
FilterAcceptableTemplateNames(Found, AllowFunctionTemplatesInLookup);
FilterAcceptableTemplateNames(Found);
if (Found.empty()) {
if (IsDependent) {
Found.setNotFoundInCurrentInstantiation();
return false;
}

// If a 'template' keyword was used, a lookup that finds only non-template
// names is an error.
if (ExampleLookupResult && RequiredTemplate) {
Expand Down Expand Up @@ -741,7 +735,7 @@ Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS,
/*IsArrow=*/!Context.getLangOpts().HLSL,
/*OperatorLoc=*/SourceLocation(),
/*QualifierLoc=*/NestedNameSpecifierLoc(), TemplateKWLoc,
/*FirstQualifierFoundInScope=*/nullptr, NameInfo, TemplateArgs);
/*UnqualifiedLookups=*/std::nullopt, NameInfo, TemplateArgs);
}
return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs);
}
Expand Down Expand Up @@ -5855,14 +5849,10 @@ ExprResult Sema::BuildQualifiedTemplateIdExpr(
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*ADL=*/false, TemplateArgs);
}

TemplateNameKind Sema::ActOnTemplateName(Scope *S,
CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
const UnqualifiedId &Name,
ParsedType ObjectType,
bool EnteringContext,
TemplateTy &Result,
bool AllowInjectedClassName) {
TemplateNameKind Sema::ActOnTemplateName(
Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
const UnqualifiedId &Name, ParsedType ObjectType, bool EnteringContext,
TemplateTy &Result, bool AllowInjectedClassName, bool MayBeNNS) {
if (TemplateKWLoc.isValid() && S && !S->getTemplateParamParent())
Diag(TemplateKWLoc,
getLangOpts().CPlusPlus11 ?
Expand Down Expand Up @@ -5897,9 +5887,10 @@ TemplateNameKind Sema::ActOnTemplateName(Scope *S,
// "template" keyword is now permitted). We follow the C++0x
// rules, even in C++03 mode with a warning, retroactively applying the DR.
bool MemberOfUnknownSpecialization;
TemplateNameKind TNK = isTemplateName(S, SS, TemplateKWLoc.isValid(), Name,
ObjectType, EnteringContext, Result,
MemberOfUnknownSpecialization);
TemplateNameKind TNK =
isTemplateName(S, SS, TemplateKWLoc.isValid(), Name, ObjectType,
EnteringContext, Result, MemberOfUnknownSpecialization,
/*Disambiguation=*/false, MayBeNNS);
if (TNK != TNK_Non_template) {
// We resolved this to a (non-dependent) template name. Return it.
auto *LookupRD = dyn_cast_or_null<CXXRecordDecl>(LookupCtx);
Expand Down Expand Up @@ -5938,7 +5929,8 @@ TemplateNameKind Sema::ActOnTemplateName(Scope *S,
? RequiredTemplateKind(TemplateKWLoc)
: TemplateNameIsRequired;
if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext, RTK,
/*ATK=*/nullptr, /*AllowTypoCorrection=*/false) &&
/*ATK=*/nullptr, /*AllowTypoCorrection=*/false,
MayBeNNS) &&
!R.isAmbiguous()) {
if (LookupCtx)
Diag(Name.getBeginLoc(), diag::err_no_member)
Expand Down Expand Up @@ -11698,6 +11690,13 @@ Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc,
// Construct a dependent template specialization type.
assert(DTN && "dependent template has non-dependent name?");
assert(DTN->getQualifier() == SS.getScopeRep());

if (!DTN->isIdentifier()) {
Diag(TemplateIILoc, diag::err_template_id_not_a_type) << Template;
NoteAllFoundTemplates(Template);
return true;
}

QualType T = Context.getDependentTemplateSpecializationType(
ElaboratedTypeKeyword::Typename, DTN->getQualifier(),
DTN->getIdentifier(), TemplateArgs.arguments());
Expand Down
17 changes: 7 additions & 10 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1515,12 +1515,11 @@ namespace {
NestedNameSpecifierLoc QualifierLoc,
QualType T);

TemplateName
TransformTemplateName(CXXScopeSpec &SS, TemplateName Name,
SourceLocation NameLoc,
QualType ObjectType = QualType(),
NamedDecl *FirstQualifierInScope = nullptr,
bool AllowInjectedClassName = false);
TemplateName TransformTemplateName(CXXScopeSpec &SS, TemplateName Name,
SourceLocation NameLoc,
QualType ObjectType = QualType(),
bool AllowInjectedClassName = false,
bool MayBeNNS = false);

const CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA);
const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH);
Expand Down Expand Up @@ -1952,8 +1951,7 @@ TemplateInstantiator::RebuildElaboratedType(SourceLocation KeywordLoc,

TemplateName TemplateInstantiator::TransformTemplateName(
CXXScopeSpec &SS, TemplateName Name, SourceLocation NameLoc,
QualType ObjectType, NamedDecl *FirstQualifierInScope,
bool AllowInjectedClassName) {
QualType ObjectType, bool AllowInjectedClassName, bool MayBeNNS) {
if (TemplateTemplateParmDecl *TTP
= dyn_cast_or_null<TemplateTemplateParmDecl>(Name.getAsTemplateDecl())) {
if (TTP->getDepth() < TemplateArgs.getNumLevels()) {
Expand Down Expand Up @@ -2025,8 +2023,7 @@ TemplateName TemplateInstantiator::TransformTemplateName(
}

return inherited::TransformTemplateName(SS, Name, NameLoc, ObjectType,
FirstQualifierInScope,
AllowInjectedClassName);
AllowInjectedClassName, MayBeNNS);
}

ExprResult
Expand Down
329 changes: 131 additions & 198 deletions clang/lib/Sema/TreeTransform.h

Large diffs are not rendered by default.

63 changes: 32 additions & 31 deletions clang/lib/Serialization/ASTReaderStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1993,42 +1993,43 @@ void ASTStmtReader::VisitCXXDependentScopeMemberExpr(
CXXDependentScopeMemberExpr *E) {
VisitExpr(E);

unsigned NumTemplateArgs = Record.readInt();
CurrentUnpackingBits.emplace(Record.readInt());
bool HasTemplateKWAndArgsInfo = CurrentUnpackingBits->getNextBit();
bool HasFirstQualifierFoundInScope = CurrentUnpackingBits->getNextBit();

assert((HasTemplateKWAndArgsInfo == E->hasTemplateKWAndArgsInfo()) &&
"Wrong HasTemplateKWAndArgsInfo!");
assert(
(HasFirstQualifierFoundInScope == E->hasFirstQualifierFoundInScope()) &&
"Wrong HasFirstQualifierFoundInScope!");

if (HasTemplateKWAndArgsInfo)
ReadTemplateKWAndArgsInfo(
*E->getTrailingObjects<ASTTemplateKWAndArgsInfo>(),
E->getTrailingObjects<TemplateArgumentLoc>(), NumTemplateArgs);

assert((NumTemplateArgs == E->getNumTemplateArgs()) &&
"Wrong NumTemplateArgs!");
bool HasQualifier = CurrentUnpackingBits->getNextBit();
bool HasTemplateInfo = CurrentUnpackingBits->getNextBit();
unsigned NumUnqualifiedLookups = Record.readInt();
unsigned NumTemplateArgs = Record.readInt();
E->CXXDependentScopeMemberExprBits.HasQualifier = HasQualifier;
E->CXXDependentScopeMemberExprBits.NumUnqualifiedLookups =
NumUnqualifiedLookups;
E->CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo = HasTemplateInfo;

E->BaseType = Record.readType();
E->CXXDependentScopeMemberExprBits.IsArrow =
CurrentUnpackingBits->getNextBit();

E->BaseType = Record.readType();
E->QualifierLoc = Record.readNestedNameSpecifierLoc();
// not ImplicitAccess
if (CurrentUnpackingBits->getNextBit())
E->Base = Record.readSubExpr();
else
E->Base = nullptr;

E->CXXDependentScopeMemberExprBits.OperatorLoc = readSourceLocation();
E->OperatorLoc = Record.readSourceLocation();
E->MemberNameInfo = Record.readDeclarationNameInfo();

if (HasFirstQualifierFoundInScope)
*E->getTrailingObjects<NamedDecl *>() = readDeclAs<NamedDecl>();
if (HasQualifier)
new (E->getTrailingObjects<NestedNameSpecifierLoc>())
NestedNameSpecifierLoc(Record.readNestedNameSpecifierLoc());

E->MemberNameInfo = Record.readDeclarationNameInfo();
for (unsigned I = 0; I != NumUnqualifiedLookups; ++I) {
auto *FoundD = Record.readDeclAs<NamedDecl>();
auto AS = (AccessSpecifier)Record.readInt();
E->getTrailingObjects<DeclAccessPair>()[I] =
DeclAccessPair::make(FoundD, AS);
}

if (HasTemplateInfo)
ReadTemplateKWAndArgsInfo(
*E->getTrailingObjects<ASTTemplateKWAndArgsInfo>(),
E->getTrailingObjects<TemplateArgumentLoc>(), NumTemplateArgs);
}

void
Expand Down Expand Up @@ -4075,16 +4076,16 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
break;

case EXPR_CXX_DEPENDENT_SCOPE_MEMBER: {
unsigned NumTemplateArgs = Record[ASTStmtReader::NumExprFields];
BitsUnpacker DependentScopeMemberBits(
Record[ASTStmtReader::NumExprFields + 1]);
bool HasTemplateKWAndArgsInfo = DependentScopeMemberBits.getNextBit();
Record[ASTStmtReader::NumExprFields]);
bool HasQualifier = DependentScopeMemberBits.getNextBit();
bool HasTemplateInfo = DependentScopeMemberBits.getNextBit();
unsigned NumUnqualifiedLookups = Record[ASTStmtReader::NumExprFields + 1];
unsigned NumTemplateArgs = Record[ASTStmtReader::NumExprFields + 2];

bool HasFirstQualifierFoundInScope =
DependentScopeMemberBits.getNextBit();
S = CXXDependentScopeMemberExpr::CreateEmpty(
Context, HasTemplateKWAndArgsInfo, NumTemplateArgs,
HasFirstQualifierFoundInScope);
Context, HasQualifier, NumUnqualifiedLookups, HasTemplateInfo,
NumTemplateArgs);
break;
}

Expand Down
43 changes: 25 additions & 18 deletions clang/lib/Serialization/ASTWriterStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1988,34 +1988,41 @@ void ASTStmtWriter::VisitCXXDependentScopeMemberExpr(
CXXDependentScopeMemberExpr *E) {
VisitExpr(E);

// Don't emit anything here (or if you do you will have to update
// the corresponding deserialization function).
Record.push_back(E->getNumTemplateArgs());
CurrentPackingBits.updateBits();
CurrentPackingBits.addBit(E->hasTemplateKWAndArgsInfo());
CurrentPackingBits.addBit(E->hasFirstQualifierFoundInScope());

if (E->hasTemplateKWAndArgsInfo()) {
const ASTTemplateKWAndArgsInfo &ArgInfo =
*E->getTrailingObjects<ASTTemplateKWAndArgsInfo>();
AddTemplateKWAndArgsInfo(ArgInfo,
E->getTrailingObjects<TemplateArgumentLoc>());
}
bool HasQualifier = E->hasQualifier();
unsigned NumUnqualifiedLookups = E->getNumUnqualifiedLookups();
bool HasTemplateInfo = E->hasTemplateKWAndArgsInfo();
unsigned NumTemplateArgs = E->getNumTemplateArgs();

CurrentPackingBits.addBit(E->isArrow());
// Write these first for easy access when deserializing, as they affect the
// size of the CXXDependentScopeMemberExpr.
CurrentPackingBits.updateBits();
CurrentPackingBits.addBit(HasQualifier);
CurrentPackingBits.addBit(HasTemplateInfo);
Record.push_back(NumUnqualifiedLookups);
Record.push_back(NumTemplateArgs);

Record.AddTypeRef(E->getBaseType());
Record.AddNestedNameSpecifierLoc(E->getQualifierLoc());
CurrentPackingBits.addBit(E->isArrow());
CurrentPackingBits.addBit(!E->isImplicitAccess());
if (!E->isImplicitAccess())
Record.AddStmt(E->getBase());

Record.AddSourceLocation(E->getOperatorLoc());

if (E->hasFirstQualifierFoundInScope())
Record.AddDeclRef(E->getFirstQualifierFoundInScope());

Record.AddDeclarationNameInfo(E->MemberNameInfo);

if (HasQualifier)
Record.AddNestedNameSpecifierLoc(E->getQualifierLoc());

for (DeclAccessPair D : E->unqualified_lookups()) {
Record.AddDeclRef(D.getDecl());
Record.push_back(D.getAccess());
}

if (HasTemplateInfo)
AddTemplateKWAndArgsInfo(*E->getTrailingObjects<ASTTemplateKWAndArgsInfo>(),
E->getTrailingObjects<TemplateArgumentLoc>());

Code = serialization::EXPR_CXX_DEPENDENT_SCOPE_MEMBER;
}

Expand Down
83 changes: 83 additions & 0 deletions clang/test/C/C2y/n3244.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// RUN: %clang_cc1 -std=c2y %s -verify -Wno-gnu-alignof-expression

/* WG14 N3244: Partial
* Slay Some Earthly Demons I
*
* NB: the committee adopted:
* Annex J Item 21 (including additional change) -- no, we lack explicit documentation
* Annex J Item 56 -- yes
* Annex J Item 57 Option 1 -- yes
* Annex J Item 67 -- no
* Annex J Item 69 (alternative wording for semantics) -- no
*/

void reg_array(void) {
// Decay of an array with the register storage class specifier has gone from
// explicit undefined behavior to be implementation defined instead. Clang
// does not support this.
register int array[10];
(void)sizeof(array); // okay
int *vp = array; // expected-error {{address of register variable requested}}
int val = array[0]; // expected-error {{address of register variable requested}}
}

struct F; // expected-note {{forward declaration of 'struct F'}}
void incomplete_no_linkage(struct F); // okay
void incomplete_no_linkage(struct F f) { // expected-error {{variable has incomplete type 'struct F'}}
struct G g; // expected-error {{variable has incomplete type 'struct G'}} \
expected-note {{forward declaration of 'struct G'}}
int i[]; // expected-error {{definition of variable with array type needs an explicit size or an initializer}}
}

void block_scope_non_extern_func_decl(void) {
static void f(void); // expected-error {{function declared in block scope cannot have 'static' storage class}}
extern void g(void); // okay
__private_extern__ void h(void); // okay
}

// FIXME: this function should be diagnosed as it is never defined in the TU.
extern inline void never_defined_extern_inline(void);

// While this declaration is fine because the function is defined within the TU.
extern inline void is_defined_extern_inline(void);
extern inline void is_defined_extern_inline(void) {}

int NoAlignmentOnOriginalDecl;
// FIXME: the original declaration has no alignment specifier, so the
// declaration below should be diagnosed due to the incompatible alignment
// specifier.
_Alignas(8) int NoAlignmentOnOriginalDecl;
_Static_assert(_Alignof(NoAlignmentOnOriginalDecl) == 8, "");

_Alignas(8) int AlignmentOnOriginalDecl; // expected-note {{declared with '_Alignas' attribute here}}
// FIXME: this should be accepted because the redeclaration has no alignment
// specifier.
int AlignmentOnOriginalDecl; // expected-error {{'_Alignas' must be specified on definition if it is specified on any declaration}}
_Static_assert(_Alignof(AlignmentOnOriginalDecl) == 8, "");

long long CompatibleAlignment;
_Static_assert(_Alignof(CompatibleAlignment) == _Alignof(long long), "");
_Alignas(_Alignof(long long)) long long CompatibleAlignment; // Okay, alignment is the same as the implied alignment

_Alignas(_Alignof(long long)) long long CompatibleAlignment2; // expected-note {{declared with '_Alignas' attribute here}}
// FIXME: this should be accepted because the redeclaration has no alignment
// specifier.
long long CompatibleAlignment2; // expected-error {{'_Alignas' must be specified on definition if it is specified on any declaration}}

// FIXME: this should be accepted because the definition specifies the
// alignment and a subsequent declaration does not specify any alignment.
_Alignas(8) long long DefnWithInit = 12; // expected-note {{declared with '_Alignas' attribute here}}
long long DefnWithInit; // expected-error {{'_Alignas' must be specified on definition if it is specified on any declaration}}

// This is accepted because the definition has an alignment specifier and the
// subsequent redeclaration does not specify an alignment.
_Alignas(8) long long DefnWithInit2 = 12;
extern long long DefnWithInit2;

// FIXME: this should be accepted because the definition specifies the
// alignment and a subsequent declaration specifies a compatible alignment.
long long DefnWithInit3 = 12; // expected-error {{'_Alignas' must be specified on definition if it is specified on any declaration}}
_Alignas(_Alignof(long long)) long long DefnWithInit3; // expected-note {{declared with '_Alignas' attribute here}}

_Alignas(8) int Mismatch; // expected-note {{previous declaration is here}}
_Alignas(16) int Mismatch; // expected-error {{redeclaration has different alignment requirement (16 vs 8)}}
20 changes: 20 additions & 0 deletions clang/test/C/C2y/n3262.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: %clang_cc1 -verify -std=c2y -Wall -pedantic %s
// expected-no-diagnostics

/* WG14 N3262: Yes
* Usability of a byte-wise copy of va_list
*
* NB: Clang explicitly documents this as being undefined behavior. A
* diagnostic is produced for some targets but not for others for assignment or
* initialization, but no diagnostic is possible to produce for use with memcpy
* in the general case, nor with a manual bytewise copy via a for loop.
*
* Therefore, nothing is tested in this file; it serves as a reminder that we
* validated our documentation against the paper. See
* clang/docs/LanguageExtensions.rst for more details.
*
* FIXME: it would be nice to add ubsan support for recognizing when an invalid
* copy is made and diagnosing on copy (or on use of the copied va_list).
*/

int main() {}
3 changes: 3 additions & 0 deletions clang/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ llvm_canonicalize_cmake_booleans(
CLANG_SPAWN_CC1
CLANG_ENABLE_CIR
ENABLE_BACKTRACES
LLVM_BUILD_EXAMPLES
LLVM_BYE_LINK_INTO_TOOLS
LLVM_ENABLE_PLUGINS
LLVM_ENABLE_ZLIB
LLVM_ENABLE_ZSTD
LLVM_ENABLE_PER_TARGET_RUNTIME_DIR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,19 @@ namespace PR11856 {

template<typename T> T *end(T*);

class X { };
struct X { };
struct Y {
int end;
};
template <typename T>
void Foo2() {
T it1;
if (it1->end < it1->end) {
}
if (it1->end < it1->end) { }

X *x;
if (x->end < 7) { // expected-error{{no member named 'end' in 'PR11856::X'}}
}
if (x->end < 7) { } // expected-error{{no member named 'end' in 'PR11856::X'}}

Y *y;
if (y->end < 7) { }
}
}
14 changes: 9 additions & 5 deletions clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,19 @@ namespace PR11856 {

template<typename T> T *end(T*);

class X { };
struct X { };
struct Y {
int end;
};
template <typename T>
void Foo2() {
T it1;
if (it1->end < it1->end) {
}
if (it1->end < it1->end) { }

X *x;
if (x->end < 7) { // expected-error{{no member named 'end' in 'PR11856::X'}}
}
if (x->end < 7) { } // expected-error{{no member named 'end' in 'PR11856::X'}}

Y *y;
if (y->end < 7) { }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// RUN: %clang_cc1 -std=c++23 %s -verify

int f();

struct A {
int B, C; // expected-note {{declared as a non-template here}}
template<int> using D = void;
using T = void;
void f();
};

using B = A;
template<int> using C = A;
template<int> using D = A;
template<int> using X = A;

template<class T>
void g(T *p) {
p->X<0>::f(); // expected-error {{no member named 'X' in 'A'}}
p->template X<0>::f();
p->B::f();
p->template C<0>::f(); // expected-error {{'C' following the 'template' keyword does not refer to a template}}
p->template D<0>::f(); // expected-error {{type 'template D<0>' (aka 'void') cannot be used prior to '::' because it has no members}}
p->T::f(); // expected-error {{'A::T' (aka 'void') is not a class, namespace, or enumeration}}
}

template void g(A*); // expected-note {{in instantiation of}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// RUN: %clang_cc1 -std=c++23 -Wno-unused %s -verify

namespace Unambiguous {
struct A {
int x;

template<typename T>
using C = A;
};

using B = A;

template<typename T>
using D = A;

using E = void;

struct F : A {
void non_template() {
this->x;
this->A::x;
this->B::x;
this->C<int>::x;
this->D<int>::x;
this->E::x; // expected-error {{'Unambiguous::E' (aka 'void') is not a class, namespace, or enumeration}}
}
};

template<typename T>
void not_instantiated(T t) {
t.x;
t.A::x;
t.B::x;
t.C<int>::x; // expected-warning {{use 'template' keyword to treat 'C' as a dependent template name}}
t.template C<int>::x;
t.D<int>::x; // expected-warning {{use 'template' keyword to treat 'D' as a dependent template name}}
t.template D<int>::x;
t.E::x;
}

template<typename T>
void instantiated_valid(T t) {
t.x;
t.A::x;
t.B::x;
t.template C<int>::x;
t.template D<int>::x;
t.E::x;
}

template<typename T>
void instantiated_invalid(T t) {
t.x;
t.A::x;
t.B::x; // expected-error {{'Unambiguous::Invalid::B' (aka 'void') is not a class, namespace, or enumeration}}
t.template C<int>::x;
t.template D<int>::x; // expected-error {{'D' following the 'template' keyword does not refer to a template}}
t.E::x; // expected-error {{'Unambiguous::E' (aka 'void') is not a class, namespace, or enumeration}}
}

struct Valid : A {
using E = A;
};

template void instantiated_valid(Valid);

struct Invalid : A {
using B = void;
using D = A; // expected-note {{declared as a non-template here}}
};

template void instantiated_invalid(Invalid); // expected-note {{in instantiation of}}
} // namespace Unambiguous

namespace Ambiguous {
inline namespace N {
struct A { }; // expected-note {{candidate found by name lookup is 'Ambiguous::N::A'}}
}

struct A { }; // expected-note {{candidate found by name lookup is 'Ambiguous::A'}}

template<typename T>
void f(T t) {
t.A::x; // expected-error {{reference to 'A' is ambiguous}}
}

struct B {
using A = B;

int x;
};

struct C { };

template void f(B);
template void f(C); // expected-note {{in instantiation of}}

} // namespace Ambiguous
4 changes: 2 additions & 2 deletions clang/test/CXX/class.derived/class.member.lookup/p8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ template<typename T>
void DerivedT<T>::Inner() {
Derived1T<T>::Foo();
Derived2T<T>::Member = 42;
this->Derived1T<T>::Foo();
this->Derived2T<T>::Member = 42;
this->Derived1T<T>::Foo(); // expected-warning{{use 'template' keyword to treat 'Derived1T' as a dependent template name}}
this->Derived2T<T>::Member = 42; // expected-warning{{use 'template' keyword to treat 'Derived2T' as a dependent template name}}
this->Foo(); // expected-error{{non-static member 'Foo' found in multiple base-class subobjects of type 'BaseT<int>'}}
}

Expand Down
8 changes: 4 additions & 4 deletions clang/test/CXX/drs/cwg1xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -615,10 +615,8 @@ namespace cwg141 { // cwg141: 3.1
// cxx98-note@#cwg141-S {{lookup from the current scope refers here}}
// expected-error@#cwg141-a {{no member named 'n' in 'cwg141::A::S<int>'; did you mean '::cwg141::S<int>::n'?}}
// expected-note@#cwg141-S {{'::cwg141::S<int>::n' declared here}}
// FIXME: we issue a useful diagnostic first, then some bogus ones.
b.f<int>();
// expected-error@-1 {{no member named 'f' in 'cwg141::B'}}
// expected-error@-2 +{{}}
(void)b.S<int>::n;
}
template<typename T> struct C {
Expand All @@ -628,10 +626,12 @@ namespace cwg141 { // cwg141: 3.1
// expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}}
}
void h() {
(void)t.S<int>::n; // ok
(void)t.S<int>::n;
// expected-error@-1 {{use 'template' keyword to treat 'S' as a dependent template name}}
}
void i() {
(void)t.S<int>(); // ok!
(void)t.S<int>();
// expected-error@-1 {{use 'template' keyword to treat 'S' as a dependent template name}}
}
};
void h() { C<B>().h(); } // ok
Expand Down
237 changes: 237 additions & 0 deletions clang/test/CXX/temp/temp.names/p3-23.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// RUN: %clang_cc1 -std=c++23 -Wno-unused %s -verify

namespace FoundNothing {
template<typename T>
void f0(T &t) {
t.x<0;
t.x<0>; // expected-error {{expected expression}}
t.x<0>1;
}

template<typename T>
struct A {
void f1() {
this->x<0; // expected-error {{no member named 'x' in 'A<T>'}}
this->x<0>; // expected-error {{no member named 'x' in 'A<T>'}}
// expected-error@-1 {{expected expression}}
this->x<0>1; // expected-error {{no member named 'x' in 'A<T>'}}
}
};
} // namespace FoundNothing

namespace FoundSingleNonTemplate {
void f0();

struct A0;

template<typename T>
void g0(T &t) {
t.f0<0;
t.f0<0>; // expected-error {{expected expression}}
t.f0<0>1;

t.A0<0;
t.A0<0>; // expected-error {{expected expression}}
t.A0<0>1;
}

template<typename T>
struct B {
void f1();

struct A1; // expected-note 3{{member 'A1' declared here}}

void g1() {
this->f0<0; // expected-error {{no member named 'f0' in 'B<T>'}}
this->f0<0>; // expected-error {{no member named 'f0' in 'B<T>'}}
// expected-error@-1 {{expected expression}}
this->f0<0>1; // expected-error {{no member named 'f0' in 'B<T>'}}

this->A0<0; // expected-error {{no member named 'A0' in 'B<T>'}}
this->A0<0>; // expected-error {{no member named 'A0' in 'B<T>'}}
// expected-error@-1 {{expected expression}}
this->A0<0>1; // expected-error {{no member named 'A0' in 'B<T>'}}

this->f1<0; // expected-error {{reference to non-static member function must be called}}
this->f1<0>; // expected-error {{reference to non-static member function must be called}}
// expected-error@-1 {{expected expression}}
this->f1<0>1; // expected-error {{reference to non-static member function must be called}}

this->A1<0; // expected-error {{cannot refer to type member 'A1' in 'B<T>' with '->'}}
this->A1<0>; // expected-error {{cannot refer to type member 'A1' in 'B<T>' with '->'}}
// expected-error@-1 {{expected expression}}
this->A1<0>1; // expected-error {{cannot refer to type member 'A1' in 'B<T>' with '->'}}
}
};
} // namespace FoundSingleNonTemplate

namespace FoundSingleTemplate {
template<int I>
void f0();

template<int I>
struct A0;

template<typename T>
void g0(T &t) {
t.f0<0;
t.f0<0>; // expected-error {{expected expression}}
t.f0<0>1;

t.A0<0;
t.A0<0>; // expected-error {{expected expression}}
t.A0<0>1;
}

template<typename T>
struct B {
template<int I>
void f1(); // expected-note 2{{possible target for call}}

template<int I>
struct A1; // expected-note 2{{member 'A1' declared here}}

void g1() {
this->f0<0; // expected-error {{no member named 'f0' in 'B<T>'}}
this->f0<0>; // expected-error {{no member named 'f0' in 'B<T>'}}
this->f0<0>1; // expected-error {{no member named 'f0' in 'B<T>'}}
// expected-error@-1 {{expected ';' after expression}}

this->A0<0; // expected-error {{no member named 'A0' in 'B<T>'}}
this->A0<0>; // expected-error {{no member named 'A0' in 'B<T>'}}
this->A0<0>1; // expected-error {{no member named 'A0' in 'B<T>'}}
// expected-error@-1 {{expected ';' after expression}}


this->f1<0; // expected-error {{expected '>'}}
// expected-note@-1 {{to match this '<'}}
this->f1<0>; // expected-error {{reference to non-static member function must be called}}
this->f1<0>1; // expected-error {{reference to non-static member function must be called}}
// expected-error@-1 {{expected ';' after expression}}

this->A1<0; // expected-error {{expected '>'}}
// expected-note@-1 {{to match this '<'}}
this->A1<0>; // expected-error {{cannot refer to member 'A1' in 'B<T>' with '->'}}
this->A1<0>1; // expected-error {{cannot refer to member 'A1' in 'B<T>' with '->'}}
// expected-error@-1 {{expected ';' after expression}}
}
};
} // namespace FoundSingleTemplate

namespace FoundAmbiguousNonTemplate {
inline namespace N {
int f0;

struct A0;
} // namespace N

void f0();

struct A0;

template<typename T>
void g0(T &t) {
t.f0<0;
t.f0<0>; // expected-error {{expected expression}}
t.f0<0>1;

t.A0<0;
t.A0<0>; // expected-error {{expected expression}}
t.A0<0>1;
}

template<typename T>
struct B {
void f1();

struct A1; // expected-note 3{{member 'A1' declared here}}

void g1() {
this->f0<0; // expected-error {{no member named 'f0' in 'B<T>'}}
this->f0<0>; // expected-error {{no member named 'f0' in 'B<T>'}}
// expected-error@-1 {{expected expression}}
this->f0<0>1; // expected-error {{no member named 'f0' in 'B<T>'}}

this->A0<0; // expected-error {{no member named 'A0' in 'B<T>'}}
this->A0<0>; // expected-error {{no member named 'A0' in 'B<T>'}}
// expected-error@-1 {{expected expression}}
this->A0<0>1; // expected-error {{no member named 'A0' in 'B<T>'}}

this->f1<0; // expected-error {{reference to non-static member function must be called}}
this->f1<0>; // expected-error {{reference to non-static member function must be called}}
// expected-error@-1 {{expected expression}}
this->f1<0>1; // expected-error {{reference to non-static member function must be called}}

this->A1<0; // expected-error {{cannot refer to type member 'A1' in 'B<T>' with '->'}}
this->A1<0>; // expected-error {{cannot refer to type member 'A1' in 'B<T>' with '->'}}
// expected-error@-1 {{expected expression}}
this->A1<0>1; // expected-error {{cannot refer to type member 'A1' in 'B<T>' with '->'}}
}
};
} // namespace FoundAmbiguousNonTemplates

namespace FoundAmbiguousTemplate {
inline namespace N {
template<int I>
int f0; // expected-note 3{{candidate found by name lookup is 'FoundAmbiguousTemplate::N::f0'}}

template<int I>
struct A0; // expected-note 3{{candidate found by name lookup is 'FoundAmbiguousTemplate::N::A0'}}
} // namespace N

template<int I>
void f0(); // expected-note 3{{candidate found by name lookup is 'FoundAmbiguousTemplate::f0'}}

template<int I>
struct A0; // expected-note 3{{candidate found by name lookup is 'FoundAmbiguousTemplate::A0'}}

template<typename T>
void g0(T &t) {
t.f0<0;
t.f0<0>; // expected-error {{expected expression}}
t.f0<0>1;

t.A0<0;
t.A0<0>; // expected-error {{expected expression}}
t.A0<0>1;
}

template<typename T>
struct B {
template<int I>
void f1(); // expected-note 2{{possible target for call}}

template<int I>
struct A1; // expected-note 2{{member 'A1' declared here}}

void g1() {
this->f0<0; // expected-error {{no member named 'f0' in 'B<T>'}}
// expected-error@-1 {{reference to 'f0' is ambiguous}}
this->f0<0>; // expected-error {{no member named 'f0' in 'B<T>'}}
// expected-error@-1 {{reference to 'f0' is ambiguous}}
this->f0<0>1; // expected-error {{no member named 'f0' in 'B<T>'}}
// expected-error@-1 {{expected ';' after expression}}
// expected-error@-2 {{reference to 'f0' is ambiguous}}

this->A0<0; // expected-error {{no member named 'A0' in 'B<T>'}}
// expected-error@-1 {{reference to 'A0' is ambiguous}}
this->A0<0>; // expected-error {{no member named 'A0' in 'B<T>'}}
// expected-error@-1 {{reference to 'A0' is ambiguous}}
this->A0<0>1; // expected-error {{no member named 'A0' in 'B<T>'}}
// expected-error@-1 {{expected ';' after expression}}
// expected-error@-2 {{reference to 'A0' is ambiguous}}

this->f1<0; // expected-error {{expected '>'}}
// expected-note@-1 {{to match this '<'}}
this->f1<0>; // expected-error {{reference to non-static member function must be called}}
this->f1<0>1; // expected-error {{reference to non-static member function must be called}}
// expected-error@-1 {{expected ';' after expression}}

this->A1<0; // expected-error {{expected '>'}}
// expected-note@-1 {{to match this '<'}}
this->A1<0>; // expected-error {{cannot refer to member 'A1' in 'B<T>' with '->'}}
this->A1<0>1; // expected-error {{cannot refer to member 'A1' in 'B<T>' with '->'}}
// expected-error@-1 {{expected ';' after expression}}
}
};
} // namespace FoundAmbiguousTemplate
2 changes: 1 addition & 1 deletion clang/test/CXX/temp/temp.res/p3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ template<typename T> int A<T>::template C<int>::*f5() {} // expected-error {{has
template<typename T> template<typename U> struct A<T>::B {
friend A<T>::C<T> f6(); // ok, same as 'friend T f6();'

friend A<U>::C<T> f7(); // expected-error {{use 'template' keyword to treat 'C' as a dependent template name}} expected-warning {{missing 'typename'}}
friend A<U>::C<T> f7(); // expected-warning {{use 'template' keyword to treat 'C' as a dependent template name}} expected-warning {{missing 'typename'}}
friend A<U>::template C<T> f8(); // expected-warning {{missing 'typename'}}
};
1 change: 0 additions & 1 deletion clang/test/CodeGen/bitfield-access-pad.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
// Configs that have expensive unaligned access
// Little Endian
// RUN: %clang_cc1 -triple=hexagon-elf %s -emit-llvm -o /dev/null -fdump-record-layouts-simple | FileCheck --check-prefixes CHECK,LAYOUT-T %s
// RUN: %clang_cc1 -triple=le64-elf %s -emit-llvm -o /dev/null -fdump-record-layouts-simple | FileCheck --check-prefixes CHECK,LAYOUT-T %s

// Big endian
// RUN: %clang_cc1 -triple=m68k-elf %s -emit-llvm -o /dev/null -fdump-record-layouts-simple | FileCheck --check-prefixes CHECK,LAYOUT-T %s
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CodeGen/bitfield-access-unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@
// RUN: %clang_cc1 -triple=sparc-elf %s -emit-llvm -o /dev/null -fdump-record-layouts-simple | FileCheck --check-prefixes CHECK,LAYOUT,LAYOUT-STRICT %s
// RUN: %clang_cc1 -triple=tce-elf %s -emit-llvm -o /dev/null -fdump-record-layouts-simple | FileCheck --check-prefixes CHECK,LAYOUT,LAYOUT-STRICT %s

// Both le64-elf and m68-elf are strict alignment ISAs with 4-byte aligned
// 64-bit or 2-byte aligned 32-bit integer types. This more compex to describe here.
// m68-elf is a strict alignment ISA with 4-byte aligned 64-bit or 2-byte
// aligned 32-bit integer types. This more compex to describe here.

// If unaligned access is expensive don't stick these together.
struct A {
Expand Down
Loading