示例#1
0
static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const ParsedAttr &A,
                                    SourceRange Range) {
  // Although the feature was introduced only in OpenCL C v2.0 s6.11.5, it's
  // useful for OpenCL 1.x too and doesn't require HW support.
  // opencl_unroll_hint can have 0 arguments (compiler
  // determines unrolling factor) or 1 argument (the unroll factor provided
  // by the user).

  unsigned NumArgs = A.getNumArgs();

  if (NumArgs > 1) {
    S.Diag(A.getLoc(), diag::err_attribute_too_many_arguments) << A << 1;
    return nullptr;
  }

  unsigned UnrollFactor = 0;

  if (NumArgs == 1) {
    Expr *E = A.getArgAsExpr(0);
    llvm::APSInt ArgVal(32);

    if (!E->isIntegerConstantExpr(ArgVal, S.Context)) {
      S.Diag(A.getLoc(), diag::err_attribute_argument_type)
          << A << AANT_ArgumentIntegerConstant << E->getSourceRange();
      return nullptr;
    }

    int Val = ArgVal.getSExtValue();

    if (Val <= 0) {
      S.Diag(A.getRange().getBegin(),
             diag::err_attribute_requires_positive_integer)
          << A;
      return nullptr;
    }
    UnrollFactor = Val;
  }

  return OpenCLUnrollHintAttr::CreateImplicit(S.Context, UnrollFactor);
}
示例#2
0
/// ActOnCXXNew - Parsed a C++ 'new' expression (C++ 5.3.4), as in e.g.:
/// @code new (memory) int[size][4] @endcode
/// or
/// @code ::new Foo(23, "hello") @endcode
/// For the interpretation of this heap of arguments, consult the base version.
Action::OwningExprResult
Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
                  SourceLocation PlacementLParen, MultiExprArg PlacementArgs,
                  SourceLocation PlacementRParen, bool ParenTypeId,
                  Declarator &D, SourceLocation ConstructorLParen,
                  MultiExprArg ConstructorArgs,
                  SourceLocation ConstructorRParen)
{
  Expr *ArraySize = 0;
  unsigned Skip = 0;
  // If the specified type is an array, unwrap it and save the expression.
  if (D.getNumTypeObjects() > 0 &&
      D.getTypeObject(0).Kind == DeclaratorChunk::Array) {
    DeclaratorChunk &Chunk = D.getTypeObject(0);
    if (Chunk.Arr.hasStatic)
      return ExprError(Diag(Chunk.Loc, diag::err_static_illegal_in_new)
        << D.getSourceRange());
    if (!Chunk.Arr.NumElts)
      return ExprError(Diag(Chunk.Loc, diag::err_array_new_needs_size)
        << D.getSourceRange());
    ArraySize = static_cast<Expr*>(Chunk.Arr.NumElts);
    Skip = 1;
  }

  QualType AllocType = GetTypeForDeclarator(D, /*Scope=*/0, Skip);
  if (D.getInvalidType())
    return ExprError();

  if (CheckAllocatedType(AllocType, D))
    return ExprError();

  QualType ResultType = AllocType->isDependentType()
                          ? Context.DependentTy
                          : Context.getPointerType(AllocType);

  // That every array dimension except the first is constant was already
  // checked by the type check above.

  // C++ 5.3.4p6: "The expression in a direct-new-declarator shall have integral
  //   or enumeration type with a non-negative value."
  if (ArraySize && !ArraySize->isTypeDependent()) {
    QualType SizeType = ArraySize->getType();
    if (!SizeType->isIntegralType() && !SizeType->isEnumeralType())
      return ExprError(Diag(ArraySize->getSourceRange().getBegin(),
                            diag::err_array_size_not_integral)
        << SizeType << ArraySize->getSourceRange());
    // Let's see if this is a constant < 0. If so, we reject it out of hand.
    // We don't care about special rules, so we tell the machinery it's not
    // evaluated - it gives us a result in more cases.
    if (!ArraySize->isValueDependent()) {
      llvm::APSInt Value;
      if (ArraySize->isIntegerConstantExpr(Value, Context, 0, false)) {
        if (Value < llvm::APSInt(
                        llvm::APInt::getNullValue(Value.getBitWidth()), false))
          return ExprError(Diag(ArraySize->getSourceRange().getBegin(),
                           diag::err_typecheck_negative_array_size)
            << ArraySize->getSourceRange());
      }
    }
  }

  FunctionDecl *OperatorNew = 0;
  FunctionDecl *OperatorDelete = 0;
  Expr **PlaceArgs = (Expr**)PlacementArgs.get();
  unsigned NumPlaceArgs = PlacementArgs.size();
  if (!AllocType->isDependentType() &&
      !Expr::hasAnyTypeDependentArguments(PlaceArgs, NumPlaceArgs) &&
      FindAllocationFunctions(StartLoc,
                              SourceRange(PlacementLParen, PlacementRParen),
                              UseGlobal, AllocType, ArraySize, PlaceArgs,
                              NumPlaceArgs, OperatorNew, OperatorDelete))
    return ExprError();

  bool Init = ConstructorLParen.isValid();
  // --- Choosing a constructor ---
  // C++ 5.3.4p15
  // 1) If T is a POD and there's no initializer (ConstructorLParen is invalid)
  //   the object is not initialized. If the object, or any part of it, is
  //   const-qualified, it's an error.
  // 2) If T is a POD and there's an empty initializer, the object is value-
  //   initialized.
  // 3) If T is a POD and there's one initializer argument, the object is copy-
  //   constructed.
  // 4) If T is a POD and there's more initializer arguments, it's an error.
  // 5) If T is not a POD, the initializer arguments are used as constructor
  //   arguments.
  //
  // Or by the C++0x formulation:
  // 1) If there's no initializer, the object is default-initialized according
  //    to C++0x rules.
  // 2) Otherwise, the object is direct-initialized.
  CXXConstructorDecl *Constructor = 0;
  Expr **ConsArgs = (Expr**)ConstructorArgs.get();
  unsigned NumConsArgs = ConstructorArgs.size();
  if (AllocType->isDependentType()) {
    // Skip all the checks.
  }
  // FIXME: Should check for primitive/aggregate here, not record.
  else if (const RecordType *RT = AllocType->getAsRecordType()) {
    // FIXME: This is incorrect for when there is an empty initializer and
    // no user-defined constructor. Must zero-initialize, not default-construct.
    Constructor = PerformInitializationByConstructor(
                      AllocType, ConsArgs, NumConsArgs,
                      D.getSourceRange().getBegin(),
                      SourceRange(D.getSourceRange().getBegin(),
                                  ConstructorRParen),
                      RT->getDecl()->getDeclName(),
                      NumConsArgs != 0 ? IK_Direct : IK_Default);
    if (!Constructor)
      return ExprError();
  } else {
    if (!Init) {
      // FIXME: Check that no subpart is const.
      if (AllocType.isConstQualified())
        return ExprError(Diag(StartLoc, diag::err_new_uninitialized_const)
          << D.getSourceRange());
    } else if (NumConsArgs == 0) {
      // Object is value-initialized. Do nothing.
    } else if (NumConsArgs == 1) {
      // Object is direct-initialized.
      // FIXME: WHAT DeclarationName do we pass in here?
      if (CheckInitializerTypes(ConsArgs[0], AllocType, StartLoc,
                                DeclarationName() /*AllocType.getAsString()*/,
                                /*DirectInit=*/true))
        return ExprError();
    } else {
      return ExprError(Diag(StartLoc,
                            diag::err_builtin_direct_init_more_than_one_arg)
        << SourceRange(ConstructorLParen, ConstructorRParen));
    }
  }

  // FIXME: Also check that the destructor is accessible. (C++ 5.3.4p16)

  PlacementArgs.release();
  ConstructorArgs.release();
  return Owned(new (Context) CXXNewExpr(UseGlobal, OperatorNew, PlaceArgs,
                        NumPlaceArgs, ParenTypeId, ArraySize, Constructor, Init,
                        ConsArgs, NumConsArgs, OperatorDelete, ResultType,
                        StartLoc, Init ? ConstructorRParen : SourceLocation()));
}
示例#3
0
void Sema::ActOnPragmaPack(PragmaPackKind Kind, IdentifierInfo *Name,
                           ExprTy *alignment, SourceLocation PragmaLoc,
                           SourceLocation LParenLoc, SourceLocation RParenLoc) {
    Expr *Alignment = static_cast<Expr *>(alignment);

    // If specified then alignment must be a "small" power of two.
    unsigned AlignmentVal = 0;
    if (Alignment) {
        llvm::APSInt Val;

        // pack(0) is like pack(), which just works out since that is what
        // we use 0 for in PackAttr.
        if (Alignment->isTypeDependent() ||
                Alignment->isValueDependent() ||
                !Alignment->isIntegerConstantExpr(Val, Context) ||
                !(Val == 0 || Val.isPowerOf2()) ||
                Val.getZExtValue() > 16) {
            Diag(PragmaLoc, diag::warn_pragma_pack_invalid_alignment);
            return; // Ignore
        }

        AlignmentVal = (unsigned) Val.getZExtValue();
    }

    if (PackContext == 0)
        PackContext = new PragmaPackStack();

    PragmaPackStack *Context = static_cast<PragmaPackStack*>(PackContext);

    switch (Kind) {
    case Sema::PPK_Default: // pack([n])
        Context->setAlignment(AlignmentVal);
        break;

    case Sema::PPK_Show: // pack(show)
        // Show the current alignment, making sure to show the right value
        // for the default.
        AlignmentVal = Context->getAlignment();
        // FIXME: This should come from the target.
        if (AlignmentVal == 0)
            AlignmentVal = 8;
        if (AlignmentVal == PackStackEntry::kMac68kAlignmentSentinel)
            Diag(PragmaLoc, diag::warn_pragma_pack_show) << "mac68k";
        else
            Diag(PragmaLoc, diag::warn_pragma_pack_show) << AlignmentVal;
        break;

    case Sema::PPK_Push: // pack(push [, id] [, [n])
        Context->push(Name);
        // Set the new alignment if specified.
        if (Alignment)
            Context->setAlignment(AlignmentVal);
        break;

    case Sema::PPK_Pop: // pack(pop [, id] [,  n])
        // MSDN, C/C++ Preprocessor Reference > Pragma Directives > pack:
        // "#pragma pack(pop, identifier, n) is undefined"
        if (Alignment && Name)
            Diag(PragmaLoc, diag::warn_pragma_pack_pop_identifer_and_alignment);

        // Do the pop.
        if (!Context->pop(Name, /*IsReset=*/false)) {
            // If a name was specified then failure indicates the name
            // wasn't found. Otherwise failure indicates the stack was
            // empty.
            Diag(PragmaLoc, diag::warn_pragma_pack_pop_failed)
                    << (Name ? "no record matching name" : "stack empty");

            // FIXME: Warn about popping named records as MSVC does.
        } else {
            // Pop succeeded, set the new alignment if specified.
            if (Alignment)
                Context->setAlignment(AlignmentVal);
        }
        break;

    default:
        assert(0 && "Invalid #pragma pack kind.");
    }
}
示例#4
0
/* Simplify a logical expression. Currently this eliminates extra parens and
 * casts, and performs basic boolean simplification according to common
 * identities.
 *
 * FIXME: Ideally, this should should be a full boolean expression minimiser,
 * returning in disjunctive normal form. */
static Expr*
_simplify_boolean_expr (Expr* expr, const ASTContext& context)
{
	expr = expr->IgnoreParens ();

	DEBUG ("Simplifying boolean expression of type " <<
	       expr->getStmtClassName ());

	if (expr->getStmtClass () == Expr::UnaryOperatorClass) {
		UnaryOperator& op_expr = cast<UnaryOperator> (*expr);
		Expr* sub_expr =
			_simplify_boolean_expr (op_expr.getSubExpr (), context);

		if (op_expr.getOpcode () != UnaryOperatorKind::UO_LNot) {
			/* op S ↦ op simplify(S) */
			op_expr.setSubExpr (sub_expr);
			return expr;
		}

		if (sub_expr->getStmtClass () == Expr::UnaryOperatorClass) {
			UnaryOperator& op_sub_expr =
				cast<UnaryOperator> (*sub_expr);
			Expr* sub_sub_expr =
				_simplify_boolean_expr (
					op_sub_expr.getSubExpr (), context);

			if (op_sub_expr.getOpcode () ==
			    UnaryOperatorKind::UO_LNot) {
				/* ! ! S ↦ simplify(S) */
				return sub_sub_expr;
			}

			/* ! op S ↦ ! op simplify(S) */
			op_sub_expr.setSubExpr (sub_sub_expr);
			return expr;
		} else if (sub_expr->getStmtClass () ==
		           Expr::BinaryOperatorClass) {
			BinaryOperator& op_sub_expr =
				cast<BinaryOperator> (*sub_expr);
			Expr* lhs =
				_simplify_boolean_expr (
					op_sub_expr.getLHS (), context);
			Expr* rhs =
				_simplify_boolean_expr (
					op_sub_expr.getRHS (), context);

			if (op_sub_expr.getOpcode () ==
			    BinaryOperatorKind::BO_EQ ||
			    op_sub_expr.getOpcode () ==
			    BinaryOperatorKind::BO_NE) {
				/* ! (S1 == S2) ↦
				 *     simplify(S1) != simplify(S2)
				 * or
				 * ! (S1 != S2) ↦
				 *     simplify(S1) == simplify(S2) */
				BinaryOperatorKind opcode =
					(op_sub_expr.getOpcode () ==
					 BinaryOperatorKind::BO_EQ) ?
						BinaryOperatorKind::BO_NE :
						BinaryOperatorKind::BO_EQ;

				return new (context)
					BinaryOperator (lhs, rhs, opcode,
					                context.getLogicalOperationType (),
					                VK_RValue, OK_Ordinary, SourceLocation (),
					                false);
			}

			/* ! (S1 op S2) ↦ ! (simplify(S1) op simplify(S2)) */
			op_sub_expr.setLHS (lhs);
			op_sub_expr.setRHS (rhs);
			return expr;
		}
	} else if (expr->getStmtClass () == Expr::BinaryOperatorClass) {
		BinaryOperator& op_expr = cast<BinaryOperator> (*expr);
		Expr* lhs = _simplify_boolean_expr (op_expr.getLHS (), context);
		Expr* rhs = _simplify_boolean_expr (op_expr.getRHS (), context);

		/* Guaranteed one-hot. */
		bool is_and =
			op_expr.getOpcode () == BinaryOperatorKind::BO_LAnd;
		bool is_or =
			op_expr.getOpcode () == BinaryOperatorKind::BO_LOr;

		if (!is_and && !is_or) {
			/* S1 op S2 ↦ simplify(S1) op simplify(S2) */
			op_expr.setLHS (lhs);
			op_expr.setRHS (rhs);
			return expr;
		}

		llvm::APSInt bool_expr;

		if (lhs->isIntegerConstantExpr (bool_expr, context)) {
			if (is_or && bool_expr.getBoolValue ()) {
				/* 1 || S2 ↦ 1 */
				return new (context)
					IntegerLiteral (context,
					                context.MakeIntValue (1, context.getLogicalOperationType ()),
					                context.getLogicalOperationType (),
					                SourceLocation ());
			} else if (is_and && !bool_expr.getBoolValue ()) {
				/* 0 && S2 ↦ 0 */
				return new (context)
					IntegerLiteral (context,
					                context.MakeIntValue (0, context.getLogicalOperationType ()),
					                context.getLogicalOperationType (),
					                SourceLocation ());
			} else {
				/* 1 && S2 ↦ simplify(S2)
				 * or
				 * 0 || S2 ↦ simplify(S2) */
				return rhs;
			}
		} else if (rhs->isIntegerConstantExpr (bool_expr, context)) {
			if (is_or && bool_expr.getBoolValue ()) {
				/* S1 || 1 ↦ 1 */
				return new (context)
					IntegerLiteral (context,
					                context.MakeIntValue (1, context.getLogicalOperationType ()),
					                context.getLogicalOperationType (),
					                SourceLocation ());
			} else if (is_and && !bool_expr.getBoolValue ()) {
				/* S2 && 0 ↦ 0 */
				return new (context)
					IntegerLiteral (context,
					                context.MakeIntValue (0, context.getLogicalOperationType ()),
					                context.getLogicalOperationType (),
					                SourceLocation ());
			} else {
				/* S1 && 1 ↦ simplify(S1)
				 * or
				 * S1 || 0 ↦ simplify(S1) */
				return lhs;
			}
		}

		/* S1 op S2 ↦ simplify(S1) op simplify(S2) */
		op_expr.setLHS (lhs);
		op_expr.setRHS (rhs);
		return expr;
	}

	return expr;
}
示例#5
0
/* Does the given statement look like:
 *  • g_return_if_fail(…)
 *  • g_return_val_if_fail(…)
 *  • g_assert(…)
 *  • g_assert_*(…)
 *  • assert(…)
 * This is complicated by the fact that if the gmessages.h header isn’t
 * available, they’ll present as CallExpr function calls with those names; if it
 * is available, they’ll be expanded as macros and turn into DoStmts with misc.
 * rubbish beneath.
 *
 * If the statement changes program state at all, return NULL. Otherwise, return
 * the condition which holds for the assertion to be bypassed (i.e. for the
 * assertion to succeed). This function is built recursively, building a boolean
 * expression for the condition based on avoiding branches which call
 * abort()-like functions.
 *
 * This function is based on a transformation of the AST to an augmented boolean
 * expression, using rules documented in each switch case. In this
 * documentation, calc(S) refers to the transformation function. The augmented
 * boolean expressions can be either NULL, or a normal boolean expression
 * (TRUE, FALSE, ∧, ∨, ¬). NULL is used iff the statement potentially changes
 * program state, and poisons any boolean expression:
 *     B ∧ NULL ≡ NULL
 *     B ∨ NULL ≡ NULL
 *     ¬NULL ≡ NULL
 */
Expr*
AssertionExtracter::is_assertion_stmt (Stmt& stmt, const ASTContext& context)
{
	DEBUG ("Checking " << stmt.getStmtClassName () << " for assertions.");

	/* Slow path: walk through the AST, aborting on statements which
	 * potentially mutate program state, and otherwise trying to find a base
	 * function call such as:
	 *  • g_return_if_fail_warning()
	 *  • g_assertion_message()
	 *  • g_assertion_message_*()
	 */
	switch ((int) stmt.getStmtClass ()) {
	case Stmt::StmtClass::CallExprClass: {
		/* Handle a direct function call.
		 * Transformations:
		 *     [g_return_if_fail|assert|…](C) ↦ C
		 *     [g_return_if_fail_warning|__assert_fail|…](C) ↦ FALSE
		 *     other_funcs(…) ↦ NULL */
		CallExpr& call_expr = cast<CallExpr> (stmt);
		FunctionDecl* func = call_expr.getDirectCallee ();
		if (func == NULL)
			return NULL;

		std::string func_name = func->getNameAsString ();
		DEBUG ("CallExpr to function " << func_name);

		if (_is_assertion_name (func_name)) {
			/* Assertion path where the compiler hasn't seen the
			 * definition of the assertion macro, so still thinks
			 * it's a function.
			 *
			 * Extract the assertion condition as the first function
			 * parameter.
			 *
			 * TODO: May need to fix up the condition for macros
			 * like g_assert_null(). */
			return call_expr.getArg (0);
		} else if (_is_assertion_fail_func_name (func_name)) {
			/* Assertion path where the assertion macro has been
			 * expanded and we're on the assertion failure branch.
			 *
			 * In this case, the assertion condition has been
			 * grabbed from an if statement already, so negate it
			 * (to avoid the failure condition) and return. */
			return new (context)
				IntegerLiteral (context, context.MakeIntValue (0, context.getLogicalOperationType ()),
				                context.getLogicalOperationType (),
				                SourceLocation ());
		}

		/* Not an assertion path. */
		return NULL;
	}
	case Stmt::StmtClass::DoStmtClass: {
		/* Handle a do { … } while (0) block (commonly used to allow
		 * macros to optionally be suffixed by a semicolon).
		 * Transformations:
		 *     do { S } while (0) ↦ calc(S)
		 *     do { S } while (C) ↦ NULL
		 * Note the second condition is overly-conservative. No
		 * solutions for the halting problem here. */
		DoStmt& do_stmt = cast<DoStmt> (stmt);
		Stmt* body = do_stmt.getBody ();
		Stmt* cond = do_stmt.getCond ();
		Expr* expr = dyn_cast<Expr> (cond);
		llvm::APSInt bool_expr;

		if (body != NULL &&
		    expr != NULL &&
		    expr->isIntegerConstantExpr (bool_expr, context) &&
		    !bool_expr.getBoolValue ()) {
			return is_assertion_stmt (*body, context);
		}

		return NULL;
	}
	case Stmt::StmtClass::IfStmtClass: {
		/* Handle an if(…) { … } else { … } block.
		 * Transformations:
		 *     if (C) { S1 } else { S2 } ↦
		 *       (C ∧ calc(S1)) ∨ (¬C ∧ calc(S2))
		 *     if (C) { S } ↦ (C ∧ calc(S)) ∨ ¬C
		 * i.e.
		 *     if (C) { S } ≡ if (C) { S } else {}
		 * where {} is an empty compound statement, below. */
		IfStmt& if_stmt = cast<IfStmt> (stmt);
		assert (if_stmt.getThen () != NULL);

		Expr* neg_cond = _negation_expr (if_stmt.getCond (), context);

		Expr* then_assertion =
			is_assertion_stmt (*(if_stmt.getThen ()), context);
		if (then_assertion == NULL)
			return NULL;

		then_assertion =
			_conjunction_expr (if_stmt.getCond (), then_assertion,
			                   context);

		if (if_stmt.getElse () == NULL)
			return _disjunction_expr (then_assertion, neg_cond,
			                          context);

		Expr* else_assertion =
			is_assertion_stmt (*(if_stmt.getElse ()), context);
		if (else_assertion == NULL)
			return NULL;

		else_assertion =
			_conjunction_expr (neg_cond, else_assertion, context);

		return _disjunction_expr (then_assertion, else_assertion,
		                          context);
	}
	case Stmt::StmtClass::ConditionalOperatorClass: {
		/* Handle a ternary operator.
		 * Transformations:
		 *     C ? S1 : S2 ↦
		 *       (C ∧ calc(S1)) ∨ (¬C ∧ calc(S2)) */
		ConditionalOperator& op_expr = cast<ConditionalOperator> (stmt);
		assert (op_expr.getTrueExpr () != NULL);
		assert (op_expr.getFalseExpr () != NULL);

		Expr* neg_cond = _negation_expr (op_expr.getCond (), context);

		Expr* then_assertion =
			is_assertion_stmt (*(op_expr.getTrueExpr ()), context);
		if (then_assertion == NULL)
			return NULL;

		then_assertion =
			_conjunction_expr (op_expr.getCond (), then_assertion,
			                   context);

		Expr* else_assertion =
			is_assertion_stmt (*(op_expr.getFalseExpr ()),
			                   context);
		if (else_assertion == NULL)
			return NULL;

		else_assertion =
			_conjunction_expr (neg_cond, else_assertion, context);

		return _disjunction_expr (then_assertion, else_assertion,
		                          context);
	}
	case Stmt::StmtClass::SwitchStmtClass: {
		/* Handle a switch statement.
		 * Transformations:
		 *     switch (C) { L1: S1; L2: S2; …; Lz: Sz } ↦ NULL
		 * FIXME: This should get a proper transformation sometime. */
		return NULL;
	}
	case Stmt::StmtClass::AttributedStmtClass: {
		/* Handle an attributed statement, e.g. G_LIKELY(…).
		 * Transformations:
		 *     att S ↦ calc(S) */
		AttributedStmt& attr_stmt = cast<AttributedStmt> (stmt);

		Stmt* sub_stmt = attr_stmt.getSubStmt ();
		if (sub_stmt == NULL)
			return NULL;

		return is_assertion_stmt (*sub_stmt, context);
	}
	case Stmt::StmtClass::CompoundStmtClass: {
		/* Handle a compound statement, e.g. { stmt1; stmt2; }.
		 * Transformations:
		 *     S1; S2; …; Sz ↦ calc(S1) ∧ calc(S2) ∧ … ∧ calc(Sz)
		 *     {} ↦ TRUE
		 *
		 * This is implemented by starting with a base TRUE case in the
		 * compound_condition, then taking the conjunction with the next
		 * statement’s assertion condition for each statement in the
		 * compound.
		 *
		 * If the compound is empty, the compound_condition will be
		 * TRUE. Otherwise, it will be (TRUE ∧ …), which will be
		 * simplified later. */
		CompoundStmt& compound_stmt = cast<CompoundStmt> (stmt);
		Expr* compound_condition =
			new (context)
				IntegerLiteral (context,
				                context.MakeIntValue (1, context.getLogicalOperationType ()),
				                context.getLogicalOperationType (),
				                SourceLocation ());

		for (CompoundStmt::const_body_iterator it =
		     compound_stmt.body_begin (),
		     ie = compound_stmt.body_end (); it != ie; ++it) {
			Stmt* body_stmt = *it;
			Expr* body_assertion =
				is_assertion_stmt (*body_stmt, context);

			if (body_assertion == NULL) {
				/* Reached a program state mutation. */
				return NULL;
			}

			/* Update the compound condition. */
			compound_condition =
				_conjunction_expr (compound_condition,
				                   body_assertion, context);

			DEBUG_EXPR ("Compound condition: ", *compound_condition);
		}

		return compound_condition;
	}
	case Stmt::StmtClass::GotoStmtClass:
		/* Handle a goto statement.
		 * Transformations:
		 *     goto L ↦ FALSE */
	case Stmt::StmtClass::ReturnStmtClass: {
		/* Handle a return statement.
		 * Transformations:
		 *     return ↦ FALSE */
		return new (context)
			IntegerLiteral (context,
			                context.MakeIntValue (0, context.getLogicalOperationType ()),
			                context.getLogicalOperationType (),
			                SourceLocation ());
	}
	case Stmt::StmtClass::NullStmtClass:
		/* Handle a null statement.
		 * Transformations:
		 *     ; ↦ TRUE */
	case Stmt::StmtClass::DeclRefExprClass:
		/* Handle a variable reference expression. These don’t modify
		 * program state.
		 * Transformations:
		 *     E ↦ TRUE */
	case Stmt::StmtClass::DeclStmtClass: {
		/* Handle a variable declaration statement. These don’t modify
		 * program state; they only introduce new state, so can’t affect
		 * subsequent assertions. (FIXME: For the moment, we ignore the
		 * possibility of the rvalue modifying program state.)
		 * Transformations:
		 *     T S1 ↦ TRUE
		 *     T S1 = S2 ↦ TRUE */
		return new (context)
			IntegerLiteral (context,
			                context.MakeIntValue (1, context.getLogicalOperationType ()),
			                context.getLogicalOperationType (),
			                SourceLocation ());
	}
	case Stmt::StmtClass::IntegerLiteralClass: {
		/* Handle an integer literal. This doesn’t modify program state,
		 * and evaluates directly to a boolean.
		 * Transformations:
		 *     0 ↦ FALSE
		 *     I ↦ TRUE */
		return dyn_cast<Expr> (&stmt);
	}
	case Stmt::StmtClass::ParenExprClass: {
		/* Handle a parenthesised expression.
		 * Transformations:
		 *     ( S ) ↦ calc(S) */
		ParenExpr& paren_expr = cast<ParenExpr> (stmt);

		Stmt* sub_expr = paren_expr.getSubExpr ();
		if (sub_expr == NULL)
			return NULL;

		return is_assertion_stmt (*sub_expr, context);
	}
	case Stmt::StmtClass::LabelStmtClass: {
		/* Handle a label statement.
		 * Transformations:
		 *     label: S ↦ calc(S) */
		LabelStmt& label_stmt = cast<LabelStmt> (stmt);

		Stmt* sub_stmt = label_stmt.getSubStmt ();
		if (sub_stmt == NULL)
			return NULL;

		return is_assertion_stmt (*sub_stmt, context);
	}
	case Stmt::StmtClass::ImplicitCastExprClass:
	case Stmt::StmtClass::CStyleCastExprClass: {
		/* Handle an explicit or implicit cast.
		 * Transformations:
		 *     (T) S ↦ calc(S) */
		CastExpr& cast_expr = cast<CastExpr> (stmt);

		Stmt* sub_expr = cast_expr.getSubExpr ();
		if (sub_expr == NULL)
			return NULL;

		return is_assertion_stmt (*sub_expr, context);
	}
	case Stmt::StmtClass::GCCAsmStmtClass:
	case Stmt::StmtClass::MSAsmStmtClass:
		/* Inline assembly. There is no way we are parsing this, so
		 * conservatively assume it modifies program state.
		 * Transformations:
		 *     A ↦ NULL */
	case Stmt::StmtClass::BinaryOperatorClass:
		/* Handle a binary operator statement. Since this is being
		 * processed at the top level, it’s most likely an assignment,
		 * so conservatively assume it modifies program state.
		 * Transformations:
		 *     S1 op S2 ↦ NULL */
	case Stmt::StmtClass::UnaryOperatorClass:
		/* Handle a unary operator statement. Since this is being
		 * processed at the top level, it’s not very interesting re.
		 * assertions, even though it probably won’t modify program
		 * state (unless it’s a pre- or post-increment or -decrement
		 * operator). Be conservative and assume it does, though.
		 * Transformations:
		 *     op S ↦ NULL */
	case Stmt::StmtClass::CompoundAssignOperatorClass:
		/* Handle a compound assignment operator, e.g. x += 5. This
		 * definitely modifies program state, so ignore it.
		 * Transformations:
		 *     S1 op S2 ↦ NULL */
	case Stmt::StmtClass::ForStmtClass:
		/* Handle a for statement. We assume these *always* change
		 * program state.
		 * Transformations:
		 *     for (…) { … } ↦ NULL */
	case Stmt::StmtClass::WhileStmtClass: {
		/* Handle a while(…) { … } block. Because we don't want to solve
		 * the halting problem, just assume all while statements cannot
		 * be assertion statements.
		 * Transformations:
		 *     while (C) { S } ↦ NULL
		 */
		return NULL;
	}
	case Stmt::StmtClass::NoStmtClass:
	default:
		WARN_EXPR (__func__ << "() can’t handle statements of type " <<
		           stmt.getStmtClassName (), stmt);
		return NULL;
	}
}