Bug #:
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:
parent
b891de392c
commit
17f76e04d2
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue