Added operator overloading for unary operators, post-increment, and

post-decrement, including support for generating all of the built-in
operator candidates for these operators. 

C++ and C have different rules for the arguments to the builtin unary
'+' and '-'. Implemented both variants in Sema::ActOnUnaryOp.

In C++, pre-increment and pre-decrement return lvalues. Update
Expr::isLvalue accordingly.

llvm-svn: 59638
This commit is contained in:
Douglas Gregor 2008-11-19 15:42:04 +00:00
parent 3462c8ecda
commit d08452f60a
11 changed files with 522 additions and 87 deletions

View File

@ -436,7 +436,7 @@ namespace {
}
// Postfix Expressions.
virtual ExprResult ActOnPostfixUnaryOp(SourceLocation OpLoc,
virtual ExprResult ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc,
tok::TokenKind Kind, ExprTy *Input) {
llvm::cout << __FUNCTION__ << "\n";
return 0;
@ -467,8 +467,8 @@ namespace {
}
// Unary Operators. 'Tok' is the token for the operator.
virtual ExprResult ActOnUnaryOp(SourceLocation OpLoc, tok::TokenKind Op,
ExprTy *Input) {
virtual ExprResult ActOnUnaryOp(Scope *S, SourceLocation OpLoc,
tok::TokenKind Op, ExprTy *Input) {
llvm::cout << __FUNCTION__ << "\n";
return 0;
}

View File

@ -509,7 +509,7 @@ public:
}
// Postfix Expressions.
virtual ExprResult ActOnPostfixUnaryOp(SourceLocation OpLoc,
virtual ExprResult ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc,
tok::TokenKind Kind, ExprTy *Input) {
return 0;
}
@ -536,8 +536,8 @@ public:
}
// Unary Operators. 'Tok' is the token for the operator.
virtual ExprResult ActOnUnaryOp(SourceLocation OpLoc, tok::TokenKind Op,
ExprTy *Input) {
virtual ExprResult ActOnUnaryOp(Scope *S, SourceLocation OpLoc,
tok::TokenKind Op, ExprTy *Input) {
return 0;
}
virtual ExprResult

View File

@ -398,6 +398,11 @@ Expr::isLvalueResult Expr::isLvalue(ASTContext &Ctx) const {
cast<UnaryOperator>(this)->getOpcode() == UnaryOperator::Imag ||
cast<UnaryOperator>(this)->getOpcode() == UnaryOperator::Extension)
return cast<UnaryOperator>(this)->getSubExpr()->isLvalue(Ctx); // GNU.
if (Ctx.getLangOptions().CPlusPlus && // C++ [expr.pre.incr]p1
(cast<UnaryOperator>(this)->getOpcode() == UnaryOperator::PreInc ||
cast<UnaryOperator>(this)->getOpcode() == UnaryOperator::PreDec))
return LV_Valid;
break;
case ImplicitCastExprClass:
return cast<ImplicitCastExpr>(this)->isLvalueCast()? LV_Valid

View File

@ -515,7 +515,7 @@ Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) {
SourceLocation SavedLoc = ConsumeToken();
Res = ParseCastExpression(true);
if (!Res.isInvalid)
Res = Actions.ActOnUnaryOp(SavedLoc, SavedKind, Res.Val);
Res = Actions.ActOnUnaryOp(CurScope, SavedLoc, SavedKind, Res.Val);
return Res;
}
case tok::amp: // unary-expression: '&' cast-expression
@ -529,7 +529,7 @@ Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) {
SourceLocation SavedLoc = ConsumeToken();
Res = ParseCastExpression(false);
if (!Res.isInvalid)
Res = Actions.ActOnUnaryOp(SavedLoc, SavedKind, Res.Val);
Res = Actions.ActOnUnaryOp(CurScope, SavedLoc, SavedKind, Res.Val);
return Res;
}
@ -539,7 +539,7 @@ Parser::ExprResult Parser::ParseCastExpression(bool isUnaryExpression) {
SourceLocation SavedLoc = ConsumeToken();
Res = ParseCastExpression(false);
if (!Res.isInvalid)
Res = Actions.ActOnUnaryOp(SavedLoc, SavedKind, Res.Val);
Res = Actions.ActOnUnaryOp(CurScope, SavedLoc, SavedKind, Res.Val);
return Res;
}
case tok::kw_sizeof: // unary-expression: 'sizeof' unary-expression
@ -724,8 +724,8 @@ Parser::ExprResult Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
case tok::plusplus: // postfix-expression: postfix-expression '++'
case tok::minusminus: // postfix-expression: postfix-expression '--'
if (!LHS.isInvalid)
LHS = Actions.ActOnPostfixUnaryOp(Tok.getLocation(), Tok.getKind(),
LHS.Val);
LHS = Actions.ActOnPostfixUnaryOp(CurScope, Tok.getLocation(),
Tok.getKind(), LHS.Val);
ConsumeToken();
break;
}

View File

@ -386,7 +386,8 @@ Parser::StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) {
}
// Add the __extension__ node to the AST.
Res = Actions.ActOnUnaryOp(ExtLoc, tok::kw___extension__, Res.Val);
Res = Actions.ActOnUnaryOp(CurScope, ExtLoc, tok::kw___extension__,
Res.Val);
if (Res.isInvalid)
continue;

View File

@ -435,9 +435,9 @@ public:
void AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys,
Expr **Args, unsigned NumArgs,
OverloadCandidateSet& CandidateSet);
void AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op,
Expr **Args,
OverloadCandidateSet& CandidateSet);
void AddBuiltinOperatorCandidates(OverloadedOperatorKind Op,
Expr **Args, unsigned NumArgs,
OverloadCandidateSet& CandidateSet);
void AddOverloadCandidates(const OverloadedFunctionDecl *Ovl,
Expr **Args, unsigned NumArgs,
OverloadCandidateSet& CandidateSet,
@ -640,8 +640,8 @@ public:
virtual ExprResult ActOnStringLiteral(const Token *Toks, unsigned NumToks);
// Binary/Unary Operators. 'Tok' is the token for the operator.
virtual ExprResult ActOnUnaryOp(SourceLocation OpLoc, tok::TokenKind Op,
ExprTy *Input);
virtual ExprResult ActOnUnaryOp(Scope *S, SourceLocation OpLoc,
tok::TokenKind Op, ExprTy *Input);
virtual ExprResult
ActOnSizeOfAlignOfExpr(SourceLocation OpLoc, bool isSizeof, bool isType,
void *TyOrEx, const SourceRange &ArgRange);
@ -649,7 +649,7 @@ public:
bool CheckSizeOfAlignOfOperand(QualType type, SourceLocation OpLoc,
const SourceRange &R, bool isSizeof);
virtual ExprResult ActOnPostfixUnaryOp(SourceLocation OpLoc,
virtual ExprResult ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc,
tok::TokenKind Kind, ExprTy *Input);
virtual ExprResult ActOnArraySubscriptExpr(ExprTy *Base, SourceLocation LLoc,

View File

@ -752,19 +752,112 @@ QualType Sema::CheckRealImagOperand(Expr *&V, SourceLocation Loc) {
Action::ExprResult Sema::ActOnPostfixUnaryOp(SourceLocation OpLoc,
Action::ExprResult Sema::ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc,
tok::TokenKind Kind,
ExprTy *Input) {
Expr *Arg = (Expr *)Input;
UnaryOperator::Opcode Opc;
switch (Kind) {
default: assert(0 && "Unknown unary op!");
case tok::plusplus: Opc = UnaryOperator::PostInc; break;
case tok::minusminus: Opc = UnaryOperator::PostDec; break;
}
QualType result = CheckIncrementDecrementOperand((Expr *)Input, OpLoc);
if (getLangOptions().CPlusPlus &&
(Arg->getType()->isRecordType() || Arg->getType()->isEnumeralType())) {
// Which overloaded operator?
OverloadedOperatorKind OverOp =
(Opc == UnaryOperator::PostInc)? OO_PlusPlus : OO_MinusMinus;
// C++ [over.inc]p1:
//
// [...] If the function is a member function with one
// parameter (which shall be of type int) or a non-member
// function with two parameters (the second of which shall be
// of type int), it defines the postfix increment operator ++
// for objects of that type. When the postfix increment is
// called as a result of using the ++ operator, the int
// argument will have value zero.
Expr *Args[2] = {
Arg,
new IntegerLiteral(llvm::APInt(Context.Target.getIntWidth(), 0,
/*isSigned=*/true),
Context.IntTy, SourceLocation())
};
// Build the candidate set for overloading
OverloadCandidateSet CandidateSet;
AddOperatorCandidates(OverOp, S, Args, 2, CandidateSet);
// Perform overload resolution.
OverloadCandidateSet::iterator Best;
switch (BestViableFunction(CandidateSet, Best)) {
case OR_Success: {
// We found a built-in operator or an overloaded operator.
FunctionDecl *FnDecl = Best->Function;
if (FnDecl) {
// We matched an overloaded operator. Build a call to that
// operator.
// Convert the arguments.
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FnDecl)) {
if (PerformObjectArgumentInitialization(Arg, Method))
return true;
} else {
// Convert the arguments.
if (PerformCopyInitialization(Arg,
FnDecl->getParamDecl(0)->getType(),
"passing"))
return true;
}
// Determine the result type
QualType ResultTy
= FnDecl->getType()->getAsFunctionType()->getResultType();
ResultTy = ResultTy.getNonReferenceType();
// Build the actual expression node.
Expr *FnExpr = new DeclRefExpr(FnDecl, FnDecl->getType(),
SourceLocation());
UsualUnaryConversions(FnExpr);
return new CXXOperatorCallExpr(FnExpr, Args, 2, ResultTy, OpLoc);
} else {
// We matched a built-in operator. Convert the arguments, then
// break out so that we will build the appropriate built-in
// operator node.
if (PerformCopyInitialization(Arg, Best->BuiltinTypes.ParamTypes[0],
"passing"))
return true;
break;
}
}
case OR_No_Viable_Function:
// No viable function; fall through to handling this as a
// built-in operator, which will produce an error message for us.
break;
case OR_Ambiguous:
Diag(OpLoc, diag::err_ovl_ambiguous_oper)
<< UnaryOperator::getOpcodeStr(Opc)
<< Arg->getSourceRange();
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true);
return true;
}
// Either we found no viable overloaded operator or we matched a
// built-in operator. In either case, fall through to trying to
// build a built-in operation.
}
QualType result = CheckIncrementDecrementOperand(Arg, OpLoc);
if (result.isNull())
return true;
return new UnaryOperator((Expr *)Input, Opc, result, OpLoc);
return new UnaryOperator(Arg, Opc, result, OpLoc);
}
Action::ExprResult Sema::
@ -2819,16 +2912,6 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
!(lhs->getType()->isRecordType() || lhs->getType()->isEnumeralType())) {
return CreateBuiltinBinOp(TokLoc, Opc, lhs, rhs);
}
// C++ [over.binary]p1:
// A binary operator shall be implemented either by a non-static
// member function (9.3) with one parameter or by a non-member
// function with two parameters. Thus, for any binary operator
// @, x@y can be interpreted as either x.operator@(y) or
// operator@(x,y). If both forms of the operator function have
// been declared, the rules in 13.3.1.2 determines which, if
// any, interpretation is used.
OverloadCandidateSet CandidateSet;
// Determine which overloaded operator we're dealing with.
static const OverloadedOperatorKind OverOps[] = {
@ -2854,6 +2937,7 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
// Add the appropriate overloaded operators (C++ [over.match.oper])
// to the candidate set.
OverloadCandidateSet CandidateSet;
Expr *Args[2] = { lhs, rhs };
AddOperatorCandidates(OverOp, S, Args, 2, CandidateSet);
@ -2893,7 +2977,6 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
SourceLocation());
UsualUnaryConversions(FnExpr);
Expr *Args[2] = { lhs, rhs };
return new CXXOperatorCallExpr(FnExpr, Args, 2, ResultTy, TokLoc);
} else {
// We matched a built-in operator. Convert the arguments, then
@ -2933,10 +3016,98 @@ Action::ExprResult Sema::ActOnBinOp(Scope *S, SourceLocation TokLoc,
}
// Unary Operators. 'Tok' is the token for the operator.
Action::ExprResult Sema::ActOnUnaryOp(SourceLocation OpLoc, tok::TokenKind Op,
ExprTy *input) {
Action::ExprResult Sema::ActOnUnaryOp(Scope *S, SourceLocation OpLoc,
tok::TokenKind Op, ExprTy *input) {
Expr *Input = (Expr*)input;
UnaryOperator::Opcode Opc = ConvertTokenKindToUnaryOpcode(Op);
if (getLangOptions().CPlusPlus &&
(Input->getType()->isRecordType()
|| Input->getType()->isEnumeralType())) {
// Determine which overloaded operator we're dealing with.
static const OverloadedOperatorKind OverOps[] = {
OO_None, OO_None,
OO_PlusPlus, OO_MinusMinus,
OO_Amp, OO_Star,
OO_Plus, OO_Minus,
OO_Tilde, OO_Exclaim,
OO_None, OO_None,
OO_None,
OO_None
};
OverloadedOperatorKind OverOp = OverOps[Opc];
// Add the appropriate overloaded operators (C++ [over.match.oper])
// to the candidate set.
OverloadCandidateSet CandidateSet;
if (OverOp != OO_None)
AddOperatorCandidates(OverOp, S, &Input, 1, CandidateSet);
// Perform overload resolution.
OverloadCandidateSet::iterator Best;
switch (BestViableFunction(CandidateSet, Best)) {
case OR_Success: {
// We found a built-in operator or an overloaded operator.
FunctionDecl *FnDecl = Best->Function;
if (FnDecl) {
// We matched an overloaded operator. Build a call to that
// operator.
// Convert the arguments.
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FnDecl)) {
if (PerformObjectArgumentInitialization(Input, Method))
return true;
} else {
// Convert the arguments.
if (PerformCopyInitialization(Input,
FnDecl->getParamDecl(0)->getType(),
"passing"))
return true;
}
// Determine the result type
QualType ResultTy
= FnDecl->getType()->getAsFunctionType()->getResultType();
ResultTy = ResultTy.getNonReferenceType();
// Build the actual expression node.
Expr *FnExpr = new DeclRefExpr(FnDecl, FnDecl->getType(),
SourceLocation());
UsualUnaryConversions(FnExpr);
return new CXXOperatorCallExpr(FnExpr, &Input, 1, ResultTy, OpLoc);
} else {
// We matched a built-in operator. Convert the arguments, then
// break out so that we will build the appropriate built-in
// operator node.
if (PerformCopyInitialization(Input, Best->BuiltinTypes.ParamTypes[0],
"passing"))
return true;
break;
}
}
case OR_No_Viable_Function:
// No viable function; fall through to handling this as a
// built-in operator, which will produce an error message for us.
break;
case OR_Ambiguous:
Diag(OpLoc, diag::err_ovl_ambiguous_oper)
<< UnaryOperator::getOpcodeStr(Opc)
<< Input->getSourceRange();
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true);
return true;
}
// Either we found no viable overloaded operator or we matched a
// built-in operator. In either case, fall through to trying to
// build a built-in operation.
}
QualType resultType;
switch (Opc) {
default:
@ -2956,10 +3127,18 @@ Action::ExprResult Sema::ActOnUnaryOp(SourceLocation OpLoc, tok::TokenKind Op,
case UnaryOperator::Minus:
UsualUnaryConversions(Input);
resultType = Input->getType();
if (!resultType->isArithmeticType()) // C99 6.5.3.3p1
return Diag(OpLoc, diag::err_typecheck_unary_expr,
resultType.getAsString());
break;
if (resultType->isArithmeticType()) // C99 6.5.3.3p1
break;
else if (getLangOptions().CPlusPlus && // C++ [expr.unary.op]p6-7
resultType->isEnumeralType())
break;
else if (getLangOptions().CPlusPlus && // C++ [expr.unary.op]p6
Opc == UnaryOperator::Plus &&
resultType->isPointerType())
break;
return Diag(OpLoc, diag::err_typecheck_unary_expr,
resultType.getAsString());
case UnaryOperator::Not: // bitwise complement
UsualUnaryConversions(Input);
resultType = Input->getType();

View File

@ -1843,8 +1843,7 @@ void Sema::AddOperatorCandidates(OverloadedOperatorKind Op, Scope *S,
}
// Add builtin overload candidates (C++ [over.built]).
if (NumArgs == 2)
return AddBuiltinBinaryOperatorCandidates(Op, Args, CandidateSet);
AddBuiltinOperatorCandidates(Op, Args, NumArgs, CandidateSet);
}
/// AddBuiltinCandidate - Add a candidate for a built-in
@ -2053,16 +2052,15 @@ void BuiltinCandidateTypeSet::AddTypesConvertedFrom(QualType Ty,
}
}
/// AddBuiltinCandidates - Add the appropriate built-in operator
/// overloads to the candidate set (C++ [over.built]), based on the
/// operator @p Op and the arguments given. For example, if the
/// operator is a binary '+', this routine might add
/// "int operator+(int, int)"
/// to cover integer addition.
/// AddBuiltinOperatorCandidates - Add the appropriate built-in
/// operator overloads to the candidate set (C++ [over.built]), based
/// on the operator @p Op and the arguments given. For example, if the
/// operator is a binary '+', this routine might add "int
/// operator+(int, int)" to cover integer addition.
void
Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op,
Expr **Args,
OverloadCandidateSet& CandidateSet) {
Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op,
Expr **Args, unsigned NumArgs,
OverloadCandidateSet& CandidateSet) {
// The set of "promoted arithmetic types", which are the arithmetic
// types are that preserved by promotion (C++ [over.built]p2). Note
// that the first few of these types are the promoted integral
@ -2090,10 +2088,11 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op,
BuiltinCandidateTypeSet CandidateTypes(Context);
if (Op == OO_Less || Op == OO_Greater || Op == OO_LessEqual ||
Op == OO_GreaterEqual || Op == OO_EqualEqual || Op == OO_ExclaimEqual ||
Op == OO_Plus || Op == OO_Minus || Op == OO_Equal ||
Op == OO_Plus || (Op == OO_Minus && NumArgs == 2) || Op == OO_Equal ||
Op == OO_PlusEqual || Op == OO_MinusEqual || Op == OO_Subscript ||
Op == OO_ArrowStar) {
for (unsigned ArgIdx = 0; ArgIdx < 2; ++ArgIdx)
Op == OO_ArrowStar || Op == OO_PlusPlus || Op == OO_MinusMinus ||
(Op == OO_Star && NumArgs == 1)) {
for (unsigned ArgIdx = 0; ArgIdx < NumArgs; ++ArgIdx)
CandidateTypes.AddTypesConvertedFrom(Args[ArgIdx]->getType());
}
@ -2104,24 +2103,184 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op,
assert(false && "Expected an overloaded operator");
break;
case OO_Star: // '*' is either unary or binary
if (NumArgs == 1)
goto UnaryStar;
else
goto BinaryStar;
break;
case OO_Plus: // '+' is either unary or binary
if (NumArgs == 1)
goto UnaryPlus;
else
goto BinaryPlus;
break;
case OO_Minus: // '-' is either unary or binary
if (NumArgs == 1)
goto UnaryMinus;
else
goto BinaryMinus;
break;
case OO_Amp: // '&' is either unary or binary
if (NumArgs == 1)
goto UnaryAmp;
else
goto BinaryAmp;
case OO_PlusPlus:
case OO_MinusMinus:
// C++ [over.built]p3:
//
// For every pair (T, VQ), where T is an arithmetic type, and VQ
// is either volatile or empty, there exist candidate operator
// functions of the form
//
// VQ T& operator++(VQ T&);
// T operator++(VQ T&, int);
//
// C++ [over.built]p4:
//
// For every pair (T, VQ), where T is an arithmetic type other
// than bool, and VQ is either volatile or empty, there exist
// candidate operator functions of the form
//
// VQ T& operator--(VQ T&);
// T operator--(VQ T&, int);
for (unsigned Arith = (Op == OO_PlusPlus? 0 : 1);
Arith < NumArithmeticTypes; ++Arith) {
QualType ArithTy = ArithmeticTypes[Arith];
QualType ParamTypes[2]
= { Context.getReferenceType(ArithTy), Context.IntTy };
// Non-volatile version.
if (NumArgs == 1)
AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, CandidateSet);
else
AddBuiltinCandidate(ArithTy, ParamTypes, Args, 2, CandidateSet);
// Volatile version
ParamTypes[0] = Context.getReferenceType(ArithTy.withVolatile());
if (NumArgs == 1)
AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, CandidateSet);
else
AddBuiltinCandidate(ArithTy, ParamTypes, Args, 2, CandidateSet);
}
// C++ [over.built]p5:
//
// For every pair (T, VQ), where T is a cv-qualified or
// cv-unqualified object type, and VQ is either volatile or
// empty, there exist candidate operator functions of the form
//
// T*VQ& operator++(T*VQ&);
// T*VQ& operator--(T*VQ&);
// T* operator++(T*VQ&, int);
// T* operator--(T*VQ&, int);
for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin();
Ptr != CandidateTypes.pointer_end(); ++Ptr) {
// Skip pointer types that aren't pointers to object types.
if (!(*Ptr)->getAsPointerType()->getPointeeType()->isObjectType())
continue;
QualType ParamTypes[2] = {
Context.getReferenceType(*Ptr), Context.IntTy
};
// Without volatile
if (NumArgs == 1)
AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, CandidateSet);
else
AddBuiltinCandidate(*Ptr, ParamTypes, Args, 2, CandidateSet);
if (!Context.getCanonicalType(*Ptr).isVolatileQualified()) {
// With volatile
ParamTypes[0] = Context.getReferenceType((*Ptr).withVolatile());
if (NumArgs == 1)
AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 1, CandidateSet);
else
AddBuiltinCandidate(*Ptr, ParamTypes, Args, 2, CandidateSet);
}
}
break;
UnaryStar:
// C++ [over.built]p6:
// For every cv-qualified or cv-unqualified object type T, there
// exist candidate operator functions of the form
//
// T& operator*(T*);
//
// C++ [over.built]p7:
// For every function type T, there exist candidate operator
// functions of the form
// T& operator*(T*);
for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin();
Ptr != CandidateTypes.pointer_end(); ++Ptr) {
QualType ParamTy = *Ptr;
QualType PointeeTy = ParamTy->getAsPointerType()->getPointeeType();
AddBuiltinCandidate(Context.getReferenceType(PointeeTy),
&ParamTy, Args, 1, CandidateSet);
}
break;
UnaryPlus:
// C++ [over.built]p8:
// For every type T, there exist candidate operator functions of
// the form
//
// T* operator+(T*);
for (BuiltinCandidateTypeSet::iterator Ptr = CandidateTypes.pointer_begin();
Ptr != CandidateTypes.pointer_end(); ++Ptr) {
QualType ParamTy = *Ptr;
AddBuiltinCandidate(ParamTy, &ParamTy, Args, 1, CandidateSet);
}
// Fall through
UnaryMinus:
// C++ [over.built]p9:
// For every promoted arithmetic type T, there exist candidate
// operator functions of the form
//
// T operator+(T);
// T operator-(T);
for (unsigned Arith = FirstPromotedArithmeticType;
Arith < LastPromotedArithmeticType; ++Arith) {
QualType ArithTy = ArithmeticTypes[Arith];
AddBuiltinCandidate(ArithTy, &ArithTy, Args, 1, CandidateSet);
}
break;
case OO_Tilde:
// C++ [over.built]p10:
// For every promoted integral type T, there exist candidate
// operator functions of the form
//
// T operator~(T);
for (unsigned Int = FirstPromotedIntegralType;
Int < LastPromotedIntegralType; ++Int) {
QualType IntTy = ArithmeticTypes[Int];
AddBuiltinCandidate(IntTy, &IntTy, Args, 1, CandidateSet);
}
break;
case OO_New:
case OO_Delete:
case OO_Array_New:
case OO_Array_Delete:
case OO_Tilde:
case OO_Exclaim:
case OO_PlusPlus:
case OO_MinusMinus:
case OO_Arrow:
case OO_Call:
assert(false && "Expected a binary operator");
assert(false && "Special operators don't use AddBuiltinOperatorCandidates");
break;
case OO_Comma:
UnaryAmp:
case OO_Arrow:
// C++ [over.match.oper]p3:
// -- For the operator ',', the unary operator '&', or the
// operator '->', the built-in candidates set is empty.
// We don't check '&' or '->' here, since they are unary operators.
break;
case OO_Less:
@ -2156,8 +2315,8 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op,
// Fall through.
isComparison = true;
case OO_Plus:
case OO_Minus:
BinaryPlus:
BinaryMinus:
if (!isComparison) {
// We didn't fall through, so we must have OO_Plus or OO_Minus.
@ -2201,8 +2360,8 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op,
}
// Fall through
case OO_Star:
case OO_Slash:
BinaryStar:
// C++ [over.built]p12:
//
// For every pair of promoted arithmetic types L and R, there
@ -2235,7 +2394,7 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op,
break;
case OO_Percent:
case OO_Amp:
BinaryAmp:
case OO_Caret:
case OO_Pipe:
case OO_LessLess:
@ -2285,10 +2444,12 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op,
ParamTypes[1] = *Enum;
AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
// volatile T& operator=(volatile T&, T)
ParamTypes[0] = Context.getReferenceType((*Enum).withVolatile());
ParamTypes[1] = *Enum;
AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
if (!Context.getCanonicalType(*Enum).isVolatileQualified()) {
// volatile T& operator=(volatile T&, T)
ParamTypes[0] = Context.getReferenceType((*Enum).withVolatile());
ParamTypes[1] = *Enum;
AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
}
}
// Fall through.
@ -2319,9 +2480,11 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op,
ParamTypes[0] = Context.getReferenceType(*Ptr);
AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
// volatile version
ParamTypes[0] = Context.getReferenceType((*Ptr).withVolatile());
AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
if (!Context.getCanonicalType(*Ptr).isVolatileQualified()) {
// volatile version
ParamTypes[0] = Context.getReferenceType((*Ptr).withVolatile());
AddBuiltinCandidate(ParamTypes[0], ParamTypes, Args, 2, CandidateSet);
}
}
// Fall through.
@ -2396,13 +2559,26 @@ Sema::AddBuiltinBinaryOperatorCandidates(OverloadedOperatorKind Op,
}
break;
case OO_Exclaim: {
// C++ [over.operator]p23:
//
// There also exist candidate operator functions of the form
//
// bool operator!(bool);
// bool operator&&(bool, bool); [BELOW]
// bool operator||(bool, bool); [BELOW]
QualType ParamTy = Context.BoolTy;
AddBuiltinCandidate(ParamTy, &ParamTy, Args, 1, CandidateSet);
break;
}
case OO_AmpAmp:
case OO_PipePipe: {
// C++ [over.operator]p23:
//
// There also exist candidate operator functions of the form
//
// bool operator!(bool); [In Unary version]
// bool operator!(bool); [ABOVE]
// bool operator&&(bool, bool);
// bool operator||(bool, bool);
QualType ParamTypes[2] = { Context.BoolTy, Context.BoolTy };

View File

@ -25,6 +25,14 @@ yes& islong(unsigned long); // FIXME: shouldn't be needed
no& islong(int);
void f(Short s, Long l, Enum1 e1, Enum2 e2) {
// C++ [over.built]p8
int i1 = +e1;
int i2 = -e2;
// C++ [over.built]p10:
int i3 = ~s;
bool b1 = !s;
// C++ [over.built]p12
(void)static_cast<yes&>(islong(s + l));
(void)static_cast<no&>(islong(s + s));
@ -46,6 +54,12 @@ struct LongRef {
};
void g(ShortRef sr, LongRef lr) {
// C++ [over.built]p3
short s1 = sr++;
// C++ [over.built]p3
long l1 = lr--;
// C++ [over.built]p18
short& sr1 = (sr *= lr);
volatile long& lr1 = (lr *= sr);
@ -65,7 +79,16 @@ struct ConstIntPtr {
operator int const *();
};
void test_with_ptrs(VolatileIntPtr vip, ConstIntPtr cip, ShortRef sr) {
struct VolatileIntPtrRef {
operator int volatile *&();
};
struct ConstIntPtrRef {
operator int const *&();
};
void test_with_ptrs(VolatileIntPtr vip, ConstIntPtr cip, ShortRef sr,
VolatileIntPtrRef vipr, ConstIntPtrRef cipr) {
#if 0
// FIXME: Enable these tests once we have operator overloading for
// operator[].
@ -76,4 +99,19 @@ void test_with_ptrs(VolatileIntPtr vip, ConstIntPtr cip, ShortRef sr) {
#endif
bool b1 = (vip == cip);
long p1 = vip - cip;
// C++ [over.built]p5:
int volatile *vip1 = vipr++;
int const *cip1 = cipr++;
int volatile *&vipr1 = ++vipr;
int const *&cipr1 = --cipr;
// C++ [over.built]p6:
int volatile &ivr = *vip;
// C++ [over.built]p8:
int volatile *vip2 = +vip;
int i1 = +sr;
int i2 = -sr;
}

View File

@ -69,3 +69,31 @@ void enum_test(Enum1 enum1, Enum2 enum2, E1 e1, E2 e2) {
float &f3 = (e1 == enum2);
float &f4 = (enum1 == enum2); // expected-error{{non-const reference to type 'float' cannot be initialized with a temporary of type '_Bool'}}
}
struct PostInc {
PostInc operator++(int);
PostInc& operator++();
};
struct PostDec {
PostDec operator--(int);
PostDec& operator--();
};
void incdec_test(PostInc pi, PostDec pd) {
const PostInc& pi1 = pi++;
const PostDec& pd1 = pd--;
PostInc &pi2 = ++pi;
PostDec &pd2 = --pd;
}
struct SmartPtr {
int& operator*();
// FIXME: spurious error: long& operator*() const;
};
void test_smartptr(SmartPtr ptr, const SmartPtr cptr) {
int &ir = *ptr;
// FIXME: reinstate long &lr = *cptr;
}

View File

@ -826,8 +826,8 @@ welcome!</p>
<tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;13.3.1.2 [over.match.oper]</td>
<td class="complete" align="center">&#x2713;</td>
<td class="broken" align="center"></td>
<td class="broken" align="center"></td>
<td class="medium" align="center"></td>
<td class="medium" align="center"></td>
<td class="broken" align="center"></td>
<td></td>
</tr>
@ -943,27 +943,27 @@ welcome!</p>
<td class="advanced" align="center"></td>
<td class="medium" align="center"></td>
<td class="broken" align="center"></td>
<td>Most overloaded operators can only be called with function syntax, e.g., <code>operator+(x)</code>.</td>
<td>Some overloaded operators can only be called with function syntax, e.g., <code>operator[](x)</code>.</td>
</tr>
<tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp;13.5.1 [over.unary]</td>
<td class="advanced" align="center"></td>
<td class="na" align="center">N/A</td>
<td class="advanced" align="center"></td>
<td class="advanced" align="center"></td>
<td class="basic" align="center"></td>
<td class="broken" align="center"></td>
<td></td>
</tr>
<tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp;13.5.2 [over.binary]</td>
<td class="advanced" align="center"></td>
<td class="na" align="center">N/A</td>
<td class="advanced" align="center"></td>
<td class="advanced" align="center"></td>
<td class="medium" align="center"></td>
<td class="broken" align="center"></td>
<td></td>
</tr>
<tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp;13.5.3 [over.ass]</td>
<td class="advanced" align="center"></td>
<td class="na" align="center">N/A</td>
<td class="advanced" align="center"></td>
<td class="basic" align="center"></td>
<td class="broken" align="center"></td>
@ -971,7 +971,7 @@ welcome!</p>
</tr>
<tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp;13.5.4 [over.call]</td>
<td class="advanced" align="center"></td>
<td class="na" align="center">N/A</td>
<td class="advanced" align="center"></td>
<td class="basic" align="center"></td>
<td class="broken" align="center"></td>
@ -979,7 +979,7 @@ welcome!</p>
</tr>
<tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp;13.5.5 [over.sub]</td>
<td class="advanced" align="center"></td>
<td class="na" align="center">N/A</td>
<td class="advanced" align="center"></td>
<td class="basic" align="center"></td>
<td class="broken" align="center"></td>
@ -987,21 +987,29 @@ welcome!</p>
</tr>
<tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp;13.5.6 [over.ref]</td>
<td class="advanced" align="center"></td>
<td class="na" align="center">N/A</td>
<td class="advanced" align="center"></td>
<td class="advanced" align="center"></td>
<td class="basic" align="center"></td>
<td class="broken" align="center"></td>
<td></td>
</tr>
<tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp;13.5.7 [over.inc]</td>
<td class="advanced" align="center"></td>
<td class="na" align="center">N/A</td>
<td class="advanced" align="center"></td>
<td class="advanced" align="center"></td>
<td class="basic" align="center"></td>
<td class="broken" align="center"></td>
<td></td>
</tr>
<tr><td>&nbsp;&nbsp;13.6 [over.built]</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr>
<td>&nbsp;&nbsp;13.6 [over.built]</td>
<td class="na" align="center">N/A</td>
<td class="advanced" align="center"></td>
<td class="medium" align="center"></td>
<td class="broken" align="center"></td>
<td>Missing pointer-to-member versions (p11, p16) and support for
the ternary operator (p24, p25).</td>
</tr>
<tr><td>14 [temp]</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>&nbsp;&nbsp;14.1 [temp.param]</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>&nbsp;&nbsp;14.2 [temp.names]</td><td></td><td></td><td></td><td></td><td></td></tr>