Submitted by:
Reviewed by:
Work on finishing up typechecking for simple assignments (=) and function
calls. Here is an overview:
- implemented type checking for function calls (in Sema::ParseCallExpr).
- refactored UsualAssignmentConversions to return the result of the conversion.
This enum will allow all clients to emit different diagnostics based on context.
- fixed bug in Expr::isLvalue()...it wasn't handling arrays properly. Also
changed the name to isModifiableLvalue, which is consistent with the function on QualType.
- Added 6 diagnostics (3 errors, 3 extensions).

llvm-svn: 39432
This commit is contained in:
Steve Naroff 2007-05-03 21:03:48 +00:00
parent b891de392c
commit 17f76e04d2
8 changed files with 259 additions and 98 deletions

View File

@ -118,7 +118,7 @@ const char *BinaryOperator::getOpcodeStr(Opcode Op) {
} }
} }
/// Expressions that can be lvalues: /// Nonarray expressions that can be lvalues:
/// - name, where name must be a variable /// - name, where name must be a variable
/// - e[i] /// - e[i]
/// - (e), where e must be an lvalue /// - (e), where e must be an lvalue
@ -127,7 +127,7 @@ const char *BinaryOperator::getOpcodeStr(Opcode Op) {
/// - *e /// - *e
/// - string-constant /// - string-constant
/// ///
bool Expr::isLvalue() { bool Expr::isModifiableLvalue() {
switch (getStmtClass()) { switch (getStmtClass()) {
case StringLiteralClass: case StringLiteralClass:
return true; return true;
@ -135,19 +135,20 @@ bool Expr::isLvalue() {
return true; return true;
case DeclRefExprClass: case DeclRefExprClass:
const DeclRefExpr *d = cast<DeclRefExpr>(this); const DeclRefExpr *d = cast<DeclRefExpr>(this);
if (isa<VarDecl>(d->getDecl())) if (const VarDecl *vd = dyn_cast<VarDecl>(d->getDecl()))
return true; if (vd->getType().isModifiableLvalue())
return true;
return false; return false;
case MemberExprClass: case MemberExprClass:
const MemberExpr *m = cast<MemberExpr>(this); const MemberExpr *m = cast<MemberExpr>(this);
if (m->isArrow()) if (m->isArrow())
return true; return true;
return m->getBase()->isLvalue(); // make sure "." is an lvalue return m->getBase()->isModifiableLvalue(); // make sure "." is an lvalue
case UnaryOperatorClass: case UnaryOperatorClass:
const UnaryOperator *u = cast<UnaryOperator>(this); const UnaryOperator *u = cast<UnaryOperator>(this);
return u->getOpcode() == UnaryOperator::Deref; return u->getOpcode() == UnaryOperator::Deref;
case ParenExprClass: case ParenExprClass:
return cast<ParenExpr>(this)->getSubExpr()->isLvalue(); return cast<ParenExpr>(this)->getSubExpr()->isModifiableLvalue();
default: default:
return false; return false;
} }

View File

@ -226,9 +226,16 @@ private:
QualType UsualUnaryConversion(QualType t); // C99 6.3 QualType UsualUnaryConversion(QualType t); // C99 6.3
QualType UsualArithmeticConversions(QualType t1, QualType t2); // C99 6.3.1.8 QualType UsualArithmeticConversions(QualType t1, QualType t2); // C99 6.3.1.8
enum AssignmentConversionResult {
Compatible,
Incompatible,
PointerFromInt,
IntFromPointer,
IncompatiblePointer
};
// Conversions for assignment, argument passing, initialization, or return // Conversions for assignment, argument passing, initialization, or return
QualType UsualAssignmentConversions(QualType lhs, QualType rhs, // C99 6.5.16 QualType UsualAssignmentConversions(QualType lhs, Expr *rex, // C99 6.5.16
Expr *rex, SourceLocation loc); AssignmentConversionResult &r);
/// the following "Check" methods will either return a well formed AST node /// the following "Check" methods will either return a well formed AST node
/// or will return true if the expressions didn't type check properly. /// or will return true if the expressions didn't type check properly.

View File

@ -22,6 +22,7 @@
#include "clang/Basic/LangOptions.h" #include "clang/Basic/LangOptions.h"
#include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
using namespace llvm; using namespace llvm;
using namespace clang; using namespace clang;
@ -336,17 +337,59 @@ ParseCallExpr(ExprTy *Fn, SourceLocation LParenLoc,
assert(!qType.isNull() && "no type for function call expression"); assert(!qType.isNull() && "no type for function call expression");
QualType canonType = qType.getCanonicalType(); const FunctionType *funcT = dyn_cast<FunctionType>(qType.getCanonicalType());
QualType resultType;
if (const FunctionType *funcT = dyn_cast<FunctionType>(canonType)) { assert(funcT && "ParseCallExpr(): not a function type");
resultType = funcT->getResultType();
// If a prototype isn't declared, the parser implicitly defines a func decl
QualType resultType = funcT->getResultType();
if (const FunctionTypeProto *proto = dyn_cast<FunctionTypeProto>(funcT)) {
// C99 6.5.2.2p7 - the arguments are implicitly converted, as if by
// assignment, to the types of the corresponding parameter, ...
unsigned NumArgsInProto = proto->getNumArgs();
unsigned n = NumArgs;
if (NumArgs < NumArgsInProto)
Diag(LParenLoc, diag::ext_typecheck_call_too_few_args);
else if (NumArgs > NumArgsInProto) { // FIXME: check isVariadic()...
Diag(LParenLoc, diag::ext_typecheck_call_too_many_args);
n = NumArgsInProto;
}
// Continue to check argument types (even if we have too few/many args).
for (unsigned i = 0; i < n; i++) {
QualType lhsType = proto->getArgType(i);
QualType rhsType = ((Expr **)Args)[i]->getType();
if (lhsType == rhsType) // common case, fast path...
continue;
AssignmentConversionResult result;
UsualAssignmentConversions(lhsType, ((Expr **)Args)[i], result);
SourceLocation l = (i == 0) ? LParenLoc : CommaLocs[i-1];
// decode the result (notice that AST's are still created for extensions).
// FIXME: consider fancier error diagnostics (since this is quite common).
// #1: emit the actual prototype arg...requires adding source loc info.
// #2: pass Diag the offending argument type...requires hacking Diag.
switch (result) {
case Compatible:
break;
case PointerFromInt:
Diag(l, diag::ext_typecheck_passing_pointer_from_int, utostr(i+1));
break;
case IntFromPointer:
Diag(l, diag::ext_typecheck_passing_int_from_pointer, utostr(i+1));
break;
case IncompatiblePointer:
Diag(l, diag::ext_typecheck_passing_incompatible_pointer, utostr(i+1));
break;
case Incompatible:
return Diag(l, diag::err_typecheck_passing_incompatible, utostr(i+1));
}
}
} }
// C99 6.5.2.2p7 - If we have a prototype, the arguments are implicitly
// converted, as if by assignment, to the types of the corresponding
// parameters, taking the type of each parameter to be the unqualified...
//
// QualType result = UsualAssignmentConversions(lhsType, rhsType, rex, loc);
return new CallExpr((Expr*)Fn, (Expr**)Args, NumArgs, resultType); return new CallExpr((Expr*)Fn, (Expr**)Args, NumArgs, resultType);
} }
@ -502,8 +545,27 @@ QualType Sema::UsualArithmeticConversions(QualType t1, QualType t2) {
return Context.maxIntegerType(lhs, rhs); return Context.maxIntegerType(lhs, rhs);
} }
QualType Sema::UsualAssignmentConversions(QualType lhsType, QualType rhsType, /// UsualAssignmentConversions (C99 6.5.16) - This routine currently
Expr *rex, SourceLocation loc) { /// has code to accommodate several GCC extensions when type checking
/// pointers. Here are some objectionable examples that GCC considers warnings:
///
/// int a, *pint;
/// short *pshort;
/// struct foo *pfoo;
///
/// pint = pshort; // warning: assignment from incompatible pointer type
/// a = pint; // warning: assignment makes integer from pointer without a cast
/// pint = a; // warning: assignment makes pointer from integer without a cast
/// pint = pfoo; // warning: assignment from incompatible pointer type
///
/// As a result, the code for dealing with pointers is more complex than the
/// C99 spec dictates.
/// Note: the warning above turn into errors when -pedantic-errors is enabled.
///
QualType Sema::UsualAssignmentConversions(QualType lhsType, Expr *rex,
AssignmentConversionResult &r) {
QualType rhsType = rex->getType();
// this check seems unnatural, however it necessary to insure the proper // this check seems unnatural, however it necessary to insure the proper
// conversion of functions/arrays. If the conversion where done for all // conversion of functions/arrays. If the conversion where done for all
// DeclExpr's (created by ParseIdentifierExpr), it would mess up the // DeclExpr's (created by ParseIdentifierExpr), it would mess up the
@ -511,6 +573,7 @@ QualType Sema::UsualAssignmentConversions(QualType lhsType, QualType rhsType,
if (rhsType->isFunctionType() || rhsType->isArrayType()) if (rhsType->isFunctionType() || rhsType->isArrayType())
rhsType = UsualUnaryConversion(rhsType); rhsType = UsualUnaryConversion(rhsType);
r = Compatible;
if (lhsType->isArithmeticType() && rhsType->isArithmeticType()) if (lhsType->isArithmeticType() && rhsType->isArithmeticType())
return lhsType; return lhsType;
else if (lhsType->isPointerType()) { else if (lhsType->isPointerType()) {
@ -518,20 +581,20 @@ QualType Sema::UsualAssignmentConversions(QualType lhsType, QualType rhsType,
// check for null pointer constant (C99 6.3.2.3p3) // check for null pointer constant (C99 6.3.2.3p3)
const IntegerLiteral *constant = dyn_cast<IntegerLiteral>(rex); const IntegerLiteral *constant = dyn_cast<IntegerLiteral>(rex);
if (!constant || constant->getValue() != 0) if (!constant || constant->getValue() != 0)
Diag(loc, diag::ext_typecheck_assign_pointer_from_int); r = PointerFromInt;
return rhsType; return rhsType;
} }
// FIXME: make sure the qualifier are matching // FIXME: make sure the qualifier are matching
if (rhsType->isPointerType()) { if (rhsType->isPointerType()) {
if (!Type::pointerTypesAreCompatible(lhsType, rhsType)) if (!Type::pointerTypesAreCompatible(lhsType, rhsType))
Diag(loc, diag::ext_typecheck_assign_incompatible_pointer); r = IncompatiblePointer;
return rhsType; return rhsType;
} }
} else if (rhsType->isPointerType()) { } else if (rhsType->isPointerType()) {
if (lhsType->isIntegerType()) { if (lhsType->isIntegerType()) {
// C99 6.5.16.1p1: the left operand is _Bool and the right is a pointer. // C99 6.5.16.1p1: the left operand is _Bool and the right is a pointer.
if (lhsType != Context.BoolTy) if (lhsType != Context.BoolTy)
Diag(loc, diag::ext_typecheck_assign_int_from_pointer); r = IntFromPointer;
return rhsType; return rhsType;
} }
// - both operands are pointers to qualified or unqualified versions of // - both operands are pointers to qualified or unqualified versions of
@ -539,7 +602,7 @@ QualType Sema::UsualAssignmentConversions(QualType lhsType, QualType rhsType,
// qualifiers of the type pointed to by the right; // qualifiers of the type pointed to by the right;
if (lhsType->isPointerType()) { if (lhsType->isPointerType()) {
if (!Type::pointerTypesAreCompatible(lhsType, rhsType)) if (!Type::pointerTypesAreCompatible(lhsType, rhsType))
Diag(loc, diag::ext_typecheck_assign_incompatible_pointer); r = IncompatiblePointer;
return rhsType; return rhsType;
} }
} else if (lhsType->isStructureType() && rhsType->isStructureType()) { } else if (lhsType->isStructureType() && rhsType->isStructureType()) {
@ -549,7 +612,8 @@ QualType Sema::UsualAssignmentConversions(QualType lhsType, QualType rhsType,
if (Type::unionTypesAreCompatible(lhsType, rhsType)) if (Type::unionTypesAreCompatible(lhsType, rhsType))
return rhsType; return rhsType;
} }
return QualType(); // incompatible r = Incompatible;
return QualType();
} }
Action::ExprResult Sema::CheckMultiplicativeOperands( Action::ExprResult Sema::CheckMultiplicativeOperands(
@ -656,23 +720,6 @@ Action::ExprResult Sema::CheckLogicalOperands( // C99 6.5.[13,14]
return new BinaryOperator(lex, rex, (BOP)code, Context.IntTy); return new BinaryOperator(lex, rex, (BOP)code, Context.IntTy);
} }
/// CheckAssignmentOperands (C99 6.5.16) - This routine currently
/// has code to accommodate several GCC extensions when type checking
/// pointers. Here are some objectionable examples that GCC considers warnings:
///
/// int a, *pint;
/// short *pshort;
/// struct foo *pfoo;
///
/// pint = pshort; // warning: assignment from incompatible pointer type
/// a = pint; // warning: assignment makes integer from pointer without a cast
/// pint = a; // warning: assignment makes pointer from integer without a cast
/// pint = pfoo; // warning: assignment from incompatible pointer type
///
/// As a result, the code for dealing with pointers is more complex than the
/// C99 spec dictates.
/// Note: the warning above turn into errors when -pedantic-errors is enabled.
///
Action::ExprResult Sema::CheckAssignmentOperands( Action::ExprResult Sema::CheckAssignmentOperands(
Expr *lex, Expr *rex, SourceLocation loc, unsigned code) Expr *lex, Expr *rex, SourceLocation loc, unsigned code)
{ {
@ -680,19 +727,39 @@ Action::ExprResult Sema::CheckAssignmentOperands(
QualType rhsType = rex->getType(); QualType rhsType = rex->getType();
if ((BOP)code == BinaryOperator::Assign) { // C99 6.5.16.1 if ((BOP)code == BinaryOperator::Assign) { // C99 6.5.16.1
if (!lex->isLvalue()) // FIXME: consider hacking isModifiableLvalue to return an enum that
return Diag(loc, diag::ext_typecheck_assign_non_lvalue); // communicates why the expression/type wasn't a modifiableLvalue.
if (lhsType.isConstQualified())
// this check is done first to give a more precise diagnostic.
if (lhsType.isConstQualified())
return Diag(loc, diag::err_typecheck_assign_const); return Diag(loc, diag::err_typecheck_assign_const);
if (!lex->isModifiableLvalue()) // this includes checking for "const"
return Diag(loc, diag::ext_typecheck_assign_non_lvalue);
if (lhsType == rhsType) // common case, fast path... if (lhsType == rhsType) // common case, fast path...
return new BinaryOperator(lex, rex, (BOP)code, lhsType); return new BinaryOperator(lex, rex, (BOP)code, lhsType);
QualType result = UsualAssignmentConversions(lhsType, rhsType, rex, loc); AssignmentConversionResult result;
if (result.isNull()) QualType resType = UsualAssignmentConversions(lhsType, rex, result);
// decode the result (notice that AST's are still created for extensions).
switch (result) {
case Compatible:
break;
case PointerFromInt:
Diag(loc, diag::ext_typecheck_assign_pointer_from_int);
break;
case IntFromPointer:
Diag(loc, diag::ext_typecheck_assign_int_from_pointer);
break;
case IncompatiblePointer:
Diag(loc, diag::ext_typecheck_assign_incompatible_pointer);
break;
case Incompatible:
return Diag(loc, diag::err_typecheck_assign_incompatible); return Diag(loc, diag::err_typecheck_assign_incompatible);
else }
return new BinaryOperator(lex, rex, (BOP)code, result); return new BinaryOperator(lex, rex, (BOP)code, resType);
} }
// FIXME: type check compound assignments... // FIXME: type check compound assignments...
return new BinaryOperator(lex, rex, (BOP)code, Context.IntTy); return new BinaryOperator(lex, rex, (BOP)code, Context.IntTy);
@ -763,7 +830,7 @@ Action::ExprResult
Sema::CheckAddressOfOperand(Expr *op, SourceLocation OpLoc) { Sema::CheckAddressOfOperand(Expr *op, SourceLocation OpLoc) {
Decl *dcl = getPrimaryDeclaration(op); Decl *dcl = getPrimaryDeclaration(op);
if (!op->isLvalue()) { if (!op->isModifiableLvalue()) {
if (dcl && isa<FunctionDecl>(dcl)) if (dcl && isa<FunctionDecl>(dcl))
; // C99 6.5.3.2p1: Allow function designators. ; // C99 6.5.3.2p1: Allow function designators.
else else

View File

@ -251,7 +251,7 @@ static void InitializeLanguageStandard(LangOptions &Options) {
case langkind_c_cpp: case langkind_c_cpp:
case langkind_objc: case langkind_objc:
case langkind_objc_cpp: case langkind_objc_cpp:
LangStd = lang_gnu89; LangStd = lang_gnu99;
break; break;
case langkind_cxx: case langkind_cxx:
case langkind_cxx_cpp: case langkind_cxx_cpp:

View File

@ -226,9 +226,16 @@ private:
QualType UsualUnaryConversion(QualType t); // C99 6.3 QualType UsualUnaryConversion(QualType t); // C99 6.3
QualType UsualArithmeticConversions(QualType t1, QualType t2); // C99 6.3.1.8 QualType UsualArithmeticConversions(QualType t1, QualType t2); // C99 6.3.1.8
enum AssignmentConversionResult {
Compatible,
Incompatible,
PointerFromInt,
IntFromPointer,
IncompatiblePointer
};
// Conversions for assignment, argument passing, initialization, or return // Conversions for assignment, argument passing, initialization, or return
QualType UsualAssignmentConversions(QualType lhs, QualType rhs, // C99 6.5.16 QualType UsualAssignmentConversions(QualType lhs, Expr *rex, // C99 6.5.16
Expr *rex, SourceLocation loc); AssignmentConversionResult &r);
/// the following "Check" methods will either return a well formed AST node /// the following "Check" methods will either return a well formed AST node
/// or will return true if the expressions didn't type check properly. /// or will return true if the expressions didn't type check properly.

View File

@ -22,6 +22,7 @@
#include "clang/Basic/LangOptions.h" #include "clang/Basic/LangOptions.h"
#include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
using namespace llvm; using namespace llvm;
using namespace clang; using namespace clang;
@ -336,17 +337,59 @@ ParseCallExpr(ExprTy *Fn, SourceLocation LParenLoc,
assert(!qType.isNull() && "no type for function call expression"); assert(!qType.isNull() && "no type for function call expression");
QualType canonType = qType.getCanonicalType(); const FunctionType *funcT = dyn_cast<FunctionType>(qType.getCanonicalType());
QualType resultType;
if (const FunctionType *funcT = dyn_cast<FunctionType>(canonType)) { assert(funcT && "ParseCallExpr(): not a function type");
resultType = funcT->getResultType();
// If a prototype isn't declared, the parser implicitly defines a func decl
QualType resultType = funcT->getResultType();
if (const FunctionTypeProto *proto = dyn_cast<FunctionTypeProto>(funcT)) {
// C99 6.5.2.2p7 - the arguments are implicitly converted, as if by
// assignment, to the types of the corresponding parameter, ...
unsigned NumArgsInProto = proto->getNumArgs();
unsigned n = NumArgs;
if (NumArgs < NumArgsInProto)
Diag(LParenLoc, diag::ext_typecheck_call_too_few_args);
else if (NumArgs > NumArgsInProto) { // FIXME: check isVariadic()...
Diag(LParenLoc, diag::ext_typecheck_call_too_many_args);
n = NumArgsInProto;
}
// Continue to check argument types (even if we have too few/many args).
for (unsigned i = 0; i < n; i++) {
QualType lhsType = proto->getArgType(i);
QualType rhsType = ((Expr **)Args)[i]->getType();
if (lhsType == rhsType) // common case, fast path...
continue;
AssignmentConversionResult result;
UsualAssignmentConversions(lhsType, ((Expr **)Args)[i], result);
SourceLocation l = (i == 0) ? LParenLoc : CommaLocs[i-1];
// decode the result (notice that AST's are still created for extensions).
// FIXME: consider fancier error diagnostics (since this is quite common).
// #1: emit the actual prototype arg...requires adding source loc info.
// #2: pass Diag the offending argument type...requires hacking Diag.
switch (result) {
case Compatible:
break;
case PointerFromInt:
Diag(l, diag::ext_typecheck_passing_pointer_from_int, utostr(i+1));
break;
case IntFromPointer:
Diag(l, diag::ext_typecheck_passing_int_from_pointer, utostr(i+1));
break;
case IncompatiblePointer:
Diag(l, diag::ext_typecheck_passing_incompatible_pointer, utostr(i+1));
break;
case Incompatible:
return Diag(l, diag::err_typecheck_passing_incompatible, utostr(i+1));
}
}
} }
// C99 6.5.2.2p7 - If we have a prototype, the arguments are implicitly
// converted, as if by assignment, to the types of the corresponding
// parameters, taking the type of each parameter to be the unqualified...
//
// QualType result = UsualAssignmentConversions(lhsType, rhsType, rex, loc);
return new CallExpr((Expr*)Fn, (Expr**)Args, NumArgs, resultType); return new CallExpr((Expr*)Fn, (Expr**)Args, NumArgs, resultType);
} }
@ -502,8 +545,27 @@ QualType Sema::UsualArithmeticConversions(QualType t1, QualType t2) {
return Context.maxIntegerType(lhs, rhs); return Context.maxIntegerType(lhs, rhs);
} }
QualType Sema::UsualAssignmentConversions(QualType lhsType, QualType rhsType, /// UsualAssignmentConversions (C99 6.5.16) - This routine currently
Expr *rex, SourceLocation loc) { /// has code to accommodate several GCC extensions when type checking
/// pointers. Here are some objectionable examples that GCC considers warnings:
///
/// int a, *pint;
/// short *pshort;
/// struct foo *pfoo;
///
/// pint = pshort; // warning: assignment from incompatible pointer type
/// a = pint; // warning: assignment makes integer from pointer without a cast
/// pint = a; // warning: assignment makes pointer from integer without a cast
/// pint = pfoo; // warning: assignment from incompatible pointer type
///
/// As a result, the code for dealing with pointers is more complex than the
/// C99 spec dictates.
/// Note: the warning above turn into errors when -pedantic-errors is enabled.
///
QualType Sema::UsualAssignmentConversions(QualType lhsType, Expr *rex,
AssignmentConversionResult &r) {
QualType rhsType = rex->getType();
// this check seems unnatural, however it necessary to insure the proper // this check seems unnatural, however it necessary to insure the proper
// conversion of functions/arrays. If the conversion where done for all // conversion of functions/arrays. If the conversion where done for all
// DeclExpr's (created by ParseIdentifierExpr), it would mess up the // DeclExpr's (created by ParseIdentifierExpr), it would mess up the
@ -511,6 +573,7 @@ QualType Sema::UsualAssignmentConversions(QualType lhsType, QualType rhsType,
if (rhsType->isFunctionType() || rhsType->isArrayType()) if (rhsType->isFunctionType() || rhsType->isArrayType())
rhsType = UsualUnaryConversion(rhsType); rhsType = UsualUnaryConversion(rhsType);
r = Compatible;
if (lhsType->isArithmeticType() && rhsType->isArithmeticType()) if (lhsType->isArithmeticType() && rhsType->isArithmeticType())
return lhsType; return lhsType;
else if (lhsType->isPointerType()) { else if (lhsType->isPointerType()) {
@ -518,20 +581,20 @@ QualType Sema::UsualAssignmentConversions(QualType lhsType, QualType rhsType,
// check for null pointer constant (C99 6.3.2.3p3) // check for null pointer constant (C99 6.3.2.3p3)
const IntegerLiteral *constant = dyn_cast<IntegerLiteral>(rex); const IntegerLiteral *constant = dyn_cast<IntegerLiteral>(rex);
if (!constant || constant->getValue() != 0) if (!constant || constant->getValue() != 0)
Diag(loc, diag::ext_typecheck_assign_pointer_from_int); r = PointerFromInt;
return rhsType; return rhsType;
} }
// FIXME: make sure the qualifier are matching // FIXME: make sure the qualifier are matching
if (rhsType->isPointerType()) { if (rhsType->isPointerType()) {
if (!Type::pointerTypesAreCompatible(lhsType, rhsType)) if (!Type::pointerTypesAreCompatible(lhsType, rhsType))
Diag(loc, diag::ext_typecheck_assign_incompatible_pointer); r = IncompatiblePointer;
return rhsType; return rhsType;
} }
} else if (rhsType->isPointerType()) { } else if (rhsType->isPointerType()) {
if (lhsType->isIntegerType()) { if (lhsType->isIntegerType()) {
// C99 6.5.16.1p1: the left operand is _Bool and the right is a pointer. // C99 6.5.16.1p1: the left operand is _Bool and the right is a pointer.
if (lhsType != Context.BoolTy) if (lhsType != Context.BoolTy)
Diag(loc, diag::ext_typecheck_assign_int_from_pointer); r = IntFromPointer;
return rhsType; return rhsType;
} }
// - both operands are pointers to qualified or unqualified versions of // - both operands are pointers to qualified or unqualified versions of
@ -539,7 +602,7 @@ QualType Sema::UsualAssignmentConversions(QualType lhsType, QualType rhsType,
// qualifiers of the type pointed to by the right; // qualifiers of the type pointed to by the right;
if (lhsType->isPointerType()) { if (lhsType->isPointerType()) {
if (!Type::pointerTypesAreCompatible(lhsType, rhsType)) if (!Type::pointerTypesAreCompatible(lhsType, rhsType))
Diag(loc, diag::ext_typecheck_assign_incompatible_pointer); r = IncompatiblePointer;
return rhsType; return rhsType;
} }
} else if (lhsType->isStructureType() && rhsType->isStructureType()) { } else if (lhsType->isStructureType() && rhsType->isStructureType()) {
@ -549,7 +612,8 @@ QualType Sema::UsualAssignmentConversions(QualType lhsType, QualType rhsType,
if (Type::unionTypesAreCompatible(lhsType, rhsType)) if (Type::unionTypesAreCompatible(lhsType, rhsType))
return rhsType; return rhsType;
} }
return QualType(); // incompatible r = Incompatible;
return QualType();
} }
Action::ExprResult Sema::CheckMultiplicativeOperands( Action::ExprResult Sema::CheckMultiplicativeOperands(
@ -656,23 +720,6 @@ Action::ExprResult Sema::CheckLogicalOperands( // C99 6.5.[13,14]
return new BinaryOperator(lex, rex, (BOP)code, Context.IntTy); return new BinaryOperator(lex, rex, (BOP)code, Context.IntTy);
} }
/// CheckAssignmentOperands (C99 6.5.16) - This routine currently
/// has code to accommodate several GCC extensions when type checking
/// pointers. Here are some objectionable examples that GCC considers warnings:
///
/// int a, *pint;
/// short *pshort;
/// struct foo *pfoo;
///
/// pint = pshort; // warning: assignment from incompatible pointer type
/// a = pint; // warning: assignment makes integer from pointer without a cast
/// pint = a; // warning: assignment makes pointer from integer without a cast
/// pint = pfoo; // warning: assignment from incompatible pointer type
///
/// As a result, the code for dealing with pointers is more complex than the
/// C99 spec dictates.
/// Note: the warning above turn into errors when -pedantic-errors is enabled.
///
Action::ExprResult Sema::CheckAssignmentOperands( Action::ExprResult Sema::CheckAssignmentOperands(
Expr *lex, Expr *rex, SourceLocation loc, unsigned code) Expr *lex, Expr *rex, SourceLocation loc, unsigned code)
{ {
@ -680,19 +727,39 @@ Action::ExprResult Sema::CheckAssignmentOperands(
QualType rhsType = rex->getType(); QualType rhsType = rex->getType();
if ((BOP)code == BinaryOperator::Assign) { // C99 6.5.16.1 if ((BOP)code == BinaryOperator::Assign) { // C99 6.5.16.1
if (!lex->isLvalue()) // FIXME: consider hacking isModifiableLvalue to return an enum that
return Diag(loc, diag::ext_typecheck_assign_non_lvalue); // communicates why the expression/type wasn't a modifiableLvalue.
if (lhsType.isConstQualified())
// this check is done first to give a more precise diagnostic.
if (lhsType.isConstQualified())
return Diag(loc, diag::err_typecheck_assign_const); return Diag(loc, diag::err_typecheck_assign_const);
if (!lex->isModifiableLvalue()) // this includes checking for "const"
return Diag(loc, diag::ext_typecheck_assign_non_lvalue);
if (lhsType == rhsType) // common case, fast path... if (lhsType == rhsType) // common case, fast path...
return new BinaryOperator(lex, rex, (BOP)code, lhsType); return new BinaryOperator(lex, rex, (BOP)code, lhsType);
QualType result = UsualAssignmentConversions(lhsType, rhsType, rex, loc); AssignmentConversionResult result;
if (result.isNull()) QualType resType = UsualAssignmentConversions(lhsType, rex, result);
// decode the result (notice that AST's are still created for extensions).
switch (result) {
case Compatible:
break;
case PointerFromInt:
Diag(loc, diag::ext_typecheck_assign_pointer_from_int);
break;
case IntFromPointer:
Diag(loc, diag::ext_typecheck_assign_int_from_pointer);
break;
case IncompatiblePointer:
Diag(loc, diag::ext_typecheck_assign_incompatible_pointer);
break;
case Incompatible:
return Diag(loc, diag::err_typecheck_assign_incompatible); return Diag(loc, diag::err_typecheck_assign_incompatible);
else }
return new BinaryOperator(lex, rex, (BOP)code, result); return new BinaryOperator(lex, rex, (BOP)code, resType);
} }
// FIXME: type check compound assignments... // FIXME: type check compound assignments...
return new BinaryOperator(lex, rex, (BOP)code, Context.IntTy); return new BinaryOperator(lex, rex, (BOP)code, Context.IntTy);
@ -763,7 +830,7 @@ Action::ExprResult
Sema::CheckAddressOfOperand(Expr *op, SourceLocation OpLoc) { Sema::CheckAddressOfOperand(Expr *op, SourceLocation OpLoc) {
Decl *dcl = getPrimaryDeclaration(op); Decl *dcl = getPrimaryDeclaration(op);
if (!op->isLvalue()) { if (!op->isModifiableLvalue()) {
if (dcl && isa<FunctionDecl>(dcl)) if (dcl && isa<FunctionDecl>(dcl))
; // C99 6.5.3.2p1: Allow function designators. ; // C99 6.5.3.2p1: Allow function designators.
else else

View File

@ -35,7 +35,7 @@ protected:
public: public:
QualType getType() const { return TR; } QualType getType() const { return TR; }
/// isLvalue - return true if the expression is one of the following: /// isModifiableLvalue - return true if the expression is one of the following:
/// - name, where name must be a variable /// - name, where name must be a variable
/// - e[i] /// - e[i]
/// - (e), where e must be an lvalue /// - (e), where e must be an lvalue
@ -44,7 +44,7 @@ public:
/// - *e /// - *e
/// - string-constant /// - string-constant
/// ///
bool isLvalue(); bool isModifiableLvalue();
virtual void visit(StmtVisitor &Visitor); virtual void visit(StmtVisitor &Visitor);
static bool classof(const Stmt *T) { static bool classof(const Stmt *T) {

View File

@ -552,7 +552,19 @@ DIAG(ext_typecheck_assign_pointer_from_int, EXTENSION,
DIAG(ext_typecheck_assign_incompatible_pointer, EXTENSION, DIAG(ext_typecheck_assign_incompatible_pointer, EXTENSION,
"assignment from incompatible pointer type") "assignment from incompatible pointer type")
DIAG(ext_typecheck_assign_non_lvalue, ERROR, DIAG(ext_typecheck_assign_non_lvalue, ERROR,
"assignment to non-lvalue") "invalid lvalue in assignment")
DIAG(ext_typecheck_call_too_few_args, ERROR,
"too few arguments to function")
DIAG(ext_typecheck_call_too_many_args, ERROR,
"too many arguments to function")
DIAG(err_typecheck_passing_incompatible, ERROR,
"incompatible type for argument %s")
DIAG(ext_typecheck_passing_int_from_pointer, EXTENSION,
"passing argument %s makes integer from pointer without a cast")
DIAG(ext_typecheck_passing_pointer_from_int, EXTENSION,
"passing argument %s makes pointer from integer without a cast")
DIAG(ext_typecheck_passing_incompatible_pointer, EXTENSION,
"passing argument %s from incompatible pointer type")
// Statements. // Statements.
DIAG(err_continue_not_in_loop, ERROR, DIAG(err_continue_not_in_loop, ERROR,