2013-07-29 05:34:39 +00:00
|
|
|
/*
|
|
|
|
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
|
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
|
|
|
|
|
|
Author: Leonardo de Moura
|
|
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <iostream>
|
|
|
|
#include <memory>
|
2013-12-13 00:33:31 +00:00
|
|
|
#include <vector>
|
|
|
|
#include <set>
|
2013-12-29 01:31:35 +00:00
|
|
|
#include <string>
|
2013-12-30 05:59:57 +00:00
|
|
|
#include <utility>
|
2013-12-13 00:33:31 +00:00
|
|
|
#include "util/lua.h"
|
|
|
|
#include "util/shared_mutex.h"
|
2013-12-18 19:34:40 +00:00
|
|
|
#include "util/name_map.h"
|
2013-09-13 10:35:29 +00:00
|
|
|
#include "kernel/object.h"
|
|
|
|
#include "kernel/level.h"
|
2013-07-29 05:34:39 +00:00
|
|
|
|
|
|
|
namespace lean {
|
2013-12-13 00:33:31 +00:00
|
|
|
class environment;
|
|
|
|
class ro_environment;
|
|
|
|
class type_checker;
|
|
|
|
class environment_extension;
|
|
|
|
|
|
|
|
/** \brief Implementation of the Lean environment. */
|
2013-12-13 21:54:09 +00:00
|
|
|
class environment_cell {
|
2013-12-13 00:33:31 +00:00
|
|
|
friend class environment;
|
2013-12-13 21:54:09 +00:00
|
|
|
friend class read_write_shared_environment;
|
|
|
|
friend class read_only_shared_environment;
|
2013-12-13 00:33:31 +00:00
|
|
|
// Remark: only named objects are stored in the dictionary.
|
2013-12-18 19:34:40 +00:00
|
|
|
typedef name_map<object> object_dictionary;
|
2013-12-13 00:33:31 +00:00
|
|
|
typedef std::tuple<level, level, int> constraint;
|
|
|
|
std::weak_ptr<environment_cell> m_this;
|
|
|
|
// Children environment management
|
|
|
|
atomic<unsigned> m_num_children;
|
|
|
|
std::shared_ptr<environment_cell> m_parent;
|
|
|
|
// Object management
|
|
|
|
std::vector<object> m_objects;
|
|
|
|
object_dictionary m_object_dictionary;
|
2014-02-19 06:55:00 +00:00
|
|
|
|
|
|
|
// std::unique_ptr<type_checker> m_type_checker;
|
2013-12-13 00:33:31 +00:00
|
|
|
std::set<name> m_imported_modules; // set of imported files and builtin modules
|
2013-12-29 11:43:45 +00:00
|
|
|
bool m_trust_imported; // if true, then imported modules are not type checked.
|
|
|
|
bool m_type_check; // auxiliary flag used to implement m_trust_imported.
|
2013-12-13 00:33:31 +00:00
|
|
|
std::vector<std::unique_ptr<environment_extension>> m_extensions;
|
|
|
|
friend class environment_extension;
|
|
|
|
|
|
|
|
// This mutex is only used to implement threadsafe environment objects
|
|
|
|
// in the external APIs
|
|
|
|
shared_mutex m_mutex;
|
|
|
|
|
|
|
|
environment env() const;
|
|
|
|
|
|
|
|
void inc_children() { m_num_children++; }
|
|
|
|
void dec_children() { m_num_children--; }
|
|
|
|
|
|
|
|
environment_extension & get_extension_core(unsigned extid);
|
|
|
|
environment_extension const & get_extension_core(unsigned extid) const;
|
|
|
|
|
|
|
|
unsigned get_max_weight(expr const & e);
|
|
|
|
void check_name_core(name const & n);
|
|
|
|
void check_name(name const & n);
|
|
|
|
|
|
|
|
void register_named_object(object const & new_obj);
|
|
|
|
optional<object> get_object_core(name const & n) const;
|
|
|
|
|
2014-02-19 06:55:00 +00:00
|
|
|
void check_no_mlocal(expr const & e);
|
2013-08-06 19:24:20 +00:00
|
|
|
void check_type(name const & n, expr const & t, expr const & v);
|
2014-02-19 06:55:00 +00:00
|
|
|
void check_type(expr const & t);
|
2013-12-13 00:33:31 +00:00
|
|
|
void check_new_definition(name const & n, expr const & t, expr const & v);
|
2013-11-18 02:11:44 +00:00
|
|
|
|
2013-12-13 00:33:31 +00:00
|
|
|
bool mark_imported_core(name n);
|
2013-12-29 03:20:04 +00:00
|
|
|
bool load_core(std::string const & fname, io_state const & ios, optional<std::string> const & mod_name);
|
2014-01-15 00:41:40 +00:00
|
|
|
bool already_imported(name const & n) const;
|
|
|
|
/** \brief Return true iff the given file was not already marked as imported. It will also mark the file as imported. */
|
|
|
|
bool mark_imported(char const * fname);
|
2013-11-17 03:18:15 +00:00
|
|
|
|
2013-12-13 00:33:31 +00:00
|
|
|
public:
|
|
|
|
environment_cell();
|
2013-12-24 19:32:09 +00:00
|
|
|
environment_cell(std::shared_ptr<environment_cell> const & parent);
|
2013-12-13 00:33:31 +00:00
|
|
|
~environment_cell();
|
2013-08-09 22:33:34 +00:00
|
|
|
|
|
|
|
/** \brief Return true iff this environment has children environments. */
|
2013-12-13 00:33:31 +00:00
|
|
|
bool has_children() const { return m_num_children > 0; }
|
|
|
|
/** \brief Return true iff this environment has a parent environment */
|
|
|
|
bool has_parent() const { return m_parent != nullptr; }
|
2013-08-09 22:33:34 +00:00
|
|
|
|
|
|
|
/**
|
2013-12-13 00:33:31 +00:00
|
|
|
\brief Return parent environment of this environment.
|
|
|
|
\pre has_parent()
|
2013-08-09 22:33:34 +00:00
|
|
|
*/
|
|
|
|
environment parent() const;
|
2013-12-13 00:33:31 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Create a child environment. This environment will only allow "read-only" operations until
|
|
|
|
all children environments are deleted.
|
|
|
|
*/
|
|
|
|
environment mk_child() const;
|
2013-08-09 22:33:34 +00:00
|
|
|
|
|
|
|
// =======================================
|
|
|
|
// Environment Objects
|
2013-09-04 15:30:04 +00:00
|
|
|
|
2013-08-05 01:26:01 +00:00
|
|
|
/**
|
|
|
|
\brief Add a new definition n : t := v.
|
|
|
|
It throws an exception if v does not have type t.
|
2013-08-05 03:52:14 +00:00
|
|
|
It throws an exception if there is already an object with the given name.
|
2013-08-05 01:26:01 +00:00
|
|
|
*/
|
|
|
|
void add_definition(name const & n, expr const & t, expr const & v, bool opaque = false);
|
refactor(kernel): add unfold_opaque flag to normalizer, modify how type checker uses the opaque flag, remove hidden_defs, and mark most builtin definitions as opaque
After this commit, in the type checker, when checking convertability, we first compute a normal form without expanding opaque terms.
If the terms are convertible, then we are done, and saved a lot of time by not expanding unnecessary definitions.
If they are not, instead of throwing an error, we try again expanding the opaque terms.
This seems to be the best of both worlds.
The opaque flag is a hint for the type checker, but it would never prevent us from type checking a valid term.
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2013-12-20 20:47:36 +00:00
|
|
|
void add_opaque_definition(name const & n, expr const & t, expr const & v) { add_definition(n, t, v, true); }
|
2013-08-06 19:24:20 +00:00
|
|
|
void add_theorem(name const & n, expr const & t, expr const & v);
|
2013-08-05 01:26:01 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Add a new definition n : infer_type(v) := v.
|
2013-08-05 03:52:14 +00:00
|
|
|
It throws an exception if there is already an object with the given name.
|
2013-08-05 01:26:01 +00:00
|
|
|
*/
|
|
|
|
void add_definition(name const & n, expr const & v, bool opaque = false);
|
|
|
|
|
2013-12-20 18:45:44 +00:00
|
|
|
/**
|
|
|
|
\brief Set the given definition as opaque (or not)
|
|
|
|
|
|
|
|
\remark It throws an error if \c n is not a definition.
|
|
|
|
*/
|
|
|
|
void set_opaque(name const & n, bool opaque);
|
|
|
|
|
2013-08-05 01:26:01 +00:00
|
|
|
/**
|
2013-08-06 03:06:07 +00:00
|
|
|
\brief Add a new fact (Axiom or Fact) to the environment.
|
2013-08-05 03:52:14 +00:00
|
|
|
It throws an exception if there is already an object with the given name.
|
2013-08-05 01:26:01 +00:00
|
|
|
*/
|
2013-08-06 03:06:07 +00:00
|
|
|
void add_axiom(name const & n, expr const & t);
|
|
|
|
void add_var(name const & n, expr const & t);
|
2013-08-05 03:52:14 +00:00
|
|
|
|
2013-08-15 01:16:11 +00:00
|
|
|
/**
|
|
|
|
\brief Register the given unanymous object in this environment.
|
|
|
|
The environment assume the object ownership.
|
|
|
|
*/
|
2013-08-16 22:09:26 +00:00
|
|
|
void add_neutral_object(neutral_object_cell * o);
|
2013-08-15 01:16:11 +00:00
|
|
|
|
2013-08-05 03:52:14 +00:00
|
|
|
/**
|
|
|
|
\brief Return the object with the given name.
|
|
|
|
It throws an exception if the environment does not have an object with the given name.
|
|
|
|
*/
|
2013-12-08 22:37:38 +00:00
|
|
|
object get_object(name const & n) const;
|
2013-08-06 03:06:07 +00:00
|
|
|
|
2013-08-17 17:55:42 +00:00
|
|
|
/**
|
|
|
|
\brief Find a given object in the environment. Return the null
|
|
|
|
object if there is no object with the given name.
|
|
|
|
*/
|
2013-12-13 00:33:31 +00:00
|
|
|
optional<object> find_object(name const & n) const { return get_object_core(n); }
|
2013-08-17 17:55:42 +00:00
|
|
|
|
2013-08-15 01:16:11 +00:00
|
|
|
/** \brief Return true iff the environment has an object with the given name */
|
2013-12-03 07:02:39 +00:00
|
|
|
bool has_object(name const & n) const { return static_cast<bool>(find_object(n)); }
|
2013-08-15 01:16:11 +00:00
|
|
|
|
2013-12-22 19:51:38 +00:00
|
|
|
/**
|
|
|
|
\brief Type check the given expression, and return the type of \c e in the given context and this environment.
|
|
|
|
*/
|
2014-02-19 06:55:00 +00:00
|
|
|
expr type_check(expr const & e) const;
|
2013-12-22 19:51:38 +00:00
|
|
|
|
2013-09-01 23:59:15 +00:00
|
|
|
/**
|
|
|
|
\brief Return the type of \c e in the given context and this environment.
|
|
|
|
*/
|
2014-02-19 06:55:00 +00:00
|
|
|
expr infer_type(expr const & e) const;
|
2013-09-01 23:59:15 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Normalize \c e in the given context and this environment.
|
|
|
|
*/
|
2014-02-19 06:55:00 +00:00
|
|
|
expr normalize(expr const & e) const;
|
2013-09-01 23:59:15 +00:00
|
|
|
|
2014-01-15 19:02:47 +00:00
|
|
|
/**
|
|
|
|
\brief Return true iff \c e is a proposition.
|
|
|
|
*/
|
2014-02-19 06:55:00 +00:00
|
|
|
bool is_proposition(expr const & e) const;
|
2014-01-15 19:02:47 +00:00
|
|
|
|
2013-11-13 22:26:01 +00:00
|
|
|
/**
|
|
|
|
\brief Low-level function for accessing objects. Consider using iterators.
|
|
|
|
*/
|
|
|
|
unsigned get_num_objects(bool local) const;
|
|
|
|
/**
|
|
|
|
\brief Low-level function for accessing objects. Consider using iterators.
|
|
|
|
*/
|
|
|
|
object const & get_object(unsigned i, bool local) const;
|
|
|
|
|
2013-08-08 23:12:46 +00:00
|
|
|
/** \brief Iterator for Lean environment objects. */
|
|
|
|
class object_iterator {
|
2013-12-13 00:33:31 +00:00
|
|
|
std::shared_ptr<environment_cell const> m_env;
|
|
|
|
unsigned m_idx;
|
|
|
|
bool m_local;
|
|
|
|
friend class environment_cell;
|
|
|
|
object_iterator(std::shared_ptr<environment_cell const> && env, unsigned idx, bool local):m_env(env), m_idx(idx), m_local(local) {}
|
2013-08-08 23:12:46 +00:00
|
|
|
public:
|
2013-08-15 16:29:42 +00:00
|
|
|
object_iterator(object_iterator const & s):m_env(s.m_env), m_idx(s.m_idx), m_local(s.m_local) {}
|
2013-08-08 23:12:46 +00:00
|
|
|
object_iterator & operator++() { ++m_idx; return *this; }
|
|
|
|
object_iterator operator++(int) { object_iterator tmp(*this); operator++(); return tmp; }
|
2013-08-21 19:42:55 +00:00
|
|
|
object_iterator & operator--() { --m_idx; return *this; }
|
|
|
|
object_iterator operator--(int) { object_iterator tmp(*this); operator--(); return tmp; }
|
2013-12-13 00:33:31 +00:00
|
|
|
bool operator==(object_iterator const & s) const { lean_assert(m_env.get() == s.m_env.get()); return m_idx == s.m_idx; }
|
2013-08-08 23:12:46 +00:00
|
|
|
bool operator!=(object_iterator const & s) const { return !operator==(s); }
|
2013-12-13 00:33:31 +00:00
|
|
|
object const & operator*() { return m_env->get_object(m_idx, m_local); }
|
|
|
|
object const * operator->() { return &(m_env->get_object(m_idx, m_local)); }
|
2013-08-08 23:12:46 +00:00
|
|
|
};
|
|
|
|
|
2013-08-15 16:29:42 +00:00
|
|
|
/**
|
|
|
|
\brief Return an iterator to the beginning of the sequence of
|
|
|
|
objects stored in this environment.
|
|
|
|
|
|
|
|
\remark The objects in this environment and ancestor
|
|
|
|
environments are considered
|
|
|
|
*/
|
2013-12-13 00:33:31 +00:00
|
|
|
object_iterator begin_objects() const { return object_iterator(m_this.lock(), 0, false); }
|
2013-08-15 16:29:42 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Return an iterator to the end of the sequence of
|
|
|
|
objects stored in this environment.
|
|
|
|
|
|
|
|
\remark The objects in this environment and ancestor
|
|
|
|
environments are considered
|
|
|
|
*/
|
2013-12-13 00:33:31 +00:00
|
|
|
object_iterator end_objects() const { return object_iterator(m_this.lock(), get_num_objects(false), false); }
|
2013-08-15 16:29:42 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Return an iterator to the beginning of the sequence of
|
|
|
|
objects stored in this environment (objects in ancestor
|
|
|
|
environments are ingored).
|
|
|
|
*/
|
2013-12-13 00:33:31 +00:00
|
|
|
object_iterator begin_local_objects() const { return object_iterator(m_this.lock(), 0, true); }
|
2013-08-08 23:12:46 +00:00
|
|
|
|
2013-08-15 16:29:42 +00:00
|
|
|
/**
|
|
|
|
\brief Return an iterator to the end of the sequence of
|
|
|
|
objects stored in this environment (objects in ancestor
|
|
|
|
environments are ingored).
|
|
|
|
*/
|
2013-12-13 00:33:31 +00:00
|
|
|
object_iterator end_local_objects() const { return object_iterator(m_this.lock(), get_num_objects(true), true); }
|
2013-08-09 22:33:34 +00:00
|
|
|
// =======================================
|
2013-08-08 23:12:46 +00:00
|
|
|
|
2013-11-07 03:21:22 +00:00
|
|
|
/**
|
|
|
|
\brief Register an environment extension. Every environment
|
|
|
|
object will contain this extension. The funciton mk creates a
|
|
|
|
new instance of the extension. The extension object can be
|
|
|
|
retrieved using the token (unsigned integer) returned by this
|
|
|
|
method.
|
|
|
|
|
|
|
|
\remark The extension objects are created on demand.
|
|
|
|
|
|
|
|
\see get_extension
|
|
|
|
*/
|
2013-12-13 00:33:31 +00:00
|
|
|
typedef std::unique_ptr<environment_extension> (*mk_extension)();
|
2013-11-07 03:21:22 +00:00
|
|
|
static unsigned register_extension(mk_extension mk);
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Retrieve the extension associated with the token \c extid.
|
|
|
|
The token is the value returned by \c register_extension.
|
|
|
|
*/
|
|
|
|
template<typename Ext>
|
2013-11-07 19:29:01 +00:00
|
|
|
Ext const & get_extension(unsigned extid) const {
|
2013-12-13 00:33:31 +00:00
|
|
|
environment_extension const & ext = get_extension_core(extid);
|
2013-11-07 19:29:01 +00:00
|
|
|
lean_assert(dynamic_cast<Ext const *>(&ext) != nullptr);
|
|
|
|
return static_cast<Ext const &>(ext);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename Ext>
|
|
|
|
Ext & get_extension(unsigned extid) {
|
2013-12-13 00:33:31 +00:00
|
|
|
environment_extension & ext = get_extension_core(extid);
|
2013-11-07 03:21:22 +00:00
|
|
|
lean_assert(dynamic_cast<Ext*>(&ext) != nullptr);
|
|
|
|
return static_cast<Ext&>(ext);
|
|
|
|
}
|
2013-11-07 19:29:01 +00:00
|
|
|
|
2013-12-29 01:31:35 +00:00
|
|
|
void export_objects(std::string const & fname);
|
|
|
|
bool import(std::string const & fname, io_state const & ios);
|
2013-12-29 03:20:04 +00:00
|
|
|
void load(std::string const & fname, io_state const & ios);
|
2014-01-15 00:41:40 +00:00
|
|
|
/** \brief Return true iff module \c n has already been imported */
|
|
|
|
bool imported(std::string const & n) const;
|
2013-12-29 03:20:04 +00:00
|
|
|
|
2013-12-29 11:43:45 +00:00
|
|
|
/**
|
|
|
|
\brief When trusted_imported flag is true, the environment will
|
|
|
|
not type check imported modules.
|
|
|
|
*/
|
|
|
|
void set_trusted_imported(bool flag);
|
|
|
|
|
2013-11-18 02:11:44 +00:00
|
|
|
/**
|
2013-12-29 01:31:35 +00:00
|
|
|
\brief Execute function \c fn. Any object created by \c fn
|
|
|
|
is not exported by the environment.
|
2013-11-18 02:11:44 +00:00
|
|
|
*/
|
2013-12-29 01:31:35 +00:00
|
|
|
void auxiliary_section(std::function<void()> fn);
|
2013-07-29 05:34:39 +00:00
|
|
|
};
|
2013-12-13 00:33:31 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Frontend can store data in environment extensions.
|
|
|
|
Each extension is associated with a unique token/id.
|
|
|
|
This token allows the frontend to retrieve/store an extension object
|
|
|
|
in the environment
|
|
|
|
*/
|
|
|
|
class environment_extension {
|
2013-12-13 21:54:09 +00:00
|
|
|
friend class environment_cell;
|
2013-12-13 00:33:31 +00:00
|
|
|
environment_cell * m_env;
|
|
|
|
unsigned m_extid; // extension id
|
|
|
|
environment_extension const * get_parent_core() const;
|
|
|
|
public:
|
|
|
|
environment_extension();
|
|
|
|
virtual ~environment_extension();
|
|
|
|
/**
|
|
|
|
\brief Return a constant reference for a parent extension,
|
|
|
|
and a nullptr if there is no parent/ancestor, or if the
|
|
|
|
parent/ancestor has an extension.
|
|
|
|
*/
|
|
|
|
template<typename Ext> Ext const * get_parent() const {
|
|
|
|
environment_extension const * ext = get_parent_core();
|
|
|
|
lean_assert(!ext || dynamic_cast<Ext const *>(ext) != nullptr);
|
|
|
|
return static_cast<Ext const *>(ext);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Reference to environment
|
|
|
|
*/
|
|
|
|
class environment {
|
|
|
|
friend class ro_environment;
|
|
|
|
friend class environment_cell;
|
|
|
|
friend class read_write_shared_environment;
|
|
|
|
std::shared_ptr<environment_cell> m_ptr;
|
|
|
|
environment(std::shared_ptr<environment_cell> const & parent, bool);
|
|
|
|
environment(std::shared_ptr<environment_cell> const & ptr);
|
|
|
|
public:
|
|
|
|
environment();
|
|
|
|
environment_cell * operator->() const { return m_ptr.get(); }
|
|
|
|
environment_cell & operator*() const { return *(m_ptr.get()); }
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
\brief Read-only reference to environment.
|
|
|
|
*/
|
|
|
|
class ro_environment {
|
|
|
|
friend class environment_cell;
|
|
|
|
friend class read_only_shared_environment;
|
|
|
|
std::shared_ptr<environment_cell const> m_ptr;
|
|
|
|
ro_environment(std::shared_ptr<environment_cell const> const & p):m_ptr(p) {}
|
|
|
|
friend int push_environment(lua_State * L, ro_environment const & env);
|
|
|
|
environment cast_to_environment() const { return environment(std::const_pointer_cast<environment_cell>(m_ptr)); }
|
|
|
|
public:
|
|
|
|
typedef std::weak_ptr<environment_cell const> weak_ref;
|
|
|
|
ro_environment(environment const & env);
|
|
|
|
ro_environment(weak_ref const & env);
|
|
|
|
explicit operator weak_ref() const { return weak_ref(m_ptr); }
|
|
|
|
weak_ref to_weak_ref() const { return weak_ref(m_ptr); }
|
|
|
|
environment_cell const * operator->() const { return m_ptr.get(); }
|
|
|
|
environment_cell const & operator*() const { return *(m_ptr.get()); }
|
|
|
|
};
|
2013-12-29 01:31:35 +00:00
|
|
|
|
|
|
|
/** \brief Return true iff the given object marks the begin of the of a sequence of imported objects. */
|
|
|
|
bool is_begin_import(object const & obj);
|
2013-12-29 03:20:04 +00:00
|
|
|
/** \brief Return true iff the given object marks the begin of the of a sequence of builtin imported objects. */
|
|
|
|
bool is_begin_builtin_import(object const & obj);
|
2013-12-29 01:31:35 +00:00
|
|
|
/** \brief Return true iff the given object marks the end of the of a sequence of imported objects. */
|
|
|
|
bool is_end_import(object const & obj);
|
2013-12-29 03:20:04 +00:00
|
|
|
/**
|
|
|
|
\brief Return the module imported by the given import object.
|
|
|
|
Return none if \c obj is not an import object.
|
|
|
|
*/
|
|
|
|
optional<std::string> get_imported_module(object const & obj);
|
2013-12-30 05:59:57 +00:00
|
|
|
|
2014-01-01 21:16:44 +00:00
|
|
|
/**
|
2014-02-19 06:55:00 +00:00
|
|
|
\brief Return true iff \c obj is a set_opaque command mark.
|
2014-01-01 21:16:44 +00:00
|
|
|
*/
|
|
|
|
bool is_set_opaque(object const & obj);
|
|
|
|
/**
|
2014-02-19 06:55:00 +00:00
|
|
|
\brief Return the identifier of a set_opaque command.
|
2014-01-01 21:16:44 +00:00
|
|
|
\pre is_set_opaque(obj)
|
|
|
|
*/
|
|
|
|
name const & get_set_opaque_id(object const & obj);
|
|
|
|
/**
|
2014-02-19 06:55:00 +00:00
|
|
|
\brief Return the flag of a set_opaque command.
|
2014-01-01 21:16:44 +00:00
|
|
|
\pre is_set_opaque(obj)
|
|
|
|
*/
|
|
|
|
bool get_set_opaque_flag(object const & obj);
|
2013-07-29 05:34:39 +00:00
|
|
|
}
|