Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 60 additions & 71 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -11309,9 +11309,6 @@ class Sema final : public SemaBase {
InventedParameterInfos.end());
}

/// The number of SFINAE diagnostics that have been trapped.
unsigned NumSFINAEErrors;

ArrayRef<sema::FunctionScopeInfo *> getFunctionScopes() const {
return llvm::ArrayRef(FunctionScopes.begin() + FunctionScopesStart,
FunctionScopes.end());
Expand Down Expand Up @@ -12385,49 +12382,65 @@ class Sema final : public SemaBase {
///@{

public:
/// When true, access checking violations are treated as SFINAE
/// failures rather than hard errors.
bool AccessCheckingSFINAE;
class SFINAETrap;

struct SFINAEContextBase {
SFINAEContextBase(Sema &S, SFINAETrap *Cur)
: S(S), Prev(std::exchange(S.CurrentSFINAEContext, Cur)) {}

protected:
Sema &S;
~SFINAEContextBase() { S.CurrentSFINAEContext = Prev; }

private:
SFINAETrap *Prev;
};
Comment on lines +12395 to +12397
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks weird to have a sub-class's pointer in a base class. Though this is not a blocker

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does allow to share the CurrentSFINAEContext setup between all derived classes.

If you have a better idea I am all ears.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems that doing so makes clients enough simple, though I don't think I have better ideas yet :(

@cor3ntin

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alternative would be SFINAEContext _(S, SFINAEContext::NoSFINAE) - that would also make sense to me.
I don't have strong feelings


struct NonSFINAEContext : SFINAEContextBase {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
struct NonSFINAEContext : SFINAEContextBase {
struct NonSFINAEContextRAII : SFINAEContextBase {

Maybe that would be clearer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think having both the 'Context' and 'RAII' qualifiers would be a bit too much, feels redundant to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You wanted to rename ContextRAII 😄?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a bad idea!

NonSFINAEContext(Sema &S) : SFINAEContextBase(S, nullptr) {}
};

/// RAII class used to determine whether SFINAE has
/// trapped any errors that occur during template argument
/// deduction.
class SFINAETrap {
Sema &SemaRef;
unsigned PrevSFINAEErrors;
bool PrevInNonInstantiationSFINAEContext;
bool PrevAccessCheckingSFINAE;
bool PrevLastDiagnosticIgnored;
class SFINAETrap : SFINAEContextBase {
bool HasErrorOcurred = false;
bool WithAccessChecking = false;
bool PrevLastDiagnosticIgnored =
S.getDiagnostics().isLastDiagnosticIgnored();
sema::TemplateDeductionInfo *DeductionInfo = nullptr;

SFINAETrap(Sema &S, sema::TemplateDeductionInfo *Info,
bool WithAccessChecking)
: SFINAEContextBase(S, this), WithAccessChecking(WithAccessChecking),
DeductionInfo(Info) {}

public:
/// \param ForValidityCheck If true, discard all diagnostics (from the
/// \param WithAccessChecking If true, discard all diagnostics (from the
/// immediate context) instead of adding them to the currently active
/// \ref TemplateDeductionInfo (as returned by \ref isSFINAEContext).
explicit SFINAETrap(Sema &SemaRef, bool ForValidityCheck = false)
: SemaRef(SemaRef), PrevSFINAEErrors(SemaRef.NumSFINAEErrors),
PrevInNonInstantiationSFINAEContext(
SemaRef.InNonInstantiationSFINAEContext),
PrevAccessCheckingSFINAE(SemaRef.AccessCheckingSFINAE),
PrevLastDiagnosticIgnored(
SemaRef.getDiagnostics().isLastDiagnosticIgnored()) {
if (ForValidityCheck || !SemaRef.isSFINAEContext())
SemaRef.InNonInstantiationSFINAEContext = true;
SemaRef.AccessCheckingSFINAE = ForValidityCheck;
}
/// \ref TemplateDeductionInfo.
explicit SFINAETrap(Sema &S, bool WithAccessChecking = false)
: SFINAETrap(S, /*Info=*/nullptr, WithAccessChecking) {}

SFINAETrap(Sema &S, sema::TemplateDeductionInfo &Info)
: SFINAETrap(S, &Info, /*WithAccessChecking=*/false) {}

~SFINAETrap() {
SemaRef.NumSFINAEErrors = PrevSFINAEErrors;
SemaRef.InNonInstantiationSFINAEContext =
PrevInNonInstantiationSFINAEContext;
SemaRef.AccessCheckingSFINAE = PrevAccessCheckingSFINAE;
SemaRef.getDiagnostics().setLastDiagnosticIgnored(
PrevLastDiagnosticIgnored);
S.getDiagnostics().setLastDiagnosticIgnored(PrevLastDiagnosticIgnored);
}

/// Determine whether any SFINAE errors have been trapped.
bool hasErrorOccurred() const {
return SemaRef.NumSFINAEErrors > PrevSFINAEErrors;
SFINAETrap(const SFINAETrap &) = delete;
SFINAETrap &operator=(const SFINAETrap &) = delete;

sema::TemplateDeductionInfo *getDeductionInfo() const {
return DeductionInfo;
}

/// Determine whether any SFINAE errors have been trapped.
bool hasErrorOccurred() const { return HasErrorOcurred; }
void setErrorOccurred() { HasErrorOcurred = true; }

bool withAccessChecking() const { return WithAccessChecking; }
};

/// RAII class used to indicate that we are performing provisional
Expand Down Expand Up @@ -13148,9 +13161,6 @@ class Sema final : public SemaBase {
PartialOrderingTTP,
} Kind;

/// Was the enclosing context a non-instantiation SFINAE context?
bool SavedInNonInstantiationSFINAEContext;

/// Whether we're substituting into constraints.
bool InConstraintSubstitution;

Expand Down Expand Up @@ -13195,22 +13205,15 @@ class Sema final : public SemaBase {
return {TemplateArgs, NumTemplateArgs};
}

/// The template deduction info object associated with the
/// substitution or checking of explicit or deduced template arguments.
sema::TemplateDeductionInfo *DeductionInfo;

/// The source range that covers the construct that cause
/// the instantiation, e.g., the template-id that causes a class
/// template instantiation.
SourceRange InstantiationRange;

CodeSynthesisContext()
: Kind(TemplateInstantiation),
SavedInNonInstantiationSFINAEContext(false),
InConstraintSubstitution(false),
: Kind(TemplateInstantiation), InConstraintSubstitution(false),
InParameterMappingSubstitution(false), Entity(nullptr),
Template(nullptr), TemplateArgs(nullptr), NumTemplateArgs(0),
DeductionInfo(nullptr) {}
Template(nullptr), TemplateArgs(nullptr), NumTemplateArgs(0) {}

/// Determines whether this template is an actual instantiation
/// that should be counted toward the maximum instantiation depth.
Expand Down Expand Up @@ -13262,15 +13265,13 @@ class Sema final : public SemaBase {
FunctionTemplateDecl *FunctionTemplate,
ArrayRef<TemplateArgument> TemplateArgs,
CodeSynthesisContext::SynthesisKind Kind,
sema::TemplateDeductionInfo &DeductionInfo,
SourceRange InstantiationRange = SourceRange());

/// Note that we are instantiating as part of template
/// argument deduction for a class template declaration.
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
TemplateDecl *Template,
ArrayRef<TemplateArgument> TemplateArgs,
sema::TemplateDeductionInfo &DeductionInfo,
SourceRange InstantiationRange = SourceRange());

/// Note that we are instantiating as part of template
Expand All @@ -13279,7 +13280,6 @@ class Sema final : public SemaBase {
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
ClassTemplatePartialSpecializationDecl *PartialSpec,
ArrayRef<TemplateArgument> TemplateArgs,
sema::TemplateDeductionInfo &DeductionInfo,
SourceRange InstantiationRange = SourceRange());

/// Note that we are instantiating as part of template
Expand All @@ -13288,7 +13288,6 @@ class Sema final : public SemaBase {
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
VarTemplatePartialSpecializationDecl *PartialSpec,
ArrayRef<TemplateArgument> TemplateArgs,
sema::TemplateDeductionInfo &DeductionInfo,
SourceRange InstantiationRange = SourceRange());

/// Note that we are instantiating a default argument for a function
Expand Down Expand Up @@ -13334,7 +13333,6 @@ class Sema final : public SemaBase {
/// concept.
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
ConstraintSubstitution, NamedDecl *Template,
sema::TemplateDeductionInfo &DeductionInfo,
SourceRange InstantiationRange);

struct ConstraintNormalization {};
Expand All @@ -13354,7 +13352,6 @@ class Sema final : public SemaBase {
/// a requirement of a requires expression.
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
concepts::Requirement *Req,
sema::TemplateDeductionInfo &DeductionInfo,
SourceRange InstantiationRange = SourceRange());

/// \brief Note that we are checking the satisfaction of the constraint
Expand All @@ -13366,7 +13363,6 @@ class Sema final : public SemaBase {
/// \brief Note that we are checking a requires clause.
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
const RequiresExpr *E,
sema::TemplateDeductionInfo &DeductionInfo,
SourceRange InstantiationRange);

struct BuildingDeductionGuidesTag {};
Expand Down Expand Up @@ -13399,8 +13395,7 @@ class Sema final : public SemaBase {
SourceLocation PointOfInstantiation,
SourceRange InstantiationRange, Decl *Entity,
NamedDecl *Template = nullptr,
ArrayRef<TemplateArgument> TemplateArgs = {},
sema::TemplateDeductionInfo *DeductionInfo = nullptr);
ArrayRef<TemplateArgument> TemplateArgs = {});

InstantiatingTemplate(const InstantiatingTemplate &) = delete;

Expand Down Expand Up @@ -13541,12 +13536,7 @@ class Sema final : public SemaBase {
/// recent visible declaration of that namespace.
llvm::DenseMap<NamedDecl *, NamedDecl *> VisibleNamespaceCache;

/// Whether we are in a SFINAE context that is not associated with
/// template instantiation.
///
/// This is used when setting up a SFINAE trap (\c see SFINAETrap) outside
/// of a template instantiation or template argument deduction.
bool InNonInstantiationSFINAEContext;
SFINAETrap *CurrentSFINAEContext = nullptr;

/// The number of \p CodeSynthesisContexts that are not template
/// instantiations and, therefore, should not be counted as part of the
Expand Down Expand Up @@ -13617,15 +13607,13 @@ class Sema final : public SemaBase {
PrintInstantiationStack(getDefaultDiagFunc());
}

/// Determines whether we are currently in a context where
/// template argument substitution failures are not considered
/// errors.
///
/// \returns An empty \c Optional if we're not in a SFINAE context.
/// Otherwise, contains a pointer that, if non-NULL, contains the nearest
/// template-deduction context object, which can be used to capture
/// diagnostics that will be suppressed.
std::optional<sema::TemplateDeductionInfo *> isSFINAEContext() const;
/// Returns a pointer to the current SFINAE context, if any.
[[nodiscard]] SFINAETrap *getSFINAEContext() const {
return CurrentSFINAEContext;
}
[[nodiscard]] bool isSFINAEContext() const {
return CurrentSFINAEContext != nullptr;
}

/// Perform substitution on the type T with a given set of template
/// arguments.
Expand Down Expand Up @@ -14637,7 +14625,8 @@ class Sema final : public SemaBase {
ArrayRef<UnexpandedParameterPack> Unexpanded,
const MultiLevelTemplateArgumentList &TemplateArgs,
bool FailOnPackProducingTemplates, bool &ShouldExpand,
bool &RetainExpansion, UnsignedOrNone &NumExpansions);
bool &RetainExpansion, UnsignedOrNone &NumExpansions,
bool Diagnose = true);

/// Determine the number of arguments in the given pack expansion
/// type.
Expand Down
42 changes: 22 additions & 20 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,9 +321,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
static_cast<unsigned>(ComparisonCategoryType::Last) + 1),
StdSourceLocationImplDecl(nullptr), CXXTypeInfoDecl(nullptr),
GlobalNewDeleteDeclared(false), DisableTypoCorrection(false),
TyposCorrected(0), IsBuildingRecoveryCallExpr(false), NumSFINAEErrors(0),
AccessCheckingSFINAE(false), CurrentInstantiationScope(nullptr),
InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0),
TyposCorrected(0), IsBuildingRecoveryCallExpr(false),
CurrentInstantiationScope(nullptr), NonInstantiationEntries(0),
ArgPackSubstIndex(std::nullopt), SatisfactionCache(Context) {
assert(pp.TUKind == TUKind);
TUScope = nullptr;
Expand Down Expand Up @@ -670,7 +669,9 @@ void Sema::addExternalSource(IntrusiveRefCntPtr<ExternalSemaSource> E) {

void Sema::PrintStats() const {
llvm::errs() << "\n*** Semantic Analysis Stats:\n";
llvm::errs() << NumSFINAEErrors << " SFINAE diagnostics trapped.\n";
if (SFINAETrap *Trap = getSFINAEContext())
llvm::errs() << int(Trap->hasErrorOccurred())
<< " SFINAE diagnostics trapped.\n";

BumpAlloc.PrintStats();
AnalysisWarnings.PrintStats();
Expand Down Expand Up @@ -1681,7 +1682,8 @@ void Sema::EmitDiagnostic(unsigned DiagID, const DiagnosticBuilder &DB) {
// issue I am not seeing yet), then there should at least be a clarifying
// comment somewhere.
Diagnostic DiagInfo(&Diags, DB);
if (std::optional<TemplateDeductionInfo *> Info = isSFINAEContext()) {
if (SFINAETrap *Trap = getSFINAEContext()) {
sema::TemplateDeductionInfo *Info = Trap->getDeductionInfo();
switch (DiagnosticIDs::getDiagnosticSFINAEResponse(DiagInfo.getID())) {
case DiagnosticIDs::SFINAE_Report:
// We'll report the diagnostic below.
Expand All @@ -1690,37 +1692,37 @@ void Sema::EmitDiagnostic(unsigned DiagID, const DiagnosticBuilder &DB) {
case DiagnosticIDs::SFINAE_SubstitutionFailure:
// Count this failure so that we know that template argument deduction
// has failed.
++NumSFINAEErrors;
Trap->setErrorOccurred();

// Make a copy of this suppressed diagnostic and store it with the
// template-deduction information.
if (*Info && !(*Info)->hasSFINAEDiagnostic()) {
(*Info)->addSFINAEDiagnostic(DiagInfo.getLocation(),
PartialDiagnostic(DiagInfo, Context.getDiagAllocator()));
}
if (Info && !Info->hasSFINAEDiagnostic())
Info->addSFINAEDiagnostic(
DiagInfo.getLocation(),
PartialDiagnostic(DiagInfo, Context.getDiagAllocator()));

Diags.setLastDiagnosticIgnored(true);
return;

case DiagnosticIDs::SFINAE_AccessControl: {
// Per C++ Core Issue 1170, access control is part of SFINAE.
// Additionally, the AccessCheckingSFINAE flag can be used to temporarily
// Additionally, the WithAccessChecking flag can be used to temporarily
// make access control a part of SFINAE for the purposes of checking
// type traits.
if (!AccessCheckingSFINAE && !getLangOpts().CPlusPlus11)
if (!Trap->withAccessChecking() && !getLangOpts().CPlusPlus11)
break;

SourceLocation Loc = DiagInfo.getLocation();

// Suppress this diagnostic.
++NumSFINAEErrors;
Trap->setErrorOccurred();

// Make a copy of this suppressed diagnostic and store it with the
// template-deduction information.
if (*Info && !(*Info)->hasSFINAEDiagnostic()) {
(*Info)->addSFINAEDiagnostic(DiagInfo.getLocation(),
PartialDiagnostic(DiagInfo, Context.getDiagAllocator()));
}
if (Info && !Info->hasSFINAEDiagnostic())
Info->addSFINAEDiagnostic(
DiagInfo.getLocation(),
PartialDiagnostic(DiagInfo, Context.getDiagAllocator()));

Diags.setLastDiagnosticIgnored(true);

Expand All @@ -1740,13 +1742,13 @@ void Sema::EmitDiagnostic(unsigned DiagID, const DiagnosticBuilder &DB) {
return;
// Make a copy of this suppressed diagnostic and store it with the
// template-deduction information;
if (*Info) {
(*Info)->addSuppressedDiagnostic(
if (Info) {
Info->addSuppressedDiagnostic(
DiagInfo.getLocation(),
PartialDiagnostic(DiagInfo, Context.getDiagAllocator()));
if (!Diags.getDiagnosticIDs()->isNote(DiagID))
PrintContextStack([Info](SourceLocation Loc, PartialDiagnostic PD) {
(*Info)->addSuppressedDiagnostic(Loc, std::move(PD));
Info->addSuppressedDiagnostic(Loc, std::move(PD));
});
}

Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/SemaAMDGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,8 @@ AMDGPUMaxNumWorkGroupsAttr *SemaAMDGPU::CreateAMDGPUMaxNumWorkGroupsAttr(
const AttributeCommonInfo &CI, Expr *XExpr, Expr *YExpr, Expr *ZExpr) {
ASTContext &Context = getASTContext();
AMDGPUMaxNumWorkGroupsAttr TmpAttr(Context, CI, XExpr, YExpr, ZExpr);
assert(!SemaRef.isSFINAEContext() &&
"Can't produce SFINAE diagnostic pointing to temporary attribute");

if (checkAMDGPUMaxNumWorkGroupsArguments(SemaRef, XExpr, YExpr, ZExpr,
TmpAttr))
Expand Down
Loading