Parsing, ASTs, and semantic analysis for the declaration of conversion

functions in C++, e.g.,

  struct X {
    operator bool() const;
  };

Note that these conversions don't actually do anything, since we don't
yet have the ability to use them for implicit or explicit conversions.

llvm-svn: 58860
This commit is contained in:
Douglas Gregor 2008-11-07 20:08:42 +00:00
parent cb0df597e0
commit dbc5daf058
14 changed files with 447 additions and 19 deletions

View File

@ -68,6 +68,7 @@ public:
CXXMethod,
CXXConstructor,
CXXDestructor,
CXXConversion,
Var,
ImplicitParam,
CXXClassVar,
@ -91,7 +92,7 @@ public:
TagFirst = Enum , TagLast = CXXRecord,
RecordFirst = Record , RecordLast = CXXRecord,
ValueFirst = EnumConstant , ValueLast = ParmVar,
FunctionFirst = Function , FunctionLast = CXXDestructor,
FunctionFirst = Function , FunctionLast = CXXConversion,
VarFirst = Var , VarLast = ParmVar
};

View File

@ -21,6 +21,7 @@ namespace clang {
class CXXRecordDecl;
class CXXConstructorDecl;
class CXXDestructorDecl;
class CXXConversionDecl;
/// OverloadedFunctionDecl - An instance of this class represents a
/// set of overloaded functions. All of the functions have the same
@ -53,8 +54,9 @@ public:
void addOverload(FunctionDecl *FD) {
assert((!getNumFunctions() || (FD->getDeclContext() == getDeclContext())) &&
"Overloaded functions must all be in the same context");
assert(FD->getIdentifier() == getIdentifier() &&
"Overloaded functions must have the same name.");
assert((FD->getIdentifier() == getIdentifier() ||
isa<CXXConversionDecl>(FD)) &&
"Overloaded functions must have the same name or be conversions.");
Functions.push_back(FD);
}
@ -238,12 +240,18 @@ class CXXRecordDecl : public RecordDecl, public DeclContext {
// Destructor - The destructor of this C++ class.
CXXDestructorDecl *Destructor;
/// Conversions - Overload set containing the conversion functions
/// of this C++ class (but not its inherited conversion
/// functions). Each of the entries in this overload set is a
/// CXXConversionDecl.
OverloadedFunctionDecl Conversions;
CXXRecordDecl(TagKind TK, DeclContext *DC,
SourceLocation L, IdentifierInfo *Id)
: RecordDecl(CXXRecord, TK, DC, L, Id), DeclContext(CXXRecord),
UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false),
Aggregate(true), Polymorphic(false), Bases(0), NumBases(0),
Constructors(DC, Id), Destructor(0) { }
Constructors(DC, Id), Destructor(0), Conversions(DC, Id) { }
~CXXRecordDecl();
@ -321,6 +329,19 @@ public:
this->Destructor = Destructor;
}
/// getConversions - Retrieve the overload set containing all of the
/// conversion functions in this class.
OverloadedFunctionDecl *getConversionFunctions() {
return &Conversions;
}
const OverloadedFunctionDecl *getConversionFunctions() const {
return &Conversions;
}
/// addConversionFunction - Add a new conversion function to the
/// list of conversion functions.
void addConversionFunction(ASTContext &Context, CXXConversionDecl *ConvDecl);
/// isAggregate - Whether this class is an aggregate (C++
/// [dcl.init.aggr]), which is a class with no user-declared
/// constructors, no private or protected non-static data members,
@ -418,7 +439,7 @@ public:
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
return D->getKind() >= CXXMethod && D->getKind() <= CXXConstructor;
return D->getKind() >= CXXMethod && D->getKind() <= CXXConversion;
}
static bool classof(const CXXMethodDecl *D) { return true; }
@ -720,6 +741,60 @@ public:
static CXXDestructorDecl* CreateImpl(llvm::Deserializer& D, ASTContext& C);
};
/// CXXConversionDecl - Represents a C++ conversion function within a
/// class. For example:
///
/// @code
/// class X {
/// public:
/// operator bool();
/// };
/// @endcode
class CXXConversionDecl : public CXXMethodDecl {
/// Explicit - Whether this conversion function is marked
/// "explicit", meaning that it can only be applied when the user
/// explicitly wrote a cast. This is a C++0x feature.
bool Explicit : 1;
CXXConversionDecl(CXXRecordDecl *RD, SourceLocation L,
IdentifierInfo *Id, QualType T,
bool isInline, bool isExplicit)
: CXXMethodDecl(CXXConversion, RD, L, Id, T, false, isInline,
/*PrevDecl=*/0),
Explicit(isExplicit) { }
public:
static CXXConversionDecl *Create(ASTContext &C, CXXRecordDecl *RD,
SourceLocation L, IdentifierInfo *Id,
QualType T, bool isInline,
bool isExplicit);
/// isExplicit - Whether this is an explicit conversion operator
/// (C++0x only). Explicit conversion operators are only considered
/// when the user has explicitly written a cast.
bool isExplicit() const { return Explicit; }
/// getConversionType - Returns the type that this conversion
/// function is converting to.
QualType getConversionType() const {
return getType()->getAsFunctionType()->getResultType();
}
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) {
return D->getKind() == CXXConversion;
}
static bool classof(const CXXConversionDecl *D) { return true; }
/// EmitImpl - Serialize this CXXConversionDecl. Called by Decl::Emit.
// FIXME: Implement this.
//virtual void EmitImpl(llvm::Serializer& S) const;
/// CreateImpl - Deserialize a CXXConversionDecl. Called by Decl::Create.
// FIXME: Implement this.
static CXXConversionDecl* CreateImpl(llvm::Deserializer& D, ASTContext& C);
};
/// CXXClassVarDecl - Represents a static data member of a struct/union/class.
class CXXClassVarDecl : public VarDecl {

View File

@ -1309,6 +1309,31 @@ DIAG(err_operator_overload_must_be_member, ERROR,
"overloaded operator '%0' must be a non-static member function")
DIAG(err_operator_overload_post_incdec_must_be_int, ERROR,
"%0parameter of overloaded post-%1 operator must have type 'int' (not '%2')")
DIAG(err_operator_missing_type_specifier, ERROR,
"missing type specifier after 'operator'")
// C++ conversion functions
DIAG(err_conv_function_not_member, ERROR,
"conversion function must be a non-static member function")
DIAG(err_conv_function_return_type, ERROR,
"conversion function cannot have a return type")
DIAG(err_conv_function_with_params, ERROR,
"conversion function cannot have any parameters")
DIAG(err_conv_function_variadic, ERROR,
"conversion function cannot be variadic")
DIAG(err_conv_function_to_array, ERROR,
"conversion function cannot convert to an array type")
DIAG(err_conv_function_to_function, ERROR,
"conversion function cannot convert to a function type")
DIAG(err_conv_function_redeclared, ERROR,
"conversion function cannot be redeclared")
DIAG(warn_conv_to_self_not_used, WARNING,
"conversion function converting '%0' to itself will never be used")
DIAG(warn_conv_to_base_not_used, WARNING,
"conversion function converting '%0' to its base class '%1' will never be used")
DIAG(warn_conv_to_void_not_used, WARNING,
"conversion function converting '%0' to '%1' will never be used")
DIAG(warn_not_compound_assign, WARNING,
"use of unary operator that may be intended as compound assignment (%0=)")

View File

@ -42,9 +42,10 @@ namespace clang {
/// the parser has just done or is about to do when the method is called. They
/// are not requests that the actions module do the specified action.
///
/// All of the methods here are optional except isTypeName(), which must be
/// specified in order for the parse to complete accurately. The MinimalAction
/// class does this bare-minimum of tracking to implement this functionality.
/// All of the methods here are optional except isTypeName() and
/// isCurrentClassName(), which must be specified in order for the
/// parse to complete accurately. The MinimalAction class does this
/// bare-minimum of tracking to implement this functionality.
class Action {
public:
/// Out-of-line virtual destructor to provide home for this class.
@ -108,6 +109,14 @@ public:
/// name of the innermost C++ class type currently being defined.
virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S) = 0;
/// getTypeAsString - Returns a string that describes the given
/// type. This callback is used in C++ to form identifiers for
/// special declarations that otherwise don't have simple names,
/// such as constructors, destructors, and conversion functions.
virtual std::string getTypeAsString(TypeTy *Type) {
return "<unknown type>";
}
/// ActOnDeclarator - This callback is invoked when a declarator is parsed and
/// 'Init' specifies the initializer if any. This is for things like:
/// "int X = 4" or "typedef int foo".

View File

@ -610,7 +610,9 @@ public:
DK_Abstract, // An abstract declarator (has no identifier)
DK_Normal, // A normal declarator (has an identifier).
DK_Constructor, // A C++ constructor (identifier is the class name)
DK_Destructor // A C++ destructor (has no identifier)
DK_Destructor, // A C++ destructor (identifier is ~class name)
DK_Conversion // A C++ conversion function (identifier is
// "operator " then the type name)
};
private:
@ -639,8 +641,9 @@ private:
/// AsmLabel - The asm label, if specified.
Action::ExprTy *AsmLabel;
// When Kind is DK_Constructor or DK_Destructor, the type associated
// with the constructor or destructor.
// When Kind is DK_Constructor, DK_Destructor, or DK_Conversion, the
// type associated with the constructor, destructor, or conversion
// operator.
Action::TypeTy *Type;
public:
@ -755,6 +758,16 @@ public:
Type = Ty;
}
// SetConversionFunction - Set this declarator to be a C++
// conversion function declarator.
void SetConversionFunction(Action::TypeTy *Ty, IdentifierInfo *ID,
SourceLocation Loc) {
Identifier = ID;
IdentifierLoc = Loc;
Kind = DK_Conversion;
Type = Ty;
}
void AddTypeInfo(const DeclaratorChunk &TI) {
DeclTypeInfo.push_back(TI);
}

View File

@ -475,6 +475,8 @@ private:
/// simple-type-specifier.
void ParseCXXSimpleTypeSpecifier(DeclSpec &DS);
bool ParseCXXTypeSpecifierSeq(DeclSpec &DS);
//===--------------------------------------------------------------------===//
// C++ if/switch/while/for condition expression.
ExprResult ParseCXXCondition();
@ -701,7 +703,7 @@ private:
/// ParseDeclarator - Parse and verify a newly-initialized declarator.
void ParseDeclarator(Declarator &D);
void ParseDeclaratorInternal(Declarator &D);
void ParseDeclaratorInternal(Declarator &D, bool PtrOperator = false);
void ParseTypeQualifierListOpt(DeclSpec &DS);
void ParseDirectDeclarator(Declarator &D);
void ParseParenDeclarator(Declarator &D);
@ -737,6 +739,7 @@ private:
//===--------------------------------------------------------------------===//
// C++ 13.5: Overloaded operators [over.oper]
IdentifierInfo *MaybeParseOperatorFunctionId();
TypeTy *ParseConversionFunctionId();
};
} // end namespace clang

View File

@ -239,6 +239,7 @@ void Decl::addDeclKind(Kind k) {
case CXXMethod:
case CXXConstructor:
case CXXDestructor:
case CXXConversion:
case CXXClassVar:
break;
}

View File

@ -48,6 +48,11 @@ void CXXRecordDecl::Destroy(ASTContext &C) {
if (isDefinition())
Destructor->Destroy(C);
for (OverloadedFunctionDecl::function_iterator func
= Conversions.function_begin();
func != Conversions.function_end(); ++func)
(*func)->Destroy(C);
RecordDecl::Destroy(C);
}
@ -101,6 +106,11 @@ CXXRecordDecl::addConstructor(ASTContext &Context,
Constructors.addOverload(ConDecl);
}
void CXXRecordDecl::addConversionFunction(ASTContext &Context,
CXXConversionDecl *ConvDecl) {
Conversions.addOverload(ConvDecl);
}
CXXMethodDecl *
CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD,
SourceLocation L, IdentifierInfo *Id,
@ -232,6 +242,14 @@ CXXDestructorDecl::Create(ASTContext &C, CXXRecordDecl *RD,
isImplicitlyDeclared);
}
CXXConversionDecl *
CXXConversionDecl::Create(ASTContext &C, CXXRecordDecl *RD,
SourceLocation L, IdentifierInfo *Id,
QualType T, bool isInline, bool isExplicit) {
void *Mem = C.getAllocator().Allocate<CXXConversionDecl>();
return new (Mem) CXXConversionDecl(RD, L, Id, T, isInline, isExplicit);
}
CXXClassVarDecl *CXXClassVarDecl::Create(ASTContext &C, CXXRecordDecl *RD,
SourceLocation L, IdentifierInfo *Id,
QualType T, ScopedDecl *PrevDecl) {

View File

@ -1229,7 +1229,11 @@ void Parser::ParseDeclarator(Declarator &D) {
ParseDeclaratorInternal(D);
}
/// ParseDeclaratorInternal
/// ParseDeclaratorInternal - Parse a C or C++ declarator. If
/// PtrOperator is true, then this routine won't parse the final
/// direct-declarator; therefore, it effectively parses the C++
/// ptr-operator production.
///
/// declarator: [C99 6.7.5]
/// pointer[opt] direct-declarator
/// [C++] '&' declarator [C++ 8p4, dcl.decl]
@ -1239,13 +1243,21 @@ void Parser::ParseDeclarator(Declarator &D) {
/// '*' type-qualifier-list[opt]
/// '*' type-qualifier-list[opt] pointer
///
void Parser::ParseDeclaratorInternal(Declarator &D) {
/// ptr-operator:
/// '*' cv-qualifier-seq[opt]
/// '&'
/// [GNU] '&' restrict[opt] attributes[opt]
/// '::'[opt] nested-name-specifier '*' cv-qualifier-seq[opt] [TODO]
void Parser::ParseDeclaratorInternal(Declarator &D, bool PtrOperator) {
tok::TokenKind Kind = Tok.getKind();
// Not a pointer, C++ reference, or block.
if (Kind != tok::star && (Kind != tok::amp || !getLang().CPlusPlus) &&
(Kind != tok::caret || !getLang().Blocks))
return ParseDirectDeclarator(D);
(Kind != tok::caret || !getLang().Blocks)) {
if (!PtrOperator)
ParseDirectDeclarator(D);
return;
}
// Otherwise, '*' -> pointer, '^' -> block, '&' -> reference.
SourceLocation Loc = ConsumeToken(); // Eat the * or &.
@ -1257,7 +1269,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D) {
ParseTypeQualifierListOpt(DS);
// Recursively parse the declarator.
ParseDeclaratorInternal(D);
ParseDeclaratorInternal(D, PtrOperator);
if (Kind == tok::star)
// Remember that we parsed a pointer type, and remember the type-quals.
D.AddTypeInfo(DeclaratorChunk::getPointer(DS.getTypeQualifiers(), Loc,
@ -1290,7 +1302,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D) {
}
// Recursively parse the declarator.
ParseDeclaratorInternal(D);
ParseDeclaratorInternal(D, PtrOperator);
if (D.getNumTypeObjects() > 0) {
// C++ [dcl.ref]p4: There shall be no references to references.
@ -1382,7 +1394,13 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
if (IdentifierInfo *II = MaybeParseOperatorFunctionId()) {
D.SetIdentifier(II, OperatorLoc);
} else {
// This must be a user-defined conversion.
// This must be a conversion function (C++ [class.conv.fct]).
if (TypeTy *ConvType = ParseConversionFunctionId()) {
IdentifierInfo *II
= &PP.getIdentifierTable().get(std::string("operator ") +
Actions.getTypeAsString(ConvType));
D.SetConversionFunction(ConvType, II, OperatorLoc);
}
}
} else if (Tok.is(tok::l_paren)) {
// direct-declarator: '(' declarator ')'

View File

@ -292,6 +292,32 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
DS.Finish(Diags, PP.getSourceManager(), getLang());
}
/// ParseCXXTypeSpecifierSeq - Parse a C++ type-specifier-seq (C++
/// [dcl.name]), which is a non-empty sequence of type-specifiers,
/// e.g., "const short int". Note that the DeclSpec is *not* finished
/// by parsing the type-specifier-seq, because these sequences are
/// typically followed by some form of declarator. Returns true and
/// emits diagnostics if this is not a type-specifier-seq, false
/// otherwise.
///
/// type-specifier-seq: [C++ 8.1]
/// type-specifier type-specifier-seq[opt]
///
bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) {
DS.SetRangeStart(Tok.getLocation());
const char *PrevSpec = 0;
int isInvalid = 0;
// Parse one or more of the type specifiers.
if (!MaybeParseTypeSpecifier(DS, isInvalid, PrevSpec)) {
Diag(Tok.getLocation(), diag::err_operator_missing_type_specifier);
return true;
}
while (MaybeParseTypeSpecifier(DS, isInvalid, PrevSpec));
return false;
}
/// MaybeParseOperatorFunctionId - Attempts to parse a C++ overloaded
/// operator name (C++ [over.oper]). If successful, returns the
/// predefined identifier that corresponds to that overloaded
@ -365,3 +391,38 @@ IdentifierInfo *Parser::MaybeParseOperatorFunctionId() {
return &PP.getIdentifierTable().getOverloadedOperator(Op);
}
}
/// ParseConversionFunctionId - Parse a C++ conversion-function-id,
/// which expresses the name of a user-defined conversion operator
/// (C++ [class.conv.fct]p1). Returns the type that this operator is
/// specifying a conversion for, or NULL if there was an error.
///
/// conversion-function-id: [C++ 12.3.2]
/// operator conversion-type-id
///
/// conversion-type-id:
/// type-specifier-seq conversion-declarator[opt]
///
/// conversion-declarator:
/// ptr-operator conversion-declarator[opt]
Parser::TypeTy *Parser::ParseConversionFunctionId() {
assert(Tok.is(tok::kw_operator) && "Expected 'operator' keyword");
ConsumeToken(); // 'operator'
// Parse the type-specifier-seq.
DeclSpec DS;
if (ParseCXXTypeSpecifierSeq(DS))
return 0;
// Parse the conversion-declarator, which is merely a sequence of
// ptr-operators.
Declarator D(DS, Declarator::TypeNameContext);
ParseDeclaratorInternal(D, /*PtrOperator=*/true);
// Finish up the type.
Action::TypeResult Result = Actions.ActOnTypeName(CurScope, D);
if (Result.isInvalid)
return 0;
else
return Result.Val;
}

View File

@ -271,6 +271,7 @@ private:
// Symbol table / Decl tracking callbacks: SemaDecl.cpp.
//
virtual TypeTy *isTypeName(const IdentifierInfo &II, Scope *S);
virtual std::string getTypeAsString(TypeTy *Type);
virtual DeclTy *ActOnDeclarator(Scope *S, Declarator &D, DeclTy *LastInGroup);
virtual DeclTy *ActOnParamDeclarator(Scope *S, Declarator &D);
virtual void ActOnParamDefaultArgument(DeclTy *param,
@ -845,8 +846,11 @@ public:
FunctionDecl::StorageClass& SC);
bool CheckDestructorDeclarator(Declarator &D, QualType &R,
FunctionDecl::StorageClass& SC);
bool CheckConversionDeclarator(Declarator &D, QualType &R,
FunctionDecl::StorageClass& SC);
DeclTy *ActOnConstructorDeclarator(CXXConstructorDecl *Constructor);
DeclTy *ActOnDestructorDeclarator(CXXDestructorDecl *Destructor);
DeclTy *ActOnConversionDeclarator(CXXConversionDecl *Conversion);
//===--------------------------------------------------------------------===//
// C++ Derived Classes

View File

@ -38,6 +38,11 @@ Sema::TypeTy *Sema::isTypeName(const IdentifierInfo &II, Scope *S) {
return 0;
}
std::string Sema::getTypeAsString(TypeTy *Type) {
QualType Ty = QualType::getFromOpaquePtr(Type);
return Ty.getAsString();
}
DeclContext *Sema::getDCParent(DeclContext *DC) {
// If CurContext is a ObjC method, getParent() will return NULL.
if (isa<ObjCMethodDecl>(DC))
@ -835,6 +840,22 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) {
if (isInvalidDecl)
NewFD->setInvalidDecl();
} else if (D.getKind() == Declarator::DK_Conversion) {
if (D.getContext() != Declarator::MemberContext) {
Diag(D.getIdentifierLoc(),
diag::err_conv_function_not_member);
return 0;
} else {
bool isInvalidDecl = CheckConversionDeclarator(D, R, SC);
NewFD = CXXConversionDecl::Create(Context,
cast<CXXRecordDecl>(CurContext),
D.getIdentifierLoc(), II, R,
isInline, isExplicit);
if (isInvalidDecl)
NewFD->setInvalidDecl();
}
} else if (D.getContext() == Declarator::MemberContext) {
// This is a C++ method declaration.
NewFD = CXXMethodDecl::Create(Context, cast<CXXRecordDecl>(CurContext),
@ -931,6 +952,8 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl) {
return ActOnConstructorDeclarator(Constructor);
else if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(NewFD))
return ActOnDestructorDeclarator(Destructor);
else if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(NewFD))
return ActOnConversionDeclarator(Conversion);
// Extra checking for C++ overloaded operators (C++ [over.oper]).
if (NewFD->isOverloadedOperator() &&

View File

@ -1004,6 +1004,82 @@ bool Sema::CheckDestructorDeclarator(Declarator &D, QualType &R,
return isInvalid;
}
/// CheckConversionDeclarator - Called by ActOnDeclarator to check the
/// well-formednes of the conversion function declarator @p D with
/// type @p R. If there are any errors in the declarator, this routine
/// will emit diagnostics and return true. Otherwise, it will return
/// false. Either way, the type @p R will be updated to reflect a
/// well-formed type for the conversion operator.
bool Sema::CheckConversionDeclarator(Declarator &D, QualType &R,
FunctionDecl::StorageClass& SC) {
bool isInvalid = false;
// C++ [class.conv.fct]p1:
// Neither parameter types nor return type can be specified. The
// type of a conversion function (8.3.5) is “function taking no
// parameter returning conversion-type-id.”
if (SC == FunctionDecl::Static) {
Diag(D.getIdentifierLoc(),
diag::err_conv_function_not_member,
"static",
SourceRange(D.getDeclSpec().getStorageClassSpecLoc()),
SourceRange(D.getIdentifierLoc()));
isInvalid = true;
SC = FunctionDecl::None;
}
if (D.getDeclSpec().hasTypeSpecifier()) {
// Conversion functions don't have return types, but the parser will
// happily parse something like:
//
// class X {
// float operator bool();
// };
//
// The return type will be changed later anyway.
Diag(D.getIdentifierLoc(),
diag::err_conv_function_return_type,
SourceRange(D.getDeclSpec().getTypeSpecTypeLoc()),
SourceRange(D.getIdentifierLoc()));
}
// Make sure we don't have any parameters.
if (R->getAsFunctionTypeProto()->getNumArgs() > 0) {
Diag(D.getIdentifierLoc(), diag::err_conv_function_with_params);
// Delete the parameters.
DeclaratorChunk::FunctionTypeInfo &FTI = D.getTypeObject(0).Fun;
if (FTI.NumArgs) {
delete [] FTI.ArgInfo;
FTI.NumArgs = 0;
FTI.ArgInfo = 0;
}
}
// Make sure the conversion function isn't variadic.
if (R->getAsFunctionTypeProto()->isVariadic())
Diag(D.getIdentifierLoc(), diag::err_conv_function_variadic);
// C++ [class.conv.fct]p4:
// The conversion-type-id shall not represent a function type nor
// an array type.
QualType ConvType = QualType::getFromOpaquePtr(D.getDeclaratorIdType());
if (ConvType->isArrayType()) {
Diag(D.getIdentifierLoc(), diag::err_conv_function_to_array);
ConvType = Context.getPointerType(ConvType);
} else if (ConvType->isFunctionType()) {
Diag(D.getIdentifierLoc(), diag::err_conv_function_to_function);
ConvType = Context.getPointerType(ConvType);
}
// Rebuild the function type "R" without any parameters (in case any
// of the errors above fired) and with the conversion type as the
// return type.
R = Context.getFunctionType(ConvType, 0, 0, false,
R->getAsFunctionTypeProto()->getTypeQuals());
return isInvalid;
}
/// ActOnConstructorDeclarator - Called by ActOnDeclarator to complete
/// the declaration of the given C++ constructor ConDecl that was
/// built from declarator D. This routine is responsible for checking
@ -1092,6 +1168,65 @@ Sema::DeclTy *Sema::ActOnDestructorDeclarator(CXXDestructorDecl *Destructor) {
return (DeclTy *)Destructor;
}
/// ActOnConversionDeclarator - Called by ActOnDeclarator to complete
/// the declaration of the given C++ conversion function. This routine
/// is responsible for recording the conversion function in the C++
/// class, if possible.
Sema::DeclTy *Sema::ActOnConversionDeclarator(CXXConversionDecl *Conversion) {
assert(Conversion && "Expected to receive a conversion function declaration");
CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(CurContext);
// Make sure we aren't redeclaring the conversion function.
QualType ConvType = Context.getCanonicalType(Conversion->getConversionType());
OverloadedFunctionDecl *Conversions = ClassDecl->getConversionFunctions();
for (OverloadedFunctionDecl::function_iterator Func
= Conversions->function_begin();
Func != Conversions->function_end(); ++Func) {
CXXConversionDecl *OtherConv = cast<CXXConversionDecl>(*Func);
if (ConvType == Context.getCanonicalType(OtherConv->getConversionType())) {
Diag(Conversion->getLocation(), diag::err_conv_function_redeclared);
Diag(OtherConv->getLocation(),
OtherConv->isThisDeclarationADefinition()?
diag::err_previous_definition
: diag::err_previous_declaration);
Conversion->setInvalidDecl();
return (DeclTy *)Conversion;
}
}
// C++ [class.conv.fct]p1:
// [...] A conversion function is never used to convert a
// (possibly cv-qualified) object to the (possibly cv-qualified)
// same object type (or a reference to it), to a (possibly
// cv-qualified) base class of that type (or a reference to it),
// or to (possibly cv-qualified) void.
// FIXME: Suppress this warning if the conversion function ends up
// being a virtual function that overrides a virtual function in a
// base class.
QualType ClassType
= Context.getCanonicalType(Context.getTypeDeclType(ClassDecl));
if (const ReferenceType *ConvTypeRef = ConvType->getAsReferenceType())
ConvType = ConvTypeRef->getPointeeType();
if (ConvType->isRecordType()) {
ConvType = Context.getCanonicalType(ConvType).getUnqualifiedType();
if (ConvType == ClassType)
Diag(Conversion->getLocation(), diag::warn_conv_to_self_not_used,
ClassType.getAsString());
else if (IsDerivedFrom(ClassType, ConvType))
Diag(Conversion->getLocation(), diag::warn_conv_to_base_not_used,
ClassType.getAsString(),
ConvType.getAsString());
} else if (ConvType->isVoidType()) {
Diag(Conversion->getLocation(), diag::warn_conv_to_void_not_used,
ClassType.getAsString(), ConvType.getAsString());
}
ClassDecl->addConversionFunction(Context, Conversion);
return (DeclTy *)Conversion;
}
//===----------------------------------------------------------------------===//
// Namespace Handling
//===----------------------------------------------------------------------===//

View File

@ -0,0 +1,42 @@
// RUN: clang -fsyntax-only -verify %s
class X {
public:
operator bool();
operator int() const;
};
operator int(); // expected-error{{conversion function must be a non-static member function}}
typedef int func_type(int);
typedef int array_type[10];
class Y {
public:
void operator bool(int, ...) const; // expected-error{{conversion function cannot have a return type}} \
// expected-error{{conversion function cannot have any parameters}} \
// expected-error{{conversion function cannot be variadic}}
operator func_type(); // expected-error{{conversion function cannot convert to a function type}}
operator array_type(); // expected-error{{conversion function cannot convert to an array type}}
};
typedef int INT;
typedef INT* INT_PTR;
class Z {
operator int(); // expected-error{{previous declaration is here}}
operator int**(); // expected-error{{previous declaration is here}}
operator INT(); // expected-error{{conversion function cannot be redeclared}}
operator INT_PTR*(); // expected-error{{conversion function cannot be redeclared}}
};
class A { };
class B : public A {
public:
operator A&() const; // expected-warning{{conversion function converting 'class B' to its base class 'class A' will never be used}}
operator const void() const; // expected-warning{{conversion function converting 'class B' to 'void const' will never be used}}
operator const B(); // expected-warning{{conversion function converting 'class B' to itself will never be used}}
};