--- /dev/null
+//{{{ Banner
+
+//============================================================================
+//
+// infer.cxx
+//
+// Inference for common conflicts.
+//
+//============================================================================
+//####COPYRIGHTBEGIN####
+//
+// ----------------------------------------------------------------------------
+// Copyright (C) 1999, 2000, 2001 Red Hat, Inc.
+//
+// This file is part of the eCos host tools.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// ----------------------------------------------------------------------------
+//
+//####COPYRIGHTEND####
+//============================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s): bartv
+// Contact(s): bartv
+// Date: 1999/11/1
+// Version: 0.01
+//
+//####DESCRIPTIONEND####
+//============================================================================
+
+//}}}
+//{{{ #include's
+
+// ----------------------------------------------------------------------------
+#include "cdlconfig.h"
+
+// Get the infrastructure types, assertions, tracing and similar
+// facilities.
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/cyg_trac.h>
+
+// <cdlcore.hxx> defines everything implemented in this module.
+// It implicitly supplies <string>, <vector> and <map> because
+// the class definitions rely on these headers.
+#include <cdlcore.hxx>
+
+//}}}
+
+//{{{ CdlInfer class
+
+//{{{ Description
+
+// ----------------------------------------------------------------------------
+// The following functions provide the main entry points for inference.
+//
+// 1) bool CdlInfer::make_active(CdlTransaction, CdlValuable, level)
+// Do whatever it takes to make the valuable active in
+// a clean sub-transaction, if possible.
+// 2) bool CdlInfer::make_inactive(CdlTransaction, CdlValuable, level)
+// Do whatever it takes to make the valuable inactive.
+// 3) bool CdlInfer::set_valuable_value(CdlTransaction, CdlValuable, CdlSimpleValue&, level)
+// Try to set the valuable to the specified value, taking into
+// account the different flavors.
+// 4) bool CdlInfer::set_valuable_bool(CdlTransaction, CdlValuable, bool, level)
+// Similar to (3) but deals with the boolean aspect of the valuable
+// rather than the data part.
+// 5) bool CdlInfer::subexpr(CdlTransaction, CdlExpression, int index, CdlSimpleValue& goal, level)
+// Process a sub-expression and try to make it evaluate to the
+// goal.
+// 6) bool CdlInfer::subexpr_bool(CdlTransaction, CdlExpression, int index, bool goal, level)
+// Ditto but only deal with boolean goals. If the expression starts to
+// involve arithmetic etc. then we need to move to CdlInfer::subexpr()
+//
+// As might be expected, the sub-expression handlers contain a big
+// switch statement and calls into various auxiliary functions when
+// necessary.
+//
+// For convenience the various entry points check whether or not the
+// desired condition is already satisfied.
+
+//}}}
+//{{{ Forward declarations
+
+// ----------------------------------------------------------------------------
+
+static bool infer_handle_interface_value(CdlTransaction, CdlInterface, CdlSimpleValue&, int);
+static bool infer_handle_reference_bool(CdlTransaction, CdlValuable, bool, int);
+
+//}}}
+//{{{ CdlInfer::make_active()
+
+// ----------------------------------------------------------------------------
+// Making a node active. This requires the following conditions to be
+// satisfied:
+// 1) the parent must be made active
+// 2) if the parent has flavor bool or booldata, it must be enabled
+// 3) any active_if properties
+
+bool
+CdlInfer::make_active(CdlTransaction transaction, CdlNode node, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("CdlInfer::make_active", "result %d");
+ CYG_REPORT_FUNCARG3XV(transaction, node, level);
+ CYG_PRECONDITION_CLASSC(transaction);
+ CYG_PRECONDITION_CLASSC(node);
+
+ bool result = false;
+ if (transaction->is_active(node)) {
+ result = true;
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+
+ CdlContainer parent = node->get_parent();
+ CYG_ASSERT_CLASSC(parent);
+ if (!transaction->is_active(parent)) {
+ if (!CdlInfer::make_active(transaction, parent, level)) {
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+ }
+ // The parent is now active. Does it have to be enabled as well?
+ CdlValuable parent_valuable = dynamic_cast<CdlValuable>(static_cast<CdlNode>(parent));
+ if (0 != parent_valuable) {
+ CdlValueFlavor flavor = parent_valuable->get_flavor();
+ if (((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) &&
+ !parent_valuable->is_enabled(transaction)) {
+ if (!CdlInfer::set_valuable_bool(transaction, parent_valuable, true, level)) {
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+ }
+ }
+ // The parent is now active and enabled. Are there any active_if properties to worry about?
+ CdlValuable valuable = dynamic_cast<CdlValuable>(node);
+ if (0 != valuable) {
+ std::vector<CdlProperty_GoalExpression> active_if_goals;
+ std::vector<CdlProperty_GoalExpression>::iterator goal_i;
+ valuable->get_active_if_conditions(active_if_goals);
+ for (goal_i = active_if_goals.begin(); goal_i != active_if_goals.end(); goal_i++) {
+
+ CdlEvalContext context(transaction, valuable, *goal_i);
+ try {
+ if (!(*goal_i)->eval(context)) {
+ CdlExpression expr = (*goal_i)->get_expression();
+ if (!CdlInfer::subexpr_bool(transaction, expr, expr->first_subexpression, true, level)) {
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+ }
+ } catch(...) {
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+ }
+
+ }
+
+ result = transaction->is_active(node);
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}
+//{{{ CdlInfer::make_inactive()
+
+// ----------------------------------------------------------------------------
+// Making a node inactive can be done in three ways:
+// 1) if the parent is boolean, try disabling it
+// 2) otherwise if the parent can be made inactive, that will do
+// fine.
+// 3) if there are any active_if properties, they could be considered
+// as well. For now this possibility is ignored.
+
+bool
+CdlInfer::make_inactive(CdlTransaction transaction, CdlNode node, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("CdlInfer::make_inactive", "result %d");
+ CYG_REPORT_FUNCARG3XV(transaction, node, level);
+ CYG_PRECONDITION_CLASSC(transaction);
+ CYG_PRECONDITION_CLASSC(node);
+
+ bool result = false;
+ if (!transaction->is_active(node)) {
+ result = true;
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+
+ CdlContainer parent = node->get_parent();
+ if (0 == parent) {
+ // No point in trying to disable the entire configuration.
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+
+ CdlValuable parent_valuable = dynamic_cast<CdlValuable>(parent);
+ if (0 != parent_valuable) {
+ CdlValueFlavor flavor = parent_valuable->get_flavor();
+ if ((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) {
+ // Since the current node is active the parent must currently be enabled.
+ // A sub-transaction is needed because an alternative approach is
+ // possible later on.
+ CdlTransaction subtransaction = transaction->make(transaction->get_conflict());
+ if (CdlInfer::set_valuable_bool(subtransaction, parent_valuable, false, level)) {
+ subtransaction->commit();
+ delete subtransaction;
+ result = true;
+ CYG_REPORT_RETVAL(result);
+ return result;
+ } else {
+ subtransaction->cancel();
+ delete subtransaction;
+ }
+ }
+ }
+
+ // It is not possible to disable the parent. How about making it inactive?
+ if (CdlInfer::make_inactive(transaction, parent, level)) {
+ result = true;
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+
+ // For now do not try to mess about with active_if conditions.
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}
+//{{{ CdlInfer::set_valuable_value()
+
+// ----------------------------------------------------------------------------
+// Deal with the value part of a valuable. The valuable is known to exist
+// and be active, so this code only deals with the actual value part.
+
+bool
+CdlInfer::set_valuable_value(CdlTransaction transaction, CdlValuable valuable, CdlSimpleValue& goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("CdlInfer::set_valuable_value", "result %d");
+ CYG_REPORT_FUNCARG3XV(transaction, valuable, level);
+ CYG_PRECONDITION_CLASSC(transaction);
+ CYG_PRECONDITION_CLASSC(valuable);
+ CYG_PRECONDITIONC(transaction->is_active(valuable));
+
+ bool result = false;
+
+ const CdlValue& current_value = transaction->get_whole_value(valuable);
+ CdlValueFlavor flavor = current_value.get_flavor();
+ bool bool_goal = goal.get_bool_value();
+
+ switch(flavor) {
+ default :
+ case CdlValueFlavor_None :
+ break;
+
+ case CdlValueFlavor_Bool :
+ if (bool_goal == current_value.is_enabled()) {
+ result = true;
+ } else {
+ if (valuable->is_modifiable() &&
+ (0 == dynamic_cast<CdlLoadable>(valuable)) &&
+ !transaction->changed_by_user(valuable)) {
+
+ valuable->set_enabled(transaction, bool_goal, CdlValueSource_Inferred);
+ valuable->set_source(transaction, CdlValueSource_Inferred);
+ result = transaction->resolve_recursion(level);
+ }
+
+ }
+ break;
+
+ case CdlValueFlavor_BoolData :
+ if (!bool_goal && !current_value.is_enabled()) {
+ result = true;
+ } else if (bool_goal && current_value.is_enabled() && (goal == current_value.get_simple_value())) {
+ result = true;
+ } else {
+ if (valuable->is_modifiable() &&
+ (0 == dynamic_cast<CdlLoadable>(valuable)) &&
+ !transaction->changed_by_user(valuable)) {
+
+ if (!bool_goal) {
+ valuable->disable(transaction, CdlValueSource_Inferred);
+ } else {
+ valuable->enable_and_set_value(transaction, goal, CdlValueSource_Inferred);
+ }
+ valuable->set_source(transaction, CdlValueSource_Inferred);
+ result = transaction->resolve_recursion(level);
+ } else if (0 != dynamic_cast<CdlInterface>(valuable)) {
+ // Interfaces are not directly modifiable, but their implementors are.
+ result = infer_handle_interface_value(transaction, dynamic_cast<CdlInterface>(valuable), goal, level);
+ }
+ }
+ break;
+
+ case CdlValueFlavor_Data:
+ // Now check whether or not the valuable already has the desired value
+ if (goal == current_value.get_simple_value()) {
+ result = true;
+ } else {
+ if (valuable->is_modifiable() &&
+ (0 == dynamic_cast<CdlLoadable>(valuable)) &&
+ !transaction->changed_by_user(valuable)) {
+
+ // Make the change, propagate, and perform further resolution.
+ valuable->set_value(transaction, goal, CdlValueSource_Inferred);
+ valuable->set_source(transaction, CdlValueSource_Inferred);
+ result = transaction->resolve_recursion(level);
+ } else if (0 != dynamic_cast<CdlInterface>(valuable)) {
+ // Interfaces are not directly modifiable, but their implementors are.
+ result = infer_handle_interface_value(transaction, dynamic_cast<CdlInterface>(valuable), goal, level);
+ }
+ }
+ break;
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}
+//{{{ CdlInfer::set_valuable_bool()
+
+// ----------------------------------------------------------------------------
+// Deal with the boolean part of a valuable. It is assumed that active vs.
+// inactive is dealt with elsewhere so this code only needs to worry
+// about the valuable itself.
+
+bool
+CdlInfer::set_valuable_bool(CdlTransaction transaction, CdlValuable valuable, bool goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("CdlInfer::set_valuable_bool", "result %d");
+ CYG_REPORT_FUNCARG4XV(transaction, valuable, goal, level);
+ CYG_PRECONDITION_CLASSC(transaction);
+ CYG_PRECONDITION_CLASSC(valuable);
+
+ bool result = false;
+
+ // Examine the current flavor. If None or Data then the valuable
+ // is always enabled. If BoolData or Boolean then the condition
+ // may be satisfied already, otherwise an attempt must be made
+ // to change the value and see what happens.
+ CdlValueFlavor flavor = valuable->get_flavor();
+ if (CdlValueFlavor_None == flavor) {
+ if (goal) {
+ result = true;
+ }
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+
+ if (CdlValueFlavor_Data == flavor) {
+ std::string value = valuable->get_value(transaction);
+ if (goal) {
+ if (("" != value) && ("0" != value)) {
+ result = true;
+ }
+ } else {
+ if (("" == value) || ("0" == value)) {
+ result = true;
+ }
+ }
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+
+ CYG_ASSERTC((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor));
+ bool enabled = valuable->is_enabled(transaction);
+ if (enabled == goal) {
+ result = true;
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+
+ // enabled != goal, and we have a boolean or booldata item.
+ // Before we actually try making any changes, is this sensible?
+ if (!valuable->is_modifiable() ||
+ (0 != dynamic_cast<CdlLoadable>(valuable)) ||
+ transaction->changed_by_user(valuable)) {
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+ // If we are about to disable a container, better check that this would
+ // not annoy the user either
+ if (!goal) {
+ CdlContainer container = dynamic_cast<CdlContainer>(valuable);
+ if ((0 != container) && transaction->subnode_changed_by_user(container)) {
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+ }
+
+ // Try to change the state, propagate, and perform further resolution.
+ valuable->set_enabled(transaction, goal, CdlValueSource_Inferred);
+ valuable->set_source(transaction, CdlValueSource_Inferred);
+ result = transaction->resolve_recursion(level);
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}
+//{{{ infer_choose()
+
+// ----------------------------------------------------------------------------
+// Given two sub-transactions which may or may not have succeeded, pick the
+// preferred one. This happens for many binary operators.
+
+static bool
+infer_lhs_preferable(CdlTransaction lhs_transaction, bool lhs_result, CdlTransaction rhs_transaction, bool rhs_result)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_choose2", "result %d");
+ CYG_REPORT_FUNCARG4XV(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
+ CYG_PRECONDITIONC(lhs_result || rhs_result);
+
+ bool result = true;
+
+ if (lhs_result && !rhs_result) {
+ // Only the lhs succeeded.
+ result = true;
+ } else if (!lhs_result && rhs_result) {
+ // Only the rhs succeeded.
+ result = false;
+ } else if (lhs_result && rhs_result) {
+ // Both sides succeeded. Next check for user_confirmation.
+ bool lhs_confirm_needed = lhs_transaction->user_confirmation_required();
+ bool rhs_confirm_needed = rhs_transaction->user_confirmation_required();
+ if (lhs_confirm_needed && !rhs_confirm_needed) {
+ result = false;
+ } else if (!lhs_confirm_needed && rhs_confirm_needed) {
+ result = true;
+ } else {
+ // Neither or both of the two sides need user confirmation, so they
+ // are equal in that respect
+ if (lhs_transaction->is_preferable_to(rhs_transaction)) {
+ result = true;
+ } else {
+ result = false;
+ }
+ }
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+// A variant which will actually do the commits and cancels. This is
+// commonly required when doing inferences of binary operators.
+static bool
+infer_choose2(CdlTransaction lhs_transaction, bool lhs_result, CdlTransaction rhs_transaction, bool rhs_result)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_choose2", "result %d");
+ CYG_REPORT_FUNCARG4XV(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
+ bool result = false;
+
+ if (lhs_result || rhs_result) {
+ bool lhs_preferable = infer_lhs_preferable(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
+ if (lhs_preferable) {
+ rhs_transaction->cancel();
+ lhs_transaction->commit();
+ } else {
+ lhs_transaction->cancel();
+ rhs_transaction->commit();
+ }
+ result = true;
+ } else {
+ // Neither side succeeded.
+ lhs_transaction->cancel();
+ rhs_transaction->cancel();
+ }
+
+ // Zero or one of these transactions will have been committed,
+ // neither is still necessary.
+ delete lhs_transaction;
+ delete rhs_transaction;
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}
+//{{{ infer_handle_interface()
+
+// ----------------------------------------------------------------------------
+// Set an interface to a specific value, which should be some number n.
+// If (n == 0) then all implementers must be disabled or made inactive.
+// If (n == 1) then exactly one of the implementers must be active and enabled.
+// Other combinations are not considered here, they could lead to an
+// exponential explosion.
+
+static bool
+infer_handle_interface_value(CdlTransaction transaction, CdlInterface interface, CdlSimpleValue& goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_reference_bool", "result %d");
+ CYG_REPORT_FUNCARG4XV(transaction, interface, &goal, level);
+ bool result = false;
+
+ if (goal.has_integer_value()) {
+ cdl_int real_goal = goal.get_integer_value();
+ if (real_goal == interface->get_integer_value(transaction)) {
+ result = true;
+ } else if (0 == real_goal) {
+ // All implementers must be disabled or made inactive. This
+ // can be achieved by creating a sub-transaction and calling
+ // infer_handle_reference_bool() on all of the implementers.
+ //
+ // However there are no guarantees that the result is what
+ // is intended. Updating a later implementer may as a side
+ // effect cause an earlier one to become active again. Also
+ // there may be confusion with valuables with the data
+ // flavor being given a value of 0. Hence a final check is
+ // needed that the new interface value really is the desired goal.
+ CdlTransaction sub_transaction;
+ std::vector<CdlValuable> implementers;
+ std::vector<CdlValuable>::const_iterator impl_i;
+
+ sub_transaction = transaction->make(transaction->get_conflict());
+ try {
+ interface->get_implementers(implementers);
+ for (impl_i = implementers.begin(); impl_i != implementers.end(); impl_i++) {
+ (void) infer_handle_reference_bool(sub_transaction, *impl_i, false, level);
+ }
+ if (0 == interface->get_integer_value(sub_transaction)) {
+ sub_transaction->commit();
+ result = true;
+ } else {
+ sub_transaction->cancel();
+ }
+ } catch (...) {
+ delete sub_transaction;
+ throw;
+ }
+ delete sub_transaction;
+ sub_transaction = 0;
+
+ } else if (1 == real_goal) {
+ // This is a bit trickier than the above. We need n
+ // sub-transactions, one per implementer. In each
+ // sub-transaction we try to set exactly one of the
+ // implementers to enabled and the rest to disabled.
+ std::vector<CdlValuable> implementers;
+ unsigned int impl_count;
+ unsigned int i, j;
+
+ interface->get_implementers(implementers);
+ impl_count = implementers.size();
+ std::vector<CdlTransaction> sub_transactions;
+ std::vector<bool> results;
+
+ try {
+ for (i = 0; i < impl_count; i++) {
+ CdlTransaction sub_transaction = transaction->make(transaction->get_conflict());
+ sub_transactions.push_back(sub_transaction);
+ results.push_back(false);
+ results[i] = false;
+ }
+ for (i = 0; i < impl_count; i++) {
+ for (j = 0; j < impl_count; j++) {
+ (void) infer_handle_reference_bool(sub_transactions[i], implementers[j], (i == j), level);
+ }
+ if (1 == interface->get_integer_value(sub_transactions[i])) {
+ results[i] = true;
+ }
+ }
+
+ // At this point we may have some combination of successful and unsucessful
+ // sub-transactions, and it is time to choose the best one.
+ CdlTransaction preferred = 0;
+ for (i = 0; i < impl_count; i++) {
+ if (results[i]) {
+ preferred = sub_transactions[i];
+ break;
+ }
+ }
+
+ for (j = i + 1; j < impl_count; j++) {
+ if (results[j]) {
+ if (!infer_lhs_preferable(preferred, true, sub_transactions[j], true)) {
+ preferred = sub_transactions[j];
+ }
+ }
+ }
+
+ // Now either preferred == 0, i.e. all
+ // sub-transactions failed and we want to cancel them
+ // all. Or we have a viable sub-transaction.
+ for (i = 0; i < impl_count; i++) {
+ if (preferred == sub_transactions[i]) {
+ sub_transactions[i]->commit();
+ result = true;
+ } else {
+ sub_transactions[i]->cancel();
+ }
+ delete sub_transactions[i];
+ sub_transactions[i] = 0;
+ }
+
+ } catch(...) {
+ for (i = 0; i < sub_transactions.size(); i++) {
+ if (0 != sub_transactions[i]) {
+ sub_transactions[i]->cancel();
+ delete sub_transactions[i];
+ sub_transactions[i] = 0;
+ }
+ }
+ }
+ }
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}
+//{{{ infer_handle_reference()
+
+// ----------------------------------------------------------------------------
+// We are processing an expression and have reached a point where we
+// need <reference>, !<reference> or <reference>==<value>. The
+// reference may currently be unbound, in which case 0 is the only
+// goal that can be satisfied. If the reference is bound then it may
+// be possible to satisfy the goal by setting the value. In addition
+// it is necessary to worry about active vs. inactive state.
+
+static bool
+infer_handle_reference_bool(CdlTransaction transaction, CdlValuable valuable, bool goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_reference_bool", "result %d");
+ CYG_REPORT_FUNCARG4XV(transaction, valuable, goal, level);
+
+ bool result = false;
+ if (0 == valuable) {
+ if (!goal) {
+ result = true;
+ }
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+
+ // If the valuable should evaluate to true then it must be both active
+ // and be either enabled or have a non-zero value.
+ if (goal) {
+ if (!transaction->is_active(valuable)) {
+ if (!CdlInfer::make_active(transaction, valuable, level)) {
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+ }
+ if (CdlInfer::set_valuable_bool(transaction, valuable, true, level)) {
+ result = true;
+ }
+
+ } else {
+ // If the valuable should evaluate to false then it must be either
+ // inactive or it must be disabled or have a zero value.
+ if (!transaction->is_active(valuable)) {
+ // The goal is already satisfied, no need to proceed
+ result = true;
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+
+ // There is a choice to be made so two sub-transactions are
+ // needed. Disabling is generally preferred to making inactive.
+ CdlTransaction value_transaction = transaction->make(transaction->get_conflict());
+ CdlTransaction inactive_transaction = 0;
+ bool value_result = CdlInfer::set_valuable_bool(value_transaction, valuable, false, level);
+ if (value_result && !value_transaction->user_confirmation_required()) {
+ value_transaction->commit();
+ delete value_transaction;
+ value_transaction = 0;
+ result = true;
+ CYG_REPORT_RETVAL(result);
+ return result;
+ }
+
+ inactive_transaction = transaction->make(transaction->get_conflict());
+ bool inactive_result = CdlInfer::make_inactive(inactive_transaction, valuable, level);
+ if (!inactive_result) {
+ if (value_result) {
+ // Changing the value is the only solution.
+ inactive_transaction->cancel();
+ value_transaction->commit();
+ result = true;
+ } else {
+ inactive_transaction->cancel();
+ value_transaction->cancel();
+ result = false;
+ }
+ } else {
+ if (!value_result) {
+ // Making the valuable inactive is the only solution.
+ value_transaction->cancel();
+ inactive_transaction->commit();
+ result = true;
+ } else if (!inactive_transaction->user_confirmation_required()) {
+ // Disabling the valuable would require user confirmation, making it inactive does not
+ value_transaction->cancel();
+ inactive_transaction->commit();
+ result = true;
+ } else {
+ // Both approaches are valid but would require user confirmation.
+ // Pick the preferred one.
+ if (value_transaction->is_preferable_to(inactive_transaction)) {
+ inactive_transaction->cancel();
+ value_transaction->commit();
+ result = true;
+ } else {
+ value_transaction->cancel();
+ inactive_transaction->commit();
+ result = true;
+ }
+ }
+ }
+
+ delete value_transaction;
+ delete inactive_transaction;
+ value_transaction = 0;
+ inactive_transaction = 0;
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// Try to set a valuable to a particular value. Of course the reference
+// may not be bound yet.
+//
+// First check whether or not the valuable is currently active. If it is
+// inactive and the goal is 0 then we have succeeded. If it is active and
+// the goal is 0 then we could try to make the valuable inactive, but
+// this possibility is ignored for now in case it leads to unexpected
+// behaviour. If it is active then we try to set the value, using
+// CdlInfer::set_valuable_value().
+
+static bool
+infer_handle_reference_value(CdlTransaction transaction, CdlValuable valuable, CdlSimpleValue& goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_reference", "result %d");
+ CYG_REPORT_FUNCARG3XV(transaction, valuable, level);
+
+ bool result = false;
+
+ if (0 == valuable) {
+ if (goal == (cdl_int) 0) {
+ result = true;
+ }
+ } else {
+
+ bool active = transaction->is_active(valuable);
+ if (!active) {
+ if (goal == (cdl_int) 0) {
+ result = true;
+ }
+ } else {
+ result = CdlInfer::set_valuable_value(transaction, valuable, goal, level);
+ }
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}
+//{{{ infer_handle_xxx_constant()
+
+// ----------------------------------------------------------------------------
+// Somewhere in the expression processing we have encountered a string
+// constant. The expression cannot be changed, so either the goal matches
+// the constant or it does not.
+static bool
+infer_handle_string_constant_bool(CdlSimpleValue& constant, bool goal)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_string_constant_bool", "result %d");
+ bool result = false;
+
+ if (goal) {
+ if (("" != constant.get_value()) && ("0" != constant.get_value())) {
+ result = true;
+ }
+ } else {
+ if (("" == constant.get_value()) || ("0" == constant.get_value())) {
+ result = true;
+ }
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+static bool
+infer_handle_string_constant_value(CdlSimpleValue& constant, CdlSimpleValue& goal)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_string_constant_value", "result %d");
+ bool result = false;
+
+ if (constant.get_value() == goal.get_value()) {
+ result = true;
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// Integers are also fairly straightforward.
+static bool
+infer_handle_integer_constant_bool(CdlSimpleValue& constant, bool goal)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_integer_constant_bool", "result %d");
+ CYG_PRECONDITIONC(constant.has_integer_value());
+ bool result = false;
+
+ if (goal) {
+ if (0 != constant.get_integer_value()) {
+ result = true;
+ }
+ } else {
+ if (0 == constant.get_integer_value()) {
+ result = true;
+ }
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+static bool
+infer_handle_integer_constant_value(CdlSimpleValue& constant, CdlSimpleValue& goal)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_integer_constant_value", "result %d");
+ CYG_PRECONDITIONC(constant.has_integer_value());
+ bool result = false;
+
+ if (goal.has_integer_value() && (constant.get_integer_value() == goal.get_integer_value())) {
+ result = true;
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// Doubles are also straightforward, except than an exact comparision may
+// be too strict. There is not a lot that can be done about this right now.
+// Future enhancements to CDL may support tolerances.
+static bool
+infer_handle_double_constant_bool(CdlSimpleValue& constant, bool goal)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_double_constant_bool", "result %d");
+ CYG_PRECONDITIONC(constant.has_double_value());
+ bool result = false;
+
+ if (goal) {
+ if (0.0 != constant.get_double_value()) {
+ result = true;
+ }
+ } else {
+ if (0.0 == constant.get_double_value()) {
+ result = true;
+ }
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+static bool
+infer_handle_double_constant_value(CdlSimpleValue& constant, CdlSimpleValue& goal)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_double_constant_value", "result %d");
+ CYG_PRECONDITIONC(constant.has_double_value());
+ bool result = false;
+
+ if (goal.has_double_value() && (constant.get_double_value() == goal.get_double_value())) {
+ result = true;
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}
+//{{{ infer_handle_logical_xxx()
+
+// ----------------------------------------------------------------------------
+// Logical not simply involves inverting the goal and then trying to infer
+// the rest of the sub-expression. There is little point in touching
+// the other arguments.
+static bool
+infer_handle_logical_NOT_bool(CdlTransaction transaction, CdlExpression expr, unsigned int index, bool goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_logical_NOT_bool", "result %d");
+
+ bool result = CdlInfer::subexpr_bool(transaction, expr, index, !goal, level);
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// Depending on the goal, we want either both sides of the AND to evaluate to
+// true, or we want one of the sides to evaluate to false.
+static bool
+infer_handle_AND_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
+ bool goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_AND_bool", "result %d");
+ CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
+ CYG_PRECONDITION_CLASSC(transaction);
+ CYG_PRECONDITION_CLASSC(expr);
+ CYG_PRECONDITIONC(lhs != rhs);
+
+ bool result = false;
+
+ if (goal) {
+ // Both sides must be true in the same transaction, in case
+ // the solutions overlap in conflicting ways. A sub-transaction
+ // is still used to avoid polluting current values if the lhs
+ // can be inferred but not the rhs.
+ CdlTransaction sub_transaction = transaction->make(transaction->get_conflict());
+ if (CdlInfer::subexpr_bool(sub_transaction, expr, lhs, true, level) &&
+ CdlInfer::subexpr_bool(sub_transaction, expr, rhs, true, level)) {
+ sub_transaction->commit();
+ result = true;
+ } else {
+ sub_transaction->cancel();
+ }
+ delete sub_transaction;
+ } else {
+ // We need to try out both sides of the OR and see which one is preferable.
+ // An optimization would be to only try the LHS, but trying both allows
+ // for a more informed choice.
+ CdlTransaction lhs_transaction = transaction->make(transaction->get_conflict());
+ CdlTransaction rhs_transaction = transaction->make(transaction->get_conflict());
+ bool lhs_result = CdlInfer::subexpr_bool(lhs_transaction, expr, lhs, false, level);
+ bool rhs_result = CdlInfer::subexpr_bool(rhs_transaction, expr, rhs, false, level);
+
+ result = infer_choose2(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+// The support for the other logical operations involves basically minor
+// variants of the above.
+
+static bool
+infer_handle_OR_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
+ bool goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_OR_bool", "result %d");
+ CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
+ CYG_PRECONDITION_CLASSC(transaction);
+ CYG_PRECONDITION_CLASSC(expr);
+ CYG_PRECONDITIONC(lhs != rhs);
+
+
+ bool result = false;
+
+ if (goal) {
+ // We need to try out both sides of the OR and see which one is preferable.
+ // An optimization would be to only try the LHS, but trying both allows
+ // for a more informed choice.
+ CdlTransaction lhs_transaction = transaction->make(transaction->get_conflict());
+ CdlTransaction rhs_transaction = transaction->make(transaction->get_conflict());
+ bool lhs_result = CdlInfer::subexpr_bool(lhs_transaction, expr, lhs, true, level);
+ bool rhs_result = CdlInfer::subexpr_bool(rhs_transaction, expr, rhs, true, level);
+
+ result = infer_choose2(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
+ } else {
+
+ // !(A || B) -> !A && !B
+ CdlTransaction sub_transaction = transaction->make(transaction->get_conflict());
+ if (CdlInfer::subexpr_bool(sub_transaction, expr, lhs, false, level) &&
+ CdlInfer::subexpr_bool(sub_transaction, expr, rhs, false, level)) {
+ sub_transaction->commit();
+ result = true;
+ } else {
+ sub_transaction->cancel();
+ }
+ delete sub_transaction;
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+
+static bool
+infer_handle_IMPLIES_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
+ bool goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_implies_bool", "result %d");
+ CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
+ CYG_PRECONDITION_CLASSC(transaction);
+ CYG_PRECONDITION_CLASSC(expr);
+ CYG_PRECONDITIONC(lhs != rhs);
+
+
+ bool result = false;
+
+ if (goal) {
+ // A implies B -> !A || B
+ // Given a choice between !A or B, arguably the "implies"
+ // operator has the connotation that B is preferred. All other
+ // things being equal, infer_choose2() will prefer the rhs
+ // over the lhs so this is achieved automagically.
+
+ CdlTransaction lhs_transaction = transaction->make(transaction->get_conflict());
+ CdlTransaction rhs_transaction = transaction->make(transaction->get_conflict());
+ bool lhs_result = CdlInfer::subexpr_bool(lhs_transaction, expr, lhs, false, level);
+ bool rhs_result = CdlInfer::subexpr_bool(rhs_transaction, expr, rhs, true, level);
+
+ result = infer_choose2(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
+
+ } else {
+
+ // !(A implies B) -> !(!A || B) -> (A && !B)
+ CdlTransaction sub_transaction = transaction->make(transaction->get_conflict());
+ if (CdlInfer::subexpr_bool(sub_transaction, expr, lhs, true, level) &&
+ CdlInfer::subexpr_bool(sub_transaction, expr, rhs, false, level)) {
+ sub_transaction->commit();
+ result = true;
+ } else {
+ sub_transaction->cancel();
+ }
+ delete sub_transaction;
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+
+static bool
+infer_handle_XOR_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
+ bool goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_XOR_bool", "result %d");
+ CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
+ CYG_PRECONDITION_CLASSC(transaction);
+ CYG_PRECONDITION_CLASSC(expr);
+ CYG_PRECONDITIONC(lhs != rhs);
+
+
+ bool result = false;
+
+ if (goal) {
+ // (A xor B) -> (A && !B) || (!A && B)
+
+ CdlTransaction sub1 = transaction->make(transaction->get_conflict());
+ CdlTransaction sub2 = transaction->make(transaction->get_conflict());
+ bool result1 = (CdlInfer::subexpr_bool(sub1, expr, lhs, true, level) &&
+ CdlInfer::subexpr_bool(sub1, expr, rhs, false, level));
+ bool result2 = (CdlInfer::subexpr_bool(sub2, expr, lhs, false, level) &&
+ CdlInfer::subexpr_bool(sub2, expr, rhs, true, level));
+
+ result = infer_choose2(sub1, result1, sub2, result2);
+
+ } else {
+
+ // !(A xor B) -> (!A && !B) || (A && B)
+ CdlTransaction sub1 = transaction->make(transaction->get_conflict());
+ CdlTransaction sub2 = transaction->make(transaction->get_conflict());
+ bool result1 = (CdlInfer::subexpr_bool(sub1, expr, lhs, false, level) &&
+ CdlInfer::subexpr_bool(sub1, expr, rhs, false, level));
+ bool result2 = (CdlInfer::subexpr_bool(sub2, expr, lhs, true, level) &&
+ CdlInfer::subexpr_bool(sub2, expr, rhs, true, level));
+
+ result = infer_choose2(sub1, result1, sub2, result2);
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+
+static bool
+infer_handle_EQV_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs,
+ bool goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_EQV_bool", "result %d");
+ CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
+ CYG_PRECONDITION_CLASSC(transaction);
+ CYG_PRECONDITION_CLASSC(expr);
+ CYG_PRECONDITIONC(lhs != rhs);
+
+
+ bool result = false;
+
+ if (goal) {
+ // (A eqv B) -> (A && B) || (!A && !B)
+
+ CdlTransaction sub1 = transaction->make(transaction->get_conflict());
+ CdlTransaction sub2 = transaction->make(transaction->get_conflict());
+ bool result1 = (CdlInfer::subexpr_bool(sub1, expr, lhs, true, level) &&
+ CdlInfer::subexpr_bool(sub1, expr, rhs, true, level));
+ bool result2 = (CdlInfer::subexpr_bool(sub2, expr, lhs, false, level) &&
+ CdlInfer::subexpr_bool(sub2, expr, rhs, false, level));
+
+ result = infer_choose2(sub1, result1, sub2, result2);
+ } else {
+ // !(A eqv B) -> (A && !B) || (!A && B)
+
+ CdlTransaction sub1 = transaction->make(transaction->get_conflict());
+ CdlTransaction sub2 = transaction->make(transaction->get_conflict());
+ bool result1 = (CdlInfer::subexpr_bool(sub1, expr, lhs, true, level) &&
+ CdlInfer::subexpr_bool(sub1, expr, rhs, false, level));
+ bool result2 = (CdlInfer::subexpr_bool(sub2, expr, lhs, false, level) &&
+ CdlInfer::subexpr_bool(sub2, expr, rhs, true, level));
+
+ result = infer_choose2(sub1, result1, sub2, result2);
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}
+//{{{ infer_handle_Equal()
+
+// ----------------------------------------------------------------------------
+// Handle expressions of the form A == B. This can be achieved either by
+// evaluating B and trying to assign the result to A, or vice versa. There
+// is a problem if assigning to one side has a side effect on the other, e.g.
+//
+// requires { xyzzy == (xyzzy + 3) }
+//
+// This has to be guarded against by reevaluating the expression.
+//
+// At present this code only copes with equality, not inequality.
+
+static bool
+infer_handle_equal_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs, bool goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_equal_bool", "result %d");
+ CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
+ CYG_PRECONDITION_CLASSC(transaction);
+ CYG_PRECONDITION_CLASSC(expr);
+ CYG_PRECONDITIONC(lhs != rhs);
+
+ bool result = false;
+ if (goal) {
+
+ // We need two sub-transactions, The lhs_transaction is for evaluating the lhs
+ // and trying to update the rhs.
+ CdlTransaction lhs_transaction = transaction->make(transaction->get_conflict());
+ bool lhs_result = false;
+ try {
+ CdlSimpleValue lhs_value;
+ CdlEvalContext lhs_context(lhs_transaction);
+ expr->eval_subexpression(lhs_context, lhs, lhs_value);
+ lhs_result = CdlInfer::subexpr_value(lhs_transaction, expr, rhs, lhs_value, level);
+ if (lhs_result) {
+ CdlSimpleValue check;
+ expr->eval_subexpression(lhs_context, lhs, check);
+ if (lhs_value != check) {
+ lhs_result = false;
+ }
+ }
+ } catch (...) {
+ lhs_result = false;
+ }
+
+ CdlTransaction rhs_transaction = transaction->make(transaction->get_conflict());
+ bool rhs_result = false;
+ try {
+ CdlSimpleValue rhs_value;
+ CdlEvalContext rhs_context(rhs_transaction);
+ expr->eval_subexpression(rhs_context, rhs, rhs_value);
+ rhs_result = CdlInfer::subexpr_value(rhs_transaction, expr, lhs, rhs_value, level);
+ if (rhs_result) {
+ CdlSimpleValue check;
+ expr->eval_subexpression(rhs_context, rhs, check);
+ if (rhs_value != check) {
+ rhs_result = false;
+ }
+ }
+ } catch (...) {
+ rhs_result = false;
+ }
+
+ result = infer_choose2(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}
+//{{{ infer_handle_numerical_equal()
+
+// ----------------------------------------------------------------------------
+// Handle expressions of the form A == B, where the comparison has to be
+// numerical in basis. This is used primarily for operators like <=
+// and >.
+
+static bool
+infer_handle_numerical_equal_bool(CdlTransaction transaction, CdlExpression expr, unsigned int lhs, unsigned int rhs, bool goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("infer_handle_numerical_equal_bool", "result %d");
+ CYG_REPORT_FUNCARG4XV(transaction, expr, lhs, rhs);
+ CYG_PRECONDITION_CLASSC(transaction);
+ CYG_PRECONDITION_CLASSC(expr);
+ CYG_PRECONDITIONC(lhs != rhs);
+
+ bool result = false;
+ if (goal) {
+
+ // We need two sub-transactions, The lhs_transaction is for evaluating the lhs
+ // and trying to update the rhs.
+ CdlTransaction lhs_transaction = transaction->make(transaction->get_conflict());
+ bool lhs_result = false;
+ try {
+ CdlSimpleValue lhs_value;
+ CdlEvalContext lhs_context(lhs_transaction);
+ expr->eval_subexpression(lhs_context, lhs, lhs_value);
+ if (lhs_value.has_integer_value() || lhs_value.has_double_value()) {
+ lhs_result = CdlInfer::subexpr_value(lhs_transaction, expr, rhs, lhs_value, level);
+ if (lhs_result) {
+ CdlSimpleValue check;
+ expr->eval_subexpression(lhs_context, lhs, check);
+ if (lhs_value != check) {
+ lhs_result = false;
+ }
+ }
+ }
+ } catch (...) {
+ lhs_result = false;
+ }
+
+ CdlTransaction rhs_transaction = transaction->make(transaction->get_conflict());
+ bool rhs_result = false;
+ try {
+ CdlSimpleValue rhs_value;
+ CdlEvalContext rhs_context(rhs_transaction);
+ expr->eval_subexpression(rhs_context, rhs, rhs_value);
+ if (rhs_value.has_integer_value() || rhs_value.has_double_value()) {
+ rhs_result = CdlInfer::subexpr_value(rhs_transaction, expr, lhs, rhs_value, level);
+ if (rhs_result) {
+ CdlSimpleValue check;
+ expr->eval_subexpression(rhs_context, rhs, check);
+ if (rhs_value != check) {
+ rhs_result = false;
+ }
+ }
+ }
+ } catch (...) {
+ rhs_result = false;
+ }
+
+ result = infer_choose2(lhs_transaction, lhs_result, rhs_transaction, rhs_result);
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}
+//{{{ CdlInfer::subexpr_bool()
+
+// ----------------------------------------------------------------------------
+bool
+CdlInfer::subexpr_bool(CdlTransaction transaction, CdlExpression expr, unsigned int index, bool goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("CdlInfer::subexpr_bool", "result %d");
+ CYG_REPORT_FUNCARG5XV(transaction, expr, index, goal, level);
+ CYG_PRECONDITION_CLASSC(transaction);
+ CYG_PRECONDITION_CLASSC(expr);
+ CYG_PRECONDITIONC((0 <= index) && (index < expr->sub_expressions.size()));
+
+ bool result = false;
+ CdlSubexpression& subexpr = expr->sub_expressions[index];
+
+ switch(subexpr.op) {
+
+ case CdlExprOp_Reference :
+ // The most common case. Follow the reference, and call the appropriate function.
+ // Note that the reference may be unbound.
+ {
+ CdlNode node = expr->references[subexpr.reference_index].get_destination();
+ CdlValuable valuable = 0;
+ if (0 != node) {
+ valuable = dynamic_cast<CdlValuable>(node);
+ }
+ result = infer_handle_reference_bool(transaction, valuable, goal, level);
+ break;
+ }
+
+ case CdlExprOp_StringConstant :
+ result = infer_handle_string_constant_bool(subexpr.constants, goal);
+ break;
+
+ case CdlExprOp_IntegerConstant :
+ result = infer_handle_integer_constant_bool(subexpr.constants, goal);
+ break;
+
+ case CdlExprOp_DoubleConstant :
+ result = infer_handle_double_constant_bool(subexpr.constants, goal);
+ break;
+
+ case CdlExprOp_LogicalNot :
+ result = infer_handle_logical_NOT_bool(transaction, expr, subexpr.lhs_index, goal, level);
+ break;
+
+ case CdlExprOp_And :
+ result = infer_handle_AND_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
+ break;
+
+ case CdlExprOp_Or :
+ result = infer_handle_OR_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
+ break;
+
+ case CdlExprOp_Implies :
+ result = infer_handle_IMPLIES_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
+ break;
+
+ case CdlExprOp_Xor :
+ result = infer_handle_XOR_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
+ break;
+
+ case CdlExprOp_Eqv :
+ result = infer_handle_EQV_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
+ break;
+
+ case CdlExprOp_Equal :
+ result = infer_handle_equal_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, goal, level);
+ break;
+
+ case CdlExprOp_NotEqual :
+ result = infer_handle_equal_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, !goal, level);
+ break;
+
+ // <= is satisfied by a numerical equality. However the inverse relation > cannot be handled that way
+ // The other comparison operators are much the same.
+ case CdlExprOp_LessEqual :
+ case CdlExprOp_GreaterEqual :
+ if (goal) {
+ result = infer_handle_numerical_equal_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, true, level);
+ }
+ break;
+
+ case CdlExprOp_LessThan :
+ case CdlExprOp_GreaterThan :
+ if (!goal) {
+ result = infer_handle_numerical_equal_bool(transaction, expr, subexpr.lhs_index, subexpr.rhs_index, true, level);
+ }
+ break;
+
+ case CdlExprOp_Function :
+ result = CdlFunction::infer_bool(transaction, expr, index, goal, level);
+ break;
+
+ default:
+ // No other inferences are implemented at this stage.
+ break;
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}
+//{{{ CdlInfer::subexpr_value()
+
+bool
+CdlInfer::subexpr_value(CdlTransaction transaction, CdlExpression expr, unsigned int index, CdlSimpleValue& goal, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("CdlInfer::subexpr_value", "result %d");
+ CYG_REPORT_FUNCARG4XV(transaction, expr, index, level);
+ CYG_PRECONDITION_CLASSC(transaction);
+ CYG_PRECONDITION_CLASSC(expr);
+ CYG_PRECONDITIONC((0 <= index) && (index < expr->sub_expressions.size()));
+
+ bool result = false;
+ CdlSubexpression& subexpr = expr->sub_expressions[index];
+
+ switch(subexpr.op) {
+
+ case CdlExprOp_Reference :
+ // The most common case. Follow the reference, and call the appropriate function.
+ // Note that the reference may be unbound.
+ {
+ CdlNode node = expr->references[subexpr.reference_index].get_destination();
+ CdlValuable valuable = 0;
+ if (0 != node) {
+ valuable = dynamic_cast<CdlValuable>(node);
+ }
+ result = infer_handle_reference_value(transaction, valuable, goal, level);
+ break;
+ }
+
+ case CdlExprOp_StringConstant :
+ result = infer_handle_string_constant_value(subexpr.constants, goal);
+ break;
+
+ case CdlExprOp_IntegerConstant :
+ result = infer_handle_integer_constant_value(subexpr.constants, goal);
+ break;
+
+ case CdlExprOp_DoubleConstant :
+ result = infer_handle_double_constant_value(subexpr.constants, goal);
+ break;
+
+ case CdlExprOp_LogicalNot :
+ case CdlExprOp_And :
+ case CdlExprOp_Or :
+ case CdlExprOp_Implies :
+ case CdlExprOp_Xor :
+ case CdlExprOp_Eqv :
+ {
+ bool new_goal = true;
+ if (("0" == goal.get_value()) || ("" == goal.get_value())) {
+ new_goal = false;
+ }
+ result = CdlInfer::subexpr_bool(transaction, expr, index, new_goal, level);
+ break;
+ }
+
+ case CdlExprOp_Function :
+ result = CdlFunction::infer_value(transaction, expr, index, goal, level);
+ break;
+
+ default:
+ // No other inferences are implemented at this stage.
+ break;
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}
+
+//}}}
+//{{{ Illegal value resolution
+
+// ----------------------------------------------------------------------------
+// This is not yet implemented.
+
+bool
+CdlConflict_IllegalValueBody::inner_resolve(CdlTransaction transaction, int level)
+{
+ CYG_REPORT_FUNCNAMETYPE("CdlConflict_IllegalValue::inner_resolve", "result %d");
+ CYG_REPORT_FUNCARG3XV(this, transaction, level);
+ CYG_PRECONDITION_THISC();
+ CYG_PRECONDITION_CLASSC(transaction);
+
+ CYG_UNUSED_PARAM(CdlTransaction, transaction);
+
+ CYG_REPORT_RETVAL(false);
+ return false;
+}
+
+//}}}
+//{{{ Requires resolution
+
+// ----------------------------------------------------------------------------
+// The entry point for this code is
+// CdlConflict_RequiresBody::resolve(). "this" is a requires conflict
+// that needs to be resolved, if possible. There are twos argument: a
+// sub-transaction, which should be filled in with the solution if
+// possible; and a recursion level indicator, 0 if this is a top-level
+// inference engine invocation rather than a recursive one. There are
+// additional static parameters inference_recursion_limit and
+// inference_override which control details of the inference process.
+//
+// As an example of what is involved in an inference, consider the
+// simple case of a "requires XXX" property. This constraint may not
+// be satisfied because XXX is disabled, because XXX is inactive,
+// or both.
+//
+// Assume for simplicity that XXX is already active. The inference
+// engine can now figure out that XXX must be enabled (it must be
+// of type bool or booldata, or else the conflict would not have
+// arisen). This is achieved by creating a sub-transaction,
+// enabling XXX in that sub-transaction, propagating the
+// sub-transaction and performing further inference. The inference
+// is successfull if no new conflicts are introduced.
+//
+// However, even if a solution is found it is not necessarily
+// acceptable without user confirmation, subject to
+// inference_override. This is handled in part by the transaction
+// class itself, in the resolve() and user_confirmation_required()
+// members. In cases where the inference engine can choose between
+// several alternatives it needs to consider this issue for each one.
+// Resolving a requires conflict. There are three ways of tackling
+// this problem, in order of preference:
+//
+// 1) change the terms in the expression to make it evaluate to
+// true.
+// 2) disable the source so that the requires property is no longer
+// relevant.
+// 3) make the source inactive, with the same effect.
+//
+// The first one should always be tried. If it is entirely successful
+// then there is no point in looking any further. If user confirmation
+// is required then the second approach should be tried. If that is
+// entirely successful then there is no point in looking further.
+// If user confirmation is required then the third approach should
+// be tried.
+
+bool
+CdlConflict_RequiresBody::inner_resolve(CdlTransaction transaction, int level)
+{
+ CYG_REPORT_FUNCNAME("CdlConflict_Requires::inner_resolve");
+ CYG_REPORT_FUNCARG3XV(this, transaction, level);
+ CYG_PRECONDITION_THISC();
+ CYG_PRECONDITION_CLASSC(transaction);
+
+ bool result = false;
+
+ CdlProperty_GoalExpression gexpr = dynamic_cast<CdlProperty_GoalExpression>(this->get_property());
+ CdlExpression expr = gexpr->get_expression();
+
+ // Only create the sub-transactions when needed.
+ CdlTransaction expr_transaction = 0;
+ CdlTransaction disable_transaction = 0;
+ CdlTransaction inactive_transaction = 0;
+
+ // Keep track of the preferred solution found to date.
+ CdlTransaction preferred_transaction = 0;
+
+ expr_transaction = transaction->make(this);
+ if (!CdlInfer::subexpr_bool(expr_transaction, expr, expr->first_subexpression, true, level)) {
+ // No luck here.
+ expr_transaction->cancel();
+ delete expr_transaction;
+ expr_transaction = 0;
+ } else {
+ // We have a possible solution. How acceptable is it?
+ if (!expr_transaction->user_confirmation_required()) {
+ // Whoopee.
+ expr_transaction->commit();
+ delete expr_transaction;
+ result = true;
+ CYG_REPORT_RETVAL(result);
+ return result;
+ } else {
+ // Maybe we can do better.
+ preferred_transaction = expr_transaction;
+ expr_transaction = 0;
+ }
+ }
+
+ // Disabling the source only makes sense if we have a bool or booldata item.
+ CdlValuable valuable = dynamic_cast<CdlValuable>(this->get_node());
+ CYG_ASSERT_CLASSC(valuable);
+
+ if ((CdlValueFlavor_Bool == valuable->get_flavor()) || (CdlValueFlavor_BoolData == valuable->get_flavor())) {
+ disable_transaction = transaction->make(this);
+ if (!CdlInfer::set_valuable_bool(disable_transaction, valuable, false, level)) {
+ // No luck here either.
+ disable_transaction->cancel();
+ delete disable_transaction;
+ disable_transaction = 0;
+ } else {
+ if (!disable_transaction->user_confirmation_required()) {
+ disable_transaction->commit();
+ delete disable_transaction;
+ if (0 != preferred_transaction) {
+ preferred_transaction->cancel();
+ delete preferred_transaction;
+ preferred_transaction = 0;
+ }
+ result = true;
+ CYG_REPORT_RETVAL(result);
+ return result;
+ } else if (0 == preferred_transaction) {
+ preferred_transaction = disable_transaction;
+ } else if (!preferred_transaction->is_preferable_to(disable_transaction)) {
+ preferred_transaction->cancel();
+ delete preferred_transaction;
+ preferred_transaction = disable_transaction;
+ disable_transaction = 0;
+ } else {
+ disable_transaction->cancel();
+ delete disable_transaction;
+ disable_transaction = 0;
+ }
+ }
+ }
+
+ // Now try for the inactive approach. This may work in cases where the disable
+ // approach does not if e.g. there are dependencies between two nodes in the
+ // same container, or if the source of the conflict is not boolean.
+ inactive_transaction = transaction->make(this);
+ if (!CdlInfer::make_inactive(inactive_transaction, valuable, level)) {
+ inactive_transaction->cancel();
+ delete inactive_transaction;
+ inactive_transaction = 0;
+ } else {
+ if (!inactive_transaction->user_confirmation_required()) {
+ inactive_transaction->commit();
+ delete inactive_transaction;
+ if (0 != preferred_transaction) {
+ preferred_transaction->cancel();
+ delete preferred_transaction;
+ preferred_transaction = 0;
+ }
+ result = true;
+ CYG_REPORT_RETVAL(result);
+ return result;
+ } else if (0 == preferred_transaction) {
+ preferred_transaction = inactive_transaction;
+ } else if (!preferred_transaction->is_preferable_to(inactive_transaction)) {
+ preferred_transaction->cancel();
+ delete preferred_transaction;
+ preferred_transaction = inactive_transaction;
+ inactive_transaction = 0;
+ } else {
+ inactive_transaction->cancel();
+ delete inactive_transaction;
+ inactive_transaction = 0;
+ }
+ }
+
+ // Is there any solution at all? If so then use the currently-preferred one.
+ if (0 != preferred_transaction) {
+ preferred_transaction->commit();
+ delete preferred_transaction;
+ preferred_transaction = 0;
+ result = true;
+ }
+
+ CYG_REPORT_RETVAL(result);
+ return result;
+}
+
+//}}}