using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using glue.Tasks; namespace agree { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Type in the type hierarchy. This hierarchy is a fully-connected directed-acyclic graph (DAG) where each type /// supports zero or more features that may be arbitrarily constrained to be any type in the hierarchy. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public partial class Type : GlbDag { public Type(TypeMgr tm, String name, List<BaseFeatConstraint> bfc) : base(tm, name, bfc) { } public static Type MakeTopType(TypeMgr tm, String name) { Type t = new Type(tm, name, null); t.m_id = TypeMgr.TopId; t.m_flags |= Flags.Expanded | Flags.LoadedDefinition; t._definition = default(TfsEdge); t._expand_task = Tasks.FromResult(default(TfsEdge)); return t; } /// <summary> /// returns 'true' for detached top /// </summary> public bool IsTop { get { return this == mgr.TopType; } } /// <summary> /// </summary> public bool HasLocalFeatures { get { return (m_flags & Flags.HasLocalFeatures) > 0; } } /// <summary> /// if a type has authored constraints then, when it is the result of a unification meet /// and is not the same as either type, it must go through the extra step of /// being made well-formed. It is not sufficient to check HasLocalFeatures because /// a type can further constraint features that it did not introduce /// </summary> public bool HasConstraints { get { return (m_flags & Flags.HasConstraints) > 0; } } /// <summary> /// 'Atomic' types have no appropriate features in their inclusive subgraph (Copestake 2001 p.68) /// We term featureless types "bare." For example, *top* is not atomic, despite being bare. /// </summary> public bool IsAtomic { get { return (m_flags & Flags.Atomic) > 0; } } public override Type InstanceType { get { return this; } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void AddLocalFeature(int i_feat) { if ((m_flags & Flags.LoadedNonLocalFeatures) > 0) throw new Exception("Cannot add local features because parent features have already been calculated."); appropriate_feat = appropriate_feat ?? new HashSet<int>(); appropriate_feat.Add(i_feat); m_flags |= Flags.HasLocalFeatures; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// [DebuggerBrowsable(DebuggerBrowsableState.Never)] HashSet<int> appropriate_feat = null; public FeatureConfig fc = FeatureConfig.Empty; public int[] _deprec_feat_indexes; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Internal use only. Implements lazy loading of parent features. /// Assumes local features have already been added to m_appropriate_features for the parent types, so this should /// be called for all types according to topological order. /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// internal void AddNonLocalFeatures() { if ((m_flags & Flags.HasAnyFeatures) > 0) { foreach (Type par in Parents) { if ((par.m_flags & Flags.LoadedNonLocalFeatures) == 0) throw new Exception("Incorrect topological order while calculating inherited features."); if (par.fc.Count > 0) { appropriate_feat = appropriate_feat ?? new HashSet<int>(); appropriate_feat.UnionWith(par.fc); } } /// Set the order for which features of this type are enumerated. /// Observation: unification failures tend to happen in more common types, as opposed to more obscure ones. /// Reverse this ordering and you'll see. /// We use the following order to unify the features of a type, i.e. according to the topological order of /// their introducing type. fc = mgr.fcm.Get(appropriate_feat); } _deprec_feat_indexes = fc.rg_fix; #if DEBUG foreach (int i in _deprec_feat_indexes) mgr.feat_arr[i].AppropriateFor.Add(this); #endif m_flags |= Flags.LoadedNonLocalFeatures; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <returns> /// 'false' if the specified feature does not exist or is not appropriate for this type. /// </returns> //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public bool HasFeature(int i_feat) { for (int i = 0; i < _deprec_feat_indexes.Length; i++) if (_deprec_feat_indexes[i] == i_feat) return true; return false; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected /*volatile*/ Task<TfsEdge> _expand_task = null; [DebuggerBrowsable(DebuggerBrowsableState.Never)] // side effects public override TfsEdge Expanded { get { if (!HasAnyFeatures) return _definition; if (_expand_task == null) { // If we're in a task already, do the job synchronously. In either case, only the race winner proceeds with the job. if (glue.Debugging.Nop.single_threaded || Task.CurrentId.HasValue) { TaskCompletionSource<TfsEdge> tcs = new TaskCompletionSource<TfsEdge>(); if (Interlocked.CompareExchange(ref _expand_task, tcs.Task, null) == null) tcs.SetResult(_do_expand()); } else { Task<TfsEdge> t = new Task<TfsEdge>(_do_expand); if (Interlocked.CompareExchange(ref _expand_task, t, null) == null) t.Start(TaskScheduler.Default); } // at this point, the value of _expand_task has been written by the race winner } else { //Task t_cur; //if (Task.CurrentId.HasValue && (t_cur = Tasks.Self) != null && !t_cur.IsCompleted && t_cur.HasAsAncestor(_expand_task)) //throw new TfsException("Error expanding type '{0}': constraint refers to itself.", this.Name); } return _expand_task.Result; } } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TfsEdge _do_expand() { Tray tr = mgr.g.loadtray; Stopwatch stopw = Stopwatch.StartNew(); TfsEdge result; if (!new Unification.Expander(tr).ExpandType(this, out result)) throw new TfsException("Error expanding type '{0}': could not unify with its constraint or parents.", this.Name); result.RegisterName(Name); m_flags |= Flags.Expanded; Interlocked.Add(ref mgr.ms_expand, (int)stopw.ElapsedMilliseconds); return result; } internal void _set_expanded(TfsEdge e) { _expand_task = Tasks.FromResult(e); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public override string ToString() { return name; } }; }