Implement dereferencing of pointers-to-member.

llvm-svn: 63983
This commit is contained in:
Sebastian Redl 2009-02-07 00:15:38 +00:00
parent ad41b83816
commit 112a976616
11 changed files with 164 additions and 36 deletions

View File

@ -1085,6 +1085,7 @@ public:
enum Opcode {
// Operators listed in order of precedence.
// Note that additions to this should also update the StmtVisitor class.
PtrMemD, PtrMemI, // [C++ 5.5] Pointer-to-member operators.
Mul, Div, Rem, // [C99 6.5.5] Multiplicative operators.
Add, Sub, // [C99 6.5.6] Additive operators.
Shl, Shr, // [C99 6.5.7] Bitwise shift operators.

View File

@ -35,6 +35,8 @@ public:
if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(S)) {
switch (BinOp->getOpcode()) {
default: assert(0 && "Unknown binary operator!");
case BinaryOperator::PtrMemD: DISPATCH(BinPtrMemD, BinaryOperator);
case BinaryOperator::PtrMemI: DISPATCH(BinPtrMemI, BinaryOperator);
case BinaryOperator::Mul: DISPATCH(BinMul, BinaryOperator);
case BinaryOperator::Div: DISPATCH(BinDiv, BinaryOperator);
case BinaryOperator::Rem: DISPATCH(BinRem, BinaryOperator);
@ -95,7 +97,7 @@ public:
case UnaryOperator::Imag: DISPATCH(UnaryImag, UnaryOperator);
case UnaryOperator::Extension: DISPATCH(UnaryExtension, UnaryOperator);
case UnaryOperator::OffsetOf: DISPATCH(UnaryOffsetOf, UnaryOperator);
}
}
}
// Top switch stmt: dispatch to VisitFooStmt for each FooStmt.
@ -119,6 +121,7 @@ public:
RetTy VisitBin ## NAME(BinaryOperator *S) { \
DISPATCH(BinaryOperator, BinaryOperator); \
}
BINOP_FALLBACK(PtrMemD) BINOP_FALLBACK(PtrMemI)
BINOP_FALLBACK(Mul) BINOP_FALLBACK(Div) BINOP_FALLBACK(Rem)
BINOP_FALLBACK(Add) BINOP_FALLBACK(Sub) BINOP_FALLBACK(Shl)
BINOP_FALLBACK(Shr)

View File

@ -863,6 +863,12 @@ DIAG(err_qualified_catch_declarator, ERROR,
"exception declarator cannot be qualified")
DIAG(err_early_catch_all, ERROR,
"catch-all handler must come last")
DIAG(err_bad_memptr_rhs, ERROR,
"right hand operand to %0 must be a pointer to member of a complete class "
"but is %1")
DIAG(err_bad_memptr_lhs, ERROR,
"left hand operand to ->* must be a %select{|pointer to }0class "
"compatible with the right hand operand, but is %1")
DIAG(err_invalid_use_of_function_type, ERROR,
"a function type is not allowed here")

View File

@ -496,6 +496,12 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
BinOp->getOpcode() == BinaryOperator::Comma)
return BinOp->getRHS()->isLvalue(Ctx);
// C++ [expr.mptr.oper]p6
if ((BinOp->getOpcode() == BinaryOperator::PtrMemD ||
BinOp->getOpcode() == BinaryOperator::PtrMemI) &&
!BinOp->getType()->isFunctionType())
return BinOp->getLHS()->isLvalue(Ctx);
if (!BinOp->isAssignmentOp())
return LV_InvalidExpression;

View File

@ -33,20 +33,21 @@ using namespace clang;
/// productions. Low precedences numbers bind more weakly than high numbers.
namespace prec {
enum Level {
Unknown = 0, // Not binary operator.
Comma = 1, // ,
Assignment = 2, // =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=
Conditional = 3, // ?
LogicalOr = 4, // ||
LogicalAnd = 5, // &&
InclusiveOr = 6, // |
ExclusiveOr = 7, // ^
And = 8, // &
Equality = 9, // ==, !=
Relational = 10, // >=, <=, >, <
Shift = 11, // <<, >>
Additive = 12, // -, +
Multiplicative = 13 // *, /, %
Unknown = 0, // Not binary operator.
Comma = 1, // ,
Assignment = 2, // =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=
Conditional = 3, // ?
LogicalOr = 4, // ||
LogicalAnd = 5, // &&
InclusiveOr = 6, // |
ExclusiveOr = 7, // ^
And = 8, // &
Equality = 9, // ==, !=
Relational = 10, // >=, <=, >, <
Shift = 11, // <<, >>
Additive = 12, // -, +
Multiplicative = 13, // *, /, %
PointerToMember = 14 // .*, ->*
};
}
@ -88,6 +89,8 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind) {
case tok::percent:
case tok::slash:
case tok::star: return prec::Multiplicative;
case tok::periodstar:
case tok::arrowstar: return prec::PointerToMember;
}
}
@ -104,7 +107,13 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind) {
/// consistency, we parse the LHS as a conditional-expression, then check for
/// l-value-ness in semantic analysis stages.
///
/// pm-expression: [C++ 5.5]
/// cast-expression
/// pm-expression '.*' cast-expression
/// pm-expression '->*' cast-expression
///
/// multiplicative-expression: [C99 6.5.5]
/// Note: in C++, apply pm-expression instead of cast-expression
/// cast-expression
/// multiplicative-expression '*' cast-expression
/// multiplicative-expression '/' cast-expression
@ -270,7 +279,7 @@ Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) {
// Consume the operator, saving the operator token for error reporting.
Token OpToken = Tok;
ConsumeToken();
// Special case handling for the ternary operator.
OwningExprResult TernaryMiddle(Actions, true);
if (NextTokPrec == prec::Conditional) {

View File

@ -1788,16 +1788,18 @@ public:
bool PerformImplicitConversion(Expr *&From, QualType ToType,
const StandardConversionSequence& SCS,
const char *Flavor);
/// the following "Check" methods will return a valid/converted QualType
/// or a null QualType (indicating an error diagnostic was issued).
/// type checking binary operators (subroutines of ActOnBinOp).
/// type checking binary operators (subroutines of CreateBuiltinBinOp).
inline QualType InvalidOperands(SourceLocation l, Expr *&lex, Expr *&rex);
inline QualType CheckPointerToMemberOperands( // C++ 5.5
Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isIndirect);
inline QualType CheckMultiplyDivideOperands( // C99 6.5.5
Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false);
Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false);
inline QualType CheckRemainderOperands( // C99 6.5.5
Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false);
Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false);
inline QualType CheckAdditionOperands( // C99 6.5.6
Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false);
inline QualType CheckSubtractionOperands( // C99 6.5.6
@ -1807,7 +1809,7 @@ public:
inline QualType CheckCompareOperands( // C99 6.5.8/9
Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isRelational);
inline QualType CheckBitwiseOperands( // C99 6.5.[10...12]
Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false);
Expr *&lex, Expr *&rex, SourceLocation OpLoc, bool isCompAssign = false);
inline QualType CheckLogicalOperands( // C99 6.5.[13,14]
Expr *&lex, Expr *&rex, SourceLocation OpLoc);
// CheckAssignmentOperands is used for both simple and compound assignment.

View File

@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
#include "SemaInherit.h"
#include "Sema.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
@ -2701,7 +2702,73 @@ inline QualType Sema::CheckVectorOperands(SourceLocation Loc, Expr *&lex,
<< lex->getType() << rex->getType()
<< lex->getSourceRange() << rex->getSourceRange();
return QualType();
}
}
inline QualType Sema::CheckPointerToMemberOperands(
Expr *&lex, Expr *&rex, SourceLocation Loc, bool isIndirect)
{
const char *OpSpelling = isIndirect ? "->*" : ".*";
// C++ 5.5p2
// The binary operator .* [p3: ->*] binds its second operand, which shall
// be of type "pointer to member of T" (where T is a completely-defined
// class type) [...]
QualType RType = rex->getType();
const MemberPointerType *MemPtr = RType->getAsMemberPointerType();
if (!MemPtr || MemPtr->getClass()->isIncompleteType()) {
Diag(Loc, diag::err_bad_memptr_rhs)
<< OpSpelling << RType << rex->getSourceRange();
return QualType();
}
QualType Class(MemPtr->getClass(), 0);
// C++ 5.5p2
// [...] to its first operand, which shall be of class T or of a class of
// which T is an unambiguous and accessible base class. [p3: a pointer to
// such a class]
QualType LType = lex->getType();
if (isIndirect) {
if (const PointerType *Ptr = LType->getAsPointerType())
LType = Ptr->getPointeeType().getNonReferenceType();
else {
Diag(Loc, diag::err_bad_memptr_lhs)
<< 1 << LType << lex->getSourceRange();
return QualType();
}
}
if (Context.getCanonicalType(Class).getUnqualifiedType() !=
Context.getCanonicalType(LType).getUnqualifiedType()) {
BasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/false,
/*DetectVirtual=*/false);
// FIXME: Would it be useful to print full ambiguity paths,
// or is that overkill?
if (!IsDerivedFrom(LType, Class, Paths) ||
Paths.isAmbiguous(Context.getCanonicalType(Class))) {
Diag(Loc, diag::err_bad_memptr_lhs)
<< (int)isIndirect << lex->getType() << lex->getSourceRange();
return QualType();
}
}
// C++ 5.5p2
// The result is an object or a function of the type specified by the
// second operand.
// The cv qualifiers are the union of those in the pointer and the left side,
// in accordance with 5.5p5 and 5.2.5.
// FIXME: This returns a dereferenced member function pointer as a normal
// function type. However, the only operation valid on such functions is
// calling them. There's also a GCC extension to get a function pointer to
// the thing, which is another complication, because this type - unlike the
// type that is the result of this expression - takes the class as the first
// argument.
// We probably need a "MemberFunctionClosureType" or something like that.
QualType Result = MemPtr->getPointeeType();
if (LType.isConstQualified())
Result.addConst();
if (LType.isVolatileQualified())
Result.addVolatile();
return Result;
}
inline QualType Sema::CheckMultiplyDivideOperands(
Expr *&lex, Expr *&rex, SourceLocation Loc, bool isCompAssign)
@ -3535,6 +3602,8 @@ static inline BinaryOperator::Opcode ConvertTokenKindToBinaryOpcode(
BinaryOperator::Opcode Opc;
switch (Kind) {
default: assert(0 && "Unknown binop!");
case tok::periodstar: Opc = BinaryOperator::PtrMemD; break;
case tok::arrowstar: Opc = BinaryOperator::PtrMemI; break;
case tok::star: Opc = BinaryOperator::Mul; break;
case tok::slash: Opc = BinaryOperator::Div; break;
case tok::percent: Opc = BinaryOperator::Rem; break;
@ -3605,7 +3674,12 @@ Action::OwningExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
case BinaryOperator::Assign:
ResultTy = CheckAssignmentOperands(lhs, rhs, OpLoc, QualType());
break;
case BinaryOperator::Mul:
case BinaryOperator::PtrMemD:
case BinaryOperator::PtrMemI:
ResultTy = CheckPointerToMemberOperands(lhs, rhs, OpLoc,
Opc == BinaryOperator::PtrMemI);
break;
case BinaryOperator::Mul:
case BinaryOperator::Div:
ResultTy = CheckMultiplyDivideOperands(lhs, rhs, OpLoc);
break;
@ -3618,7 +3692,7 @@ Action::OwningExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
case BinaryOperator::Sub:
ResultTy = CheckSubtractionOperands(lhs, rhs, OpLoc);
break;
case BinaryOperator::Shl:
case BinaryOperator::Shl:
case BinaryOperator::Shr:
ResultTy = CheckShiftOperands(lhs, rhs, OpLoc);
break;
@ -3707,11 +3781,11 @@ Action::OwningExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
Context.DependentTy,
Context.DependentTy, TokLoc));
else
return Owned(new (Context) BinaryOperator(lhs, rhs, Opc, Context.DependentTy,
TokLoc));
return Owned(new (Context) BinaryOperator(lhs, rhs, Opc,
Context.DependentTy, TokLoc));
}
if (getLangOptions().CPlusPlus &&
if (getLangOptions().CPlusPlus && Opc != BinaryOperator::PtrMemD &&
(lhs->getType()->isRecordType() || lhs->getType()->isEnumeralType() ||
rhs->getType()->isRecordType() || rhs->getType()->isEnumeralType())) {
// If this is one of the assignment operators, we only perform
@ -3724,6 +3798,8 @@ Action::OwningExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
// Determine which overloaded operator we're dealing with.
static const OverloadedOperatorKind OverOps[] = {
// Overloading .* is not possible.
static_cast<OverloadedOperatorKind>(0), OO_ArrowStar,
OO_Star, OO_Slash, OO_Percent,
OO_Plus, OO_Minus,
OO_LessLess, OO_GreaterGreater,

View File

@ -16,6 +16,7 @@
#ifndef LLVM_CLANG_SEMA_INHERIT_H
#define LLVM_CLANG_SEMA_INHERIT_H
#include "Sema.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Type.h"
@ -25,7 +26,6 @@
#include <map>
namespace clang {
class Sema;
class CXXBaseSpecifier;
class CXXRecordType;

View File

@ -610,8 +610,8 @@ Sema::CppLookupName(Scope *S, DeclarationName Name,
// using-directives later.
for (OutOfLineCtx = Ctx; OutOfLineCtx && !OutOfLineCtx->isFileContext();
OutOfLineCtx = OutOfLineCtx->getParent()) {
if (R = LookupQualifiedName(OutOfLineCtx, Name, NameKind,
RedeclarationOnly))
if ((R = LookupQualifiedName(OutOfLineCtx, Name, NameKind,
RedeclarationOnly)))
return std::make_pair(true, R);
}
}
@ -638,7 +638,7 @@ Sema::CppLookupName(Scope *S, DeclarationName Name,
// context as well as walking through the scopes.
LookupResultsTy LookupResults;
assert(!OutOfLineCtx || OutOfLineCtx->isFileContext() &&
assert((!OutOfLineCtx || OutOfLineCtx->isFileContext()) &&
"We should have been looking only at file context here already.");
bool LookedInCtx = false;
LookupResult Result;

View File

@ -79,3 +79,28 @@ void g() {
void (HasMembers::*pmd)() = &HasMembers::d;
}
void h() {
HasMembers hm, *phm = &hm;
int HasMembers::*pi = &HasMembers::i;
hm.*pi = 0;
int i = phm->*pi;
(void)&(hm.*pi);
(void)&(phm->*pi);
(void)&((&hm)->*pi); // expected-error {{address expression must be an lvalue or a function designator}}
void (HasMembers::*pf)() = &HasMembers::f;
(hm.*pf)();
(phm->*pf)();
}
struct OverloadsPtrMem
{
int operator ->*(const char *);
};
void i() {
OverloadsPtrMem m;
int foo = m->*"Awesome!";
}

View File

@ -698,11 +698,11 @@ welcome!</p>
</tr>
<tr>
<td>&nbsp;&nbsp;5.5 [expr.mptr.oper]</td>
<td class="broken"></td>
<td class="broken"></td>
<td class="broken"></td>
<td></td>
<td class="complete" align="center">&#x2713;</td>
<td class="advanced"></td>
<td class="advanced"></td>
<td></td>
<td>Dereferenced member function pointers have the wrong type.</td>
</tr>
<tr>
<td>&nbsp;&nbsp;5.6 [expr.mul]</td>