Skip to content

Commit

Permalink
[clang] adding explicit(bool) from c++2a
Browse files Browse the repository at this point in the history
this patch adds support for the explicit bool specifier.

Changes:
- The parsing for the explicit(bool) specifier was added in ParseDecl.cpp.
- The storage of the explicit specifier was changed. the explicit specifier was stored as a boolean value in the FunctionDeclBitfields and in the DeclSpec class. now it is stored as a PointerIntPair<Expr*, 2> with a flag and a potential expression in CXXConstructorDecl, CXXDeductionGuideDecl, CXXConversionDecl and in the DeclSpec class.
- Following the AST change, Serialization, ASTMatchers, ASTComparator and ASTPrinter were adapted.
- Template instantiation was adapted to instantiate the potential expressions of the explicit(bool) specifier When instantiating their associated declaration.
- The Add*Candidate functions were adapted, they now take a Boolean indicating if the context allowing explicit constructor or conversion function and this boolean is used to remove invalid overloads that required template instantiation to be detected.
- Test for Semantic and Serialization were added.

This patch is not yet complete. I still need to check that interaction with CTAD and deduction guides is correct. and add more tests for AST operations. But I wanted first feedback.
Perhaps this patch should be spited in smaller patches, but making each patch testable as a standalone may be tricky.

Patch by Tyker

Differential Revision: https://reviews.llvm.org/D60934

llvm-svn: 359949
  • Loading branch information
Rakete1111 committed May 4, 2019
1 parent a6b41d7 commit 5fe2ddb
Show file tree
Hide file tree
Showing 45 changed files with 1,643 additions and 360 deletions.
10 changes: 0 additions & 10 deletions clang/include/clang/AST/Decl.h
Expand Up @@ -2369,16 +2369,6 @@ class FunctionDecl : public DeclaratorDecl,
/// that was defined in the class body.
bool isInlined() const { return FunctionDeclBits.IsInline; }

/// Whether this function is marked as explicit explicitly.
bool isExplicitSpecified() const {
return FunctionDeclBits.IsExplicitSpecified;
}

/// State that this function is marked as explicit explicitly.
void setExplicitSpecified(bool ExpSpec = true) {
FunctionDeclBits.IsExplicitSpecified = ExpSpec;
}

bool isInlineDefinitionExternallyVisible() const;

bool isMSExternInline() const;
Expand Down
20 changes: 12 additions & 8 deletions clang/include/clang/AST/DeclBase.h
Expand Up @@ -1472,10 +1472,6 @@ class DeclContext {
uint64_t IsInline : 1;
uint64_t IsInlineSpecified : 1;

/// This is shared by CXXConstructorDecl,
/// CXXConversionDecl, and CXXDeductionGuideDecl.
uint64_t IsExplicitSpecified : 1;

uint64_t IsVirtualAsWritten : 1;
uint64_t IsPure : 1;
uint64_t HasInheritedPrototype : 1;
Expand Down Expand Up @@ -1523,7 +1519,7 @@ class DeclContext {
};

/// Number of non-inherited bits in FunctionDeclBitfields.
enum { NumFunctionDeclBits = 25 };
enum { NumFunctionDeclBits = 24 };

/// Stores the bits used by CXXConstructorDecl. If modified
/// NumCXXConstructorDeclBits and the accessor
Expand All @@ -1535,17 +1531,25 @@ class DeclContext {
/// For the bits in FunctionDeclBitfields.
uint64_t : NumFunctionDeclBits;

/// 25 bits to fit in the remaining availible space.
/// 24 bits to fit in the remaining available space.
/// Note that this makes CXXConstructorDeclBitfields take
/// exactly 64 bits and thus the width of NumCtorInitializers
/// will need to be shrunk if some bit is added to NumDeclContextBitfields,
/// NumFunctionDeclBitfields or CXXConstructorDeclBitfields.
uint64_t NumCtorInitializers : 25;
uint64_t NumCtorInitializers : 24;
uint64_t IsInheritingConstructor : 1;

/// Whether this constructor has a trail-allocated explicit specifier.
uint64_t HasTrailingExplicitSpecifier : 1;
/// If this constructor does't have a trail-allocated explicit specifier.
/// Whether this constructor is explicit specified.
uint64_t IsSimpleExplicit : 1;
};

/// Number of non-inherited bits in CXXConstructorDeclBitfields.
enum { NumCXXConstructorDeclBits = 26 };
enum {
NumCXXConstructorDeclBits = 64 - NumDeclContextBits - NumFunctionDeclBits
};

/// Stores the bits used by ObjCMethodDecl.
/// If modified NumObjCMethodDeclBits and the accessor
Expand Down
176 changes: 136 additions & 40 deletions clang/include/clang/AST/DeclCXX.h
Expand Up @@ -1990,6 +1990,50 @@ class CXXRecordDecl : public RecordDecl {
}
};

/// Store information needed for an explicit specifier.
/// used by CXXDeductionGuideDecl, CXXConstructorDecl and CXXConversionDecl.
class ExplicitSpecifier {
llvm::PointerIntPair<Expr *, 2, ExplicitSpecKind> ExplicitSpec{
nullptr, ExplicitSpecKind::ResolvedFalse};

public:
ExplicitSpecifier() = default;
ExplicitSpecifier(Expr *Expression, ExplicitSpecKind Kind)
: ExplicitSpec(Expression, Kind) {}
ExplicitSpecKind getKind() const { return ExplicitSpec.getInt(); }
const Expr *getExpr() const { return ExplicitSpec.getPointer(); }
Expr *getExpr() { return ExplicitSpec.getPointer(); }

/// Return true if the ExplicitSpecifier isn't defaulted.
bool isSpecified() const {
return ExplicitSpec.getInt() != ExplicitSpecKind::ResolvedFalse ||
ExplicitSpec.getPointer();
}

/// Check for Equivalence of explicit specifiers.
/// Return True if the explicit specifier are equivalent false otherwise.
bool isEquivalent(const ExplicitSpecifier Other) const;
/// Return true if the explicit specifier is already resolved to be explicit.
bool isExplicit() const {
return ExplicitSpec.getInt() == ExplicitSpecKind::ResolvedTrue;
}
/// Return true if the ExplicitSpecifier isn't valid.
/// This state occurs after a substitution failures.
bool isInvalid() const {
return ExplicitSpec.getInt() == ExplicitSpecKind::Unresolved &&
!ExplicitSpec.getPointer();
}
void setKind(ExplicitSpecKind Kind) { ExplicitSpec.setInt(Kind); }
void setExpr(Expr *E) { ExplicitSpec.setPointer(E); }
// getFromDecl - retrieve the explicit specifier in the given declaration.
// if the given declaration has no explicit. the returned explicit specifier
// is defaulted. .isSpecified() will be false.
static ExplicitSpecifier getFromDecl(FunctionDecl *Function);
static ExplicitSpecifier Invalid() {
return ExplicitSpecifier(nullptr, ExplicitSpecKind::Unresolved);
}
};

/// Represents a C++ deduction guide declaration.
///
/// \code
Expand All @@ -2005,31 +2049,36 @@ class CXXDeductionGuideDecl : public FunctionDecl {

private:
CXXDeductionGuideDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
bool IsExplicit, const DeclarationNameInfo &NameInfo,
QualType T, TypeSourceInfo *TInfo,
SourceLocation EndLocation)
ExplicitSpecifier ES,
const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, SourceLocation EndLocation)
: FunctionDecl(CXXDeductionGuide, C, DC, StartLoc, NameInfo, T, TInfo,
SC_None, false, false) {
SC_None, false, false),
ExplicitSpec(ES) {
if (EndLocation.isValid())
setRangeEnd(EndLocation);
setExplicitSpecified(IsExplicit);
setIsCopyDeductionCandidate(false);
}

ExplicitSpecifier ExplicitSpec;
void setExplicitSpecifier(ExplicitSpecifier ES) { ExplicitSpec = ES; }

public:
friend class ASTDeclReader;
friend class ASTDeclWriter;

static CXXDeductionGuideDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation StartLoc, bool IsExplicit,
const DeclarationNameInfo &NameInfo,
QualType T, TypeSourceInfo *TInfo,
SourceLocation EndLocation);
static CXXDeductionGuideDecl *
Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc,
ExplicitSpecifier ES, const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, SourceLocation EndLocation);

static CXXDeductionGuideDecl *CreateDeserialized(ASTContext &C, unsigned ID);

/// Whether this deduction guide is explicit.
bool isExplicit() const { return isExplicitSpecified(); }
ExplicitSpecifier getExplicitSpecifier() { return ExplicitSpec; }
const ExplicitSpecifier getExplicitSpecifier() const { return ExplicitSpec; }

/// Return true if the declartion is already resolved to be explicit.
bool isExplicit() const { return ExplicitSpec.isExplicit(); }

/// Get the template for which this guide performs deduction.
TemplateDecl *getDeducedTemplate() const {
Expand Down Expand Up @@ -2498,7 +2547,8 @@ class InheritedConstructor {
/// \endcode
class CXXConstructorDecl final
: public CXXMethodDecl,
private llvm::TrailingObjects<CXXConstructorDecl, InheritedConstructor> {
private llvm::TrailingObjects<CXXConstructorDecl, InheritedConstructor,
ExplicitSpecifier> {
// This class stores some data in DeclContext::CXXConstructorDeclBits
// to save some space. Use the provided accessors to access it.

Expand All @@ -2508,28 +2558,74 @@ class CXXConstructorDecl final
LazyCXXCtorInitializersPtr CtorInitializers;

CXXConstructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
const DeclarationNameInfo &NameInfo,
QualType T, TypeSourceInfo *TInfo,
bool isExplicitSpecified, bool isInline,
const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, ExplicitSpecifier ES, bool isInline,
bool isImplicitlyDeclared, bool isConstexpr,
InheritedConstructor Inherited);

void anchor() override;

size_t numTrailingObjects(OverloadToken<InheritedConstructor>) const {
return CXXConstructorDeclBits.IsInheritingConstructor;
}
size_t numTrailingObjects(OverloadToken<ExplicitSpecifier>) const {
return CXXConstructorDeclBits.HasTrailingExplicitSpecifier;
}

ExplicitSpecifier getExplicitSpecifierInternal() const {
if (CXXConstructorDeclBits.HasTrailingExplicitSpecifier)
return *getCanonicalDecl()->getTrailingObjects<ExplicitSpecifier>();
return ExplicitSpecifier(
nullptr, getCanonicalDecl()->CXXConstructorDeclBits.IsSimpleExplicit
? ExplicitSpecKind::ResolvedTrue
: ExplicitSpecKind::ResolvedFalse);
}

void setExplicitSpecifier(ExplicitSpecifier ES) {
assert((!ES.getExpr() ||
CXXConstructorDeclBits.HasTrailingExplicitSpecifier) &&
"cannot set this explicit specifier. no trail-allocated space for "
"explicit");
if (ES.getExpr())
*getCanonicalDecl()->getTrailingObjects<ExplicitSpecifier>() = ES;
else
CXXConstructorDeclBits.IsSimpleExplicit = ES.isExplicit();
}

enum TraillingAllocKind {
TAKInheritsConstructor = 1,
TAKHasTailExplicit = 1 << 1,
};

uint64_t getTraillingAllocKind() const {
return numTrailingObjects(OverloadToken<InheritedConstructor>()) |
(numTrailingObjects(OverloadToken<ExplicitSpecifier>()) << 1);
}

public:
friend class ASTDeclReader;
friend class ASTDeclWriter;
friend TrailingObjects;

static CXXConstructorDecl *CreateDeserialized(ASTContext &C, unsigned ID,
bool InheritsConstructor);
uint64_t AllocKind);
static CXXConstructorDecl *
Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
bool isExplicit, bool isInline, bool isImplicitlyDeclared,
ExplicitSpecifier ES, bool isInline, bool isImplicitlyDeclared,
bool isConstexpr,
InheritedConstructor Inherited = InheritedConstructor());

ExplicitSpecifier getExplicitSpecifier() {
return getExplicitSpecifierInternal();
}
const ExplicitSpecifier getExplicitSpecifier() const {
return getExplicitSpecifierInternal();
}

/// Return true if the declartion is already resolved to be explicit.
bool isExplicit() const { return getExplicitSpecifier().isExplicit(); }

/// Iterates through the member/base initializer list.
using init_iterator = CXXCtorInitializer **;

Expand Down Expand Up @@ -2600,11 +2696,6 @@ class CXXConstructorDecl final
CtorInitializers = Initializers;
}

/// Whether this function is explicit.
bool isExplicit() const {
return getCanonicalDecl()->isExplicitSpecified();
}

/// Determine whether this constructor is a delegating constructor.
bool isDelegatingConstructor() const {
return (getNumCtorInitializers() == 1) &&
Expand Down Expand Up @@ -2783,34 +2874,39 @@ class CXXDestructorDecl : public CXXMethodDecl {
class CXXConversionDecl : public CXXMethodDecl {
CXXConversionDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
const DeclarationNameInfo &NameInfo, QualType T,
TypeSourceInfo *TInfo, bool isInline,
bool isExplicitSpecified, bool isConstexpr,
SourceLocation EndLocation)
TypeSourceInfo *TInfo, bool isInline, ExplicitSpecifier ES,
bool isConstexpr, SourceLocation EndLocation)
: CXXMethodDecl(CXXConversion, C, RD, StartLoc, NameInfo, T, TInfo,
SC_None, isInline, isConstexpr, EndLocation) {
setExplicitSpecified(isExplicitSpecified);
}

SC_None, isInline, isConstexpr, EndLocation),
ExplicitSpec(ES) {}
void anchor() override;

ExplicitSpecifier ExplicitSpec;

void setExplicitSpecifier(ExplicitSpecifier ES) { ExplicitSpec = ES; }

public:
friend class ASTDeclReader;
friend class ASTDeclWriter;

static CXXConversionDecl *Create(ASTContext &C, CXXRecordDecl *RD,
SourceLocation StartLoc,
const DeclarationNameInfo &NameInfo,
QualType T, TypeSourceInfo *TInfo,
bool isInline, bool isExplicit,
bool isConstexpr,
SourceLocation EndLocation);
static CXXConversionDecl *
Create(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
const DeclarationNameInfo &NameInfo, QualType T, TypeSourceInfo *TInfo,
bool isInline, ExplicitSpecifier ES, bool isConstexpr,
SourceLocation EndLocation);
static CXXConversionDecl *CreateDeserialized(ASTContext &C, unsigned ID);

/// Whether this function is explicit.
bool isExplicit() const {
return getCanonicalDecl()->isExplicitSpecified();
ExplicitSpecifier getExplicitSpecifier() {
return getCanonicalDecl()->ExplicitSpec;
}

const ExplicitSpecifier getExplicitSpecifier() const {
return getCanonicalDecl()->ExplicitSpec;
}

/// Return true if the declartion is already resolved to be explicit.
bool isExplicit() const { return getExplicitSpecifier().isExplicit(); }

/// Returns the type that this conversion function is converting to.
QualType getConversionType() const {
return getType()->getAs<FunctionType>()->getReturnType();
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/ASTMatchers/ASTMatchers.h
Expand Up @@ -6171,6 +6171,9 @@ AST_MATCHER(CXXConstructorDecl, isDelegatingConstructor) {
AST_POLYMORPHIC_MATCHER(isExplicit,
AST_POLYMORPHIC_SUPPORTED_TYPES(CXXConstructorDecl,
CXXConversionDecl)) {
// FIXME : it's not clear whether this should match a dependent
// explicit(....). this matcher should also be able to match
// CXXDeductionGuideDecl with explicit specifier.
return Node.isExplicit();
}

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticCommonKinds.td
Expand Up @@ -150,6 +150,8 @@ def ext_warn_duplicate_declspec : ExtWarn<"%sub{duplicate_declspec}0">,
def warn_duplicate_declspec : Warning<"%sub{duplicate_declspec}0">,
InGroup<DuplicateDeclSpecifier>;

def err_duplicate_declspec : Error<"%sub{duplicate_declspec}0">;

def err_friend_decl_spec : Error<"'%0' is invalid in friend declarations">;

def err_invalid_member_in_interface : Error<
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Expand Up @@ -33,6 +33,10 @@ def err_asm_goto_not_supported_yet : Error<

let CategoryName = "Parse Issue" in {

def warn_cxx2a_compat_explicit_bool : Warning<
"this expression will be parsed as explicit(bool) in C++2a">,
InGroup<CXX2aCompat>, DefaultIgnore;

def ext_empty_translation_unit : Extension<
"ISO C requires a translation unit to contain at least one declaration">,
InGroup<DiagGroup<"empty-translation-unit">>;
Expand Down
13 changes: 8 additions & 5 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -83,11 +83,11 @@ def err_typecheck_converted_constant_expression_indirect : Error<
"bind reference to a temporary">;
def err_expr_not_cce : Error<
"%select{case value|enumerator value|non-type template argument|"
"array size|constexpr if condition}0 "
"array size|constexpr if condition|explicit specifier argument}0 "
"is not a constant expression">;
def ext_cce_narrowing : ExtWarn<
"%select{case value|enumerator value|non-type template argument|"
"array size|constexpr if condition}0 "
"array size|constexpr if condition|explicit specifier argument}0 "
"%select{cannot be narrowed from type %2 to %3|"
"evaluates to %2, which cannot be narrowed to type %3}1">,
InGroup<CXX11Narrowing>, DefaultError, SFINAEFailure;
Expand Down Expand Up @@ -2115,9 +2115,8 @@ def err_deduction_guide_wrong_scope : Error<
"deduction guide must be declared in the same scope as template %q0">;
def err_deduction_guide_defines_function : Error<
"deduction guide cannot have a function definition">;
def err_deduction_guide_explicit_mismatch : Error<
"deduction guide is %select{not |}0declared 'explicit' but "
"previous declaration was%select{ not|}0">;
def err_deduction_guide_redeclared : Error<
"redeclaration of deduction guide">;
def err_deduction_guide_specialized : Error<"deduction guide cannot be "
"%select{explicitly instantiated|explicitly specialized}0">;
def err_deduction_guide_template_not_deducible : Error<
Expand Down Expand Up @@ -3640,6 +3639,10 @@ def note_ovl_candidate : Note<
"| has different qualifiers (expected %5 but found %6)"
"| has different exception specification}4">;

def note_ovl_candidate_explicit_forbidden : Note<
"candidate %0 ignored: cannot be explicit">;
def note_explicit_bool_resolved_to_true : Note<
"explicit(bool) specifier resolved to true">;
def note_ovl_candidate_inherited_constructor : Note<
"constructor from base class %0 inherited here">;
def note_ovl_candidate_inherited_constructor_slice : Note<
Expand Down

0 comments on commit 5fe2ddb

Please sign in to comment.