Skip to content

Commit

Permalink
Further implement defaulting constructors.
Browse files Browse the repository at this point in the history
Focus is on default constructors for the time being. Currently the
exception specification and prototype are processed correctly. Codegen
might work but in all likelihood doesn't.

Note that due to an error, out-of-line defaulting of member functions is
currently impossible. It will continue to that until I muster up the
courage to admit that I secretly pray to epimetheus and that I need to
rework the way default gets from Parse -> Sema.

llvm-svn: 131115
  • Loading branch information
alercah committed May 10, 2011
1 parent be3f9ec commit 6d5b96c
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 152 deletions.
2 changes: 1 addition & 1 deletion clang/include/clang/AST/DeclCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ class CXXRecordDecl : public RecordDecl {
bool needsImplicitDefaultConstructor() const {
return data().NeedsImplicitDefaultConstructor;
}

/// hasConstCopyConstructor - Determines whether this class has a
/// copy constructor that accepts a const-qualified argument.
bool hasConstCopyConstructor(const ASTContext &Context) const;
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -2149,6 +2149,9 @@ def err_redefinition : Error<"redefinition of %0">;
def err_definition_of_implicitly_declared_member : Error<
"definition of implicitly declared %select{constructor|copy constructor|"
"copy assignment operator|destructor}1">;
def err_definition_of_explicitly_defaulted_member : Error<
"definition of explicitly defaulted %select{constructor|copy constructor|"
"copy assignment operator|destructor}0">;
def err_redefinition_extern_inline : Error<
"redefinition of a 'extern inline' function %0 is not supported in "
"%select{C99 mode|C++}1">;
Expand Down Expand Up @@ -3619,6 +3622,12 @@ def warn_not_compound_assign : Warning<
def warn_explicit_conversion_functions : Warning<
"explicit conversion functions are a C++0x extension">, InGroup<CXX0x>;

// C++0x defaulted functions
def err_defaulted_default_ctor_params : Error<
"an explicitly-defaulted default constructor must have no parameters">;
def err_incorrect_defaulted_exception_spec : Error<
"exception specification of explicitly defaulted function is incorrect">;

def warn_array_index_precedes_bounds : Warning<
"array index of '%0' indexes before the beginning of the array">,
InGroup<DiagGroup<"array-bounds">>;
Expand Down
59 changes: 59 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2526,6 +2526,62 @@ class Sema {
/// constructed variable.
void FinalizeVarWithDestructor(VarDecl *VD, const RecordType *DeclInitType);

/// \brief Helper class that collects exception specifications for
/// implicitly-declared special member functions.
class ImplicitExceptionSpecification {
ASTContext &Context;
// We order exception specifications thus:
// noexcept is the most restrictive, but is only used in C++0x.
// throw() comes next.
// Then a throw(collected exceptions)
// Finally no specification.
// throw(...) is used instead if any called function uses it.
ExceptionSpecificationType ComputedEST;
llvm::SmallPtrSet<CanQualType, 4> ExceptionsSeen;
llvm::SmallVector<QualType, 4> Exceptions;

void ClearExceptions() {
ExceptionsSeen.clear();
Exceptions.clear();
}

public:
explicit ImplicitExceptionSpecification(ASTContext &Context)
: Context(Context), ComputedEST(EST_BasicNoexcept) {
if (!Context.getLangOptions().CPlusPlus0x)
ComputedEST = EST_DynamicNone;
}

/// \brief Get the computed exception specification type.
ExceptionSpecificationType getExceptionSpecType() const {
assert(ComputedEST != EST_ComputedNoexcept &&
"noexcept(expr) should not be a possible result");
return ComputedEST;
}

/// \brief The number of exceptions in the exception specification.
unsigned size() const { return Exceptions.size(); }

/// \brief The set of exceptions in the exception specification.
const QualType *data() const { return Exceptions.data(); }

/// \brief Integrate another called method into the collected data.
void CalledDecl(CXXMethodDecl *Method);

FunctionProtoType::ExtProtoInfo getEPI() const {
FunctionProtoType::ExtProtoInfo EPI;
EPI.ExceptionSpecType = getExceptionSpecType();
EPI.NumExceptions = size();
EPI.Exceptions = data();
return EPI;
}
};

/// \brief Determine what sort of exception specification a defaulted
/// constructor of a class will have.
ImplicitExceptionSpecification
ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl);

/// \brief Declare the implicit default constructor for the given class.
///
/// \param ClassDecl The class declaration into which the implicit
Expand Down Expand Up @@ -3199,6 +3255,9 @@ class Sema {
StorageClass& SC);
Decl *ActOnConversionDeclarator(CXXConversionDecl *Conversion);

void CheckExplicitlyDefaultedMethods(CXXRecordDecl *Record);
void CheckExplicitlyDefaultedDefaultConstructor(CXXConstructorDecl *Ctor);

//===--------------------------------------------------------------------===//
// C++ Derived Classes
//
Expand Down
65 changes: 36 additions & 29 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1730,6 +1730,11 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) {
<< New << getSpecialMember(OldMethod);
return true;
}
} else if (OldMethod->isExplicitlyDefaulted()) {
Diag(NewMethod->getLocation(),
diag::err_definition_of_explicitly_defaulted_member)
<< getSpecialMember(OldMethod);
return true;
}
}

Expand Down Expand Up @@ -4137,16 +4142,6 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
/*isImplicitlyDeclared=*/false);

NewFD = NewCD;

if (DefaultLoc.isValid()) {
if (NewCD->isDefaultConstructor() ||
NewCD->isCopyOrMoveConstructor()) {
NewFD->setDefaulted();
NewFD->setExplicitlyDefaulted();
} else {
Diag(DefaultLoc, diag::err_default_special_members);
}
}
} else if (Name.getNameKind() == DeclarationName::CXXDestructorName) {
// This is a C++ destructor declaration.
if (DC->isRecord()) {
Expand All @@ -4159,11 +4154,6 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
isInline,
/*isImplicitlyDeclared=*/false);
isVirtualOkay = true;

if (DefaultLoc.isValid()) {
NewFD->setDefaulted();
NewFD->setExplicitlyDefaulted();
}
} else {
Diag(D.getIdentifierLoc(), diag::err_destructor_not_member);

Expand All @@ -4182,9 +4172,6 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
return 0;
}

if (DefaultLoc.isValid())
Diag(DefaultLoc, diag::err_default_special_members);

CheckConversionDeclarator(D, R, SC);
NewFD = CXXConversionDecl::Create(Context, cast<CXXRecordDecl>(DC),
D.getSourceRange().getBegin(),
Expand Down Expand Up @@ -4232,16 +4219,6 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
NewFD = NewMD;

isVirtualOkay = !isStatic;

if (DefaultLoc.isValid()) {
if (NewMD->isCopyAssignmentOperator() /* ||
NewMD->isMoveAssignmentOperator() */) {
NewFD->setDefaulted();
NewFD->setExplicitlyDefaulted();
} else {
Diag(DefaultLoc, diag::err_default_special_members);
}
}
} else {
if (DefaultLoc.isValid())
Diag(DefaultLoc, diag::err_default_special_members);
Expand Down Expand Up @@ -4715,7 +4692,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,

} else if (!IsFunctionDefinition && D.getCXXScopeSpec().isSet() &&
!isFriend && !isFunctionTemplateSpecialization &&
!isExplicitSpecialization) {
!isExplicitSpecialization && !DefaultLoc.isValid()) {
// An out-of-line member function declaration must also be a
// definition (C++ [dcl.meaning]p1).
// Note that this is not the case for explicit specializations of
Expand Down Expand Up @@ -4800,6 +4777,36 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
}
}

// Check explicitly defaulted methods
// FIXME: This could be made better through CXXSpecialMember if it did
// default constructors (which it should rather than any constructor).
if (NewFD && DefaultLoc.isValid() && getLangOptions().CPlusPlus) {
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewFD)) {
if (CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(MD)) {
if (CD->isDefaultConstructor() || CD->isCopyOrMoveConstructor()) {
CD->setDefaulted();
CD->setExplicitlyDefaulted();
if (CD != CD->getCanonicalDecl() && CD->isDefaultConstructor())
CheckExplicitlyDefaultedDefaultConstructor(CD);
// FIXME: Do copy/move ctors here.
} else {
Diag(DefaultLoc, diag::err_default_special_members);
}
} else if (CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
DD->setDefaulted();
DD->setExplicitlyDefaulted();
// FIXME: Add a checking method
} else if (MD->isCopyAssignmentOperator() /* ||
MD->isMoveAssignmentOperator() */) {
MD->setDefaulted();
MD->setExplicitlyDefaulted();
// FIXME: Add a checking method
} else {
Diag(DefaultLoc, diag::err_default_special_members);
}
}
}

return NewFD;
}

Expand Down

0 comments on commit 6d5b96c

Please sign in to comment.