diff --git a/src/blanket_traits.rs b/src/blanket_traits.rs
index bc6dab913..6e7496d0b 100644
--- a/src/blanket_traits.rs
+++ b/src/blanket_traits.rs
@@ -12,8 +12,8 @@
 //! automatically implemented if you satisfy all the bounds.
 //!
 
-use core::fmt;
 use core::str::FromStr;
+use core::{fmt, hash};
 
 use crate::MiniscriptKey;
 
@@ -28,19 +28,43 @@ pub trait FromStrKey:
     > + FromStr<Err = Self::_FromStrErr>
 {
     /// Dummy type. Do not use.
-    type _Sha256: FromStr<Err = Self::_Sha256FromStrErr>;
+    type _Sha256: FromStr<Err = Self::_Sha256FromStrErr>
+        + Clone
+        + Eq
+        + Ord
+        + fmt::Display
+        + fmt::Debug
+        + hash::Hash;
     /// Dummy type. Do not use.
     type _Sha256FromStrErr: fmt::Debug + fmt::Display;
     /// Dummy type. Do not use.
-    type _Hash256: FromStr<Err = Self::_Hash256FromStrErr>;
+    type _Hash256: FromStr<Err = Self::_Hash256FromStrErr>
+        + Clone
+        + Eq
+        + Ord
+        + fmt::Display
+        + fmt::Debug
+        + hash::Hash;
     /// Dummy type. Do not use.
     type _Hash256FromStrErr: fmt::Debug + fmt::Display;
     /// Dummy type. Do not use.
-    type _Ripemd160: FromStr<Err = Self::_Ripemd160FromStrErr>;
+    type _Ripemd160: FromStr<Err = Self::_Ripemd160FromStrErr>
+        + Clone
+        + Eq
+        + Ord
+        + fmt::Display
+        + fmt::Debug
+        + hash::Hash;
     /// Dummy type. Do not use.
     type _Ripemd160FromStrErr: fmt::Debug + fmt::Display;
     /// Dummy type. Do not use.
-    type _Hash160: FromStr<Err = Self::_Hash160FromStrErr>;
+    type _Hash160: FromStr<Err = Self::_Hash160FromStrErr>
+        + Clone
+        + Eq
+        + Ord
+        + fmt::Display
+        + fmt::Debug
+        + hash::Hash;
     /// Dummy type. Do not use.
     type _Hash160FromStrErr: fmt::Debug + fmt::Display;
     /// Dummy type. Do not use.
diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs
index 79adb6b33..431b8b20a 100644
--- a/src/descriptor/bare.rs
+++ b/src/descriptor/bare.rs
@@ -22,8 +22,8 @@ use crate::policy::{semantic, Liftable};
 use crate::prelude::*;
 use crate::util::{varint_len, witness_to_scriptsig};
 use crate::{
-    BareCtx, Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey, TranslateErr,
-    TranslatePk, Translator,
+    BareCtx, Error, ForEachKey, FromStrKey, Miniscript, MiniscriptKey, Satisfier, ToPublicKey,
+    TranslateErr, TranslatePk, Translator,
 };
 
 /// Create a Bare Descriptor. That is descriptor that is
@@ -166,7 +166,7 @@ impl<Pk: MiniscriptKey> Liftable<Pk> for Bare<Pk> {
     fn lift(&self) -> Result<semantic::Policy<Pk>, Error> { self.ms.lift() }
 }
 
-impl<Pk: crate::FromStrKey> FromTree for Bare<Pk> {
+impl<Pk: FromStrKey> FromTree for Bare<Pk> {
     fn from_tree(top: &expression::Tree) -> Result<Self, Error> {
         let sub = Miniscript::<Pk, BareCtx>::from_tree(top)?;
         BareCtx::top_level_checks(&sub)?;
@@ -174,7 +174,7 @@ impl<Pk: crate::FromStrKey> FromTree for Bare<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> core::str::FromStr for Bare<Pk> {
+impl<Pk: FromStrKey> core::str::FromStr for Bare<Pk> {
     type Err = Error;
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         let desc_str = verify_checksum(s)?;
@@ -364,7 +364,7 @@ impl<Pk: MiniscriptKey> Liftable<Pk> for Pkh<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> FromTree for Pkh<Pk> {
+impl<Pk: FromStrKey> FromTree for Pkh<Pk> {
     fn from_tree(top: &expression::Tree) -> Result<Self, Error> {
         if top.name == "pkh" && top.args.len() == 1 {
             Ok(Pkh::new(expression::terminal(&top.args[0], |pk| Pk::from_str(pk))?)?)
@@ -378,7 +378,7 @@ impl<Pk: crate::FromStrKey> FromTree for Pkh<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> core::str::FromStr for Pkh<Pk> {
+impl<Pk: FromStrKey> core::str::FromStr for Pkh<Pk> {
     type Err = Error;
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         let desc_str = verify_checksum(s)?;
diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs
index 394e17f63..1059a3b3c 100644
--- a/src/descriptor/mod.rs
+++ b/src/descriptor/mod.rs
@@ -25,8 +25,8 @@ use crate::miniscript::{satisfy, Legacy, Miniscript, Segwitv0};
 use crate::plan::{AssetProvider, Plan};
 use crate::prelude::*;
 use crate::{
-    expression, hash256, BareCtx, Error, ForEachKey, MiniscriptKey, Satisfier, ToPublicKey,
-    TranslateErr, TranslatePk, Translator,
+    expression, hash256, BareCtx, Error, ForEachKey, FromStrKey, MiniscriptKey, Satisfier,
+    ToPublicKey, TranslateErr, TranslatePk, Translator,
 };
 
 mod bare;
@@ -918,7 +918,7 @@ impl Descriptor<DefiniteDescriptorKey> {
     }
 }
 
-impl<Pk: crate::FromStrKey> crate::expression::FromTree for Descriptor<Pk> {
+impl<Pk: FromStrKey> crate::expression::FromTree for Descriptor<Pk> {
     /// Parse an expression tree into a descriptor.
     fn from_tree(top: &expression::Tree) -> Result<Descriptor<Pk>, Error> {
         Ok(match (top.name, top.args.len() as u32) {
@@ -932,7 +932,7 @@ impl<Pk: crate::FromStrKey> crate::expression::FromTree for Descriptor<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> FromStr for Descriptor<Pk> {
+impl<Pk: FromStrKey> FromStr for Descriptor<Pk> {
     type Err = Error;
     fn from_str(s: &str) -> Result<Descriptor<Pk>, Error> {
         // tr tree parsing has special code
diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs
index fa57eb582..541f0f0db 100644
--- a/src/descriptor/segwitv0.rs
+++ b/src/descriptor/segwitv0.rs
@@ -20,8 +20,8 @@ use crate::policy::{semantic, Liftable};
 use crate::prelude::*;
 use crate::util::varint_len;
 use crate::{
-    Error, ForEachKey, Miniscript, MiniscriptKey, Satisfier, Segwitv0, ToPublicKey, TranslateErr,
-    TranslatePk, Translator,
+    Error, ForEachKey, FromStrKey, Miniscript, MiniscriptKey, Satisfier, Segwitv0, ToPublicKey,
+    TranslateErr, TranslatePk, Translator,
 };
 /// A Segwitv0 wsh descriptor
 #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
@@ -231,7 +231,7 @@ impl<Pk: MiniscriptKey> Liftable<Pk> for Wsh<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> crate::expression::FromTree for Wsh<Pk> {
+impl<Pk: FromStrKey> crate::expression::FromTree for Wsh<Pk> {
     fn from_tree(top: &expression::Tree) -> Result<Self, Error> {
         if top.name == "wsh" && top.args.len() == 1 {
             let top = &top.args[0];
@@ -269,7 +269,7 @@ impl<Pk: MiniscriptKey> fmt::Display for Wsh<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> core::str::FromStr for Wsh<Pk> {
+impl<Pk: FromStrKey> core::str::FromStr for Wsh<Pk> {
     type Err = Error;
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         let desc_str = verify_checksum(s)?;
@@ -473,7 +473,7 @@ impl<Pk: MiniscriptKey> Liftable<Pk> for Wpkh<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> crate::expression::FromTree for Wpkh<Pk> {
+impl<Pk: FromStrKey> crate::expression::FromTree for Wpkh<Pk> {
     fn from_tree(top: &expression::Tree) -> Result<Self, Error> {
         if top.name == "wpkh" && top.args.len() == 1 {
             Ok(Wpkh::new(expression::terminal(&top.args[0], |pk| Pk::from_str(pk))?)?)
@@ -487,7 +487,7 @@ impl<Pk: crate::FromStrKey> crate::expression::FromTree for Wpkh<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> core::str::FromStr for Wpkh<Pk> {
+impl<Pk: FromStrKey> core::str::FromStr for Wpkh<Pk> {
     type Err = Error;
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         let desc_str = verify_checksum(s)?;
diff --git a/src/descriptor/sh.rs b/src/descriptor/sh.rs
index 3f7e9cb08..da782abff 100644
--- a/src/descriptor/sh.rs
+++ b/src/descriptor/sh.rs
@@ -24,8 +24,8 @@ use crate::policy::{semantic, Liftable};
 use crate::prelude::*;
 use crate::util::{varint_len, witness_to_scriptsig};
 use crate::{
-    push_opcode_size, Error, ForEachKey, Legacy, Miniscript, MiniscriptKey, Satisfier, Segwitv0,
-    ToPublicKey, TranslateErr, TranslatePk, Translator,
+    push_opcode_size, Error, ForEachKey, FromStrKey, Legacy, Miniscript, MiniscriptKey, Satisfier,
+    Segwitv0, ToPublicKey, TranslateErr, TranslatePk, Translator,
 };
 
 /// A Legacy p2sh Descriptor
@@ -81,7 +81,7 @@ impl<Pk: MiniscriptKey> fmt::Display for Sh<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> crate::expression::FromTree for Sh<Pk> {
+impl<Pk: FromStrKey> crate::expression::FromTree for Sh<Pk> {
     fn from_tree(top: &expression::Tree) -> Result<Self, Error> {
         if top.name == "sh" && top.args.len() == 1 {
             let top = &top.args[0];
@@ -106,7 +106,7 @@ impl<Pk: crate::FromStrKey> crate::expression::FromTree for Sh<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> core::str::FromStr for Sh<Pk> {
+impl<Pk: FromStrKey> core::str::FromStr for Sh<Pk> {
     type Err = Error;
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         let desc_str = verify_checksum(s)?;
diff --git a/src/descriptor/tr.rs b/src/descriptor/tr.rs
index 70e133b6c..931ffaa1d 100644
--- a/src/descriptor/tr.rs
+++ b/src/descriptor/tr.rs
@@ -23,8 +23,8 @@ use crate::policy::Liftable;
 use crate::prelude::*;
 use crate::util::{varint_len, witness_size};
 use crate::{
-    errstr, Error, ForEachKey, MiniscriptKey, Satisfier, ScriptContext, Tap, ToPublicKey,
-    TranslateErr, TranslatePk, Translator,
+    errstr, Error, ForEachKey, FromStrKey, MiniscriptKey, Satisfier, ScriptContext, Tap,
+    ToPublicKey, TranslateErr, TranslatePk, Translator,
 };
 
 /// A Taproot Tree representation.
@@ -467,7 +467,7 @@ where
 }
 
 #[rustfmt::skip]
-impl<Pk: crate::FromStrKey> Tr<Pk> {
+impl<Pk: FromStrKey> Tr<Pk> {
     // Helper function to parse taproot script path
     fn parse_tr_script_spend(tree: &expression::Tree,) -> Result<TapTree<Pk>, Error> {
         match tree {
@@ -488,7 +488,7 @@ impl<Pk: crate::FromStrKey> Tr<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> crate::expression::FromTree for Tr<Pk> {
+impl<Pk: FromStrKey> crate::expression::FromTree for Tr<Pk> {
     fn from_tree(top: &expression::Tree) -> Result<Self, Error> {
         if top.name == "tr" {
             match top.args.len() {
@@ -530,7 +530,7 @@ impl<Pk: crate::FromStrKey> crate::expression::FromTree for Tr<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> core::str::FromStr for Tr<Pk> {
+impl<Pk: FromStrKey> core::str::FromStr for Tr<Pk> {
     type Err = Error;
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         let desc_str = verify_checksum(s)?;
diff --git a/src/miniscript/astelem.rs b/src/miniscript/astelem.rs
index e30765548..28d5d8cbd 100644
--- a/src/miniscript/astelem.rs
+++ b/src/miniscript/astelem.rs
@@ -19,7 +19,8 @@ use crate::miniscript::{types, ScriptContext};
 use crate::prelude::*;
 use crate::util::MsKeyBuilder;
 use crate::{
-    errstr, expression, AbsLockTime, Error, Miniscript, MiniscriptKey, Terminal, ToPublicKey,
+    errstr, expression, AbsLockTime, Error, FromStrKey, Miniscript, MiniscriptKey, Terminal,
+    ToPublicKey,
 };
 
 impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
@@ -240,15 +241,13 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for Terminal<Pk, Ctx> {
     }
 }
 
-impl<Pk: crate::FromStrKey, Ctx: ScriptContext> crate::expression::FromTree
-    for Arc<Terminal<Pk, Ctx>>
-{
+impl<Pk: FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for Arc<Terminal<Pk, Ctx>> {
     fn from_tree(top: &expression::Tree) -> Result<Arc<Terminal<Pk, Ctx>>, Error> {
         Ok(Arc::new(expression::FromTree::from_tree(top)?))
     }
 }
 
-impl<Pk: crate::FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for Terminal<Pk, Ctx> {
+impl<Pk: FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for Terminal<Pk, Ctx> {
     fn from_tree(top: &expression::Tree) -> Result<Terminal<Pk, Ctx>, Error> {
         let (frag_name, frag_wrap) = super::split_expression_name(top.name)?;
         let unwrapped = match (frag_name, top.args.len()) {
diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs
index 053fc6da1..88a9e9a3f 100644
--- a/src/miniscript/mod.rs
+++ b/src/miniscript/mod.rs
@@ -45,7 +45,8 @@ use crate::miniscript::decode::Terminal;
 use crate::miniscript::types::extra_props::ExtData;
 use crate::miniscript::types::Type;
 use crate::{
-    expression, plan, Error, ForEachKey, MiniscriptKey, ToPublicKey, TranslatePk, Translator,
+    expression, plan, Error, ForEachKey, FromStrKey, MiniscriptKey, ToPublicKey, TranslatePk,
+    Translator,
 };
 #[cfg(test)]
 mod ms_tests;
@@ -617,7 +618,7 @@ where
     Ok(ms)
 }
 
-impl<Pk: crate::FromStrKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
+impl<Pk: FromStrKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
     /// Attempt to parse an insane(scripts don't clear sanity checks)
     /// from string into a Miniscript representation.
     /// Use this to parse scripts with repeated pubkeys, timelock mixing, malleable
@@ -648,17 +649,13 @@ impl<Pk: crate::FromStrKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
     }
 }
 
-impl<Pk: crate::FromStrKey, Ctx: ScriptContext> crate::expression::FromTree
-    for Arc<Miniscript<Pk, Ctx>>
-{
+impl<Pk: FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for Arc<Miniscript<Pk, Ctx>> {
     fn from_tree(top: &expression::Tree) -> Result<Arc<Miniscript<Pk, Ctx>>, Error> {
         Ok(Arc::new(expression::FromTree::from_tree(top)?))
     }
 }
 
-impl<Pk: crate::FromStrKey, Ctx: ScriptContext> crate::expression::FromTree
-    for Miniscript<Pk, Ctx>
-{
+impl<Pk: FromStrKey, Ctx: ScriptContext> crate::expression::FromTree for Miniscript<Pk, Ctx> {
     /// Parse an expression tree into a Miniscript. As a general rule, this
     /// should not be called directly; rather go through the descriptor API.
     fn from_tree(top: &expression::Tree) -> Result<Miniscript<Pk, Ctx>, Error> {
@@ -667,7 +664,7 @@ impl<Pk: crate::FromStrKey, Ctx: ScriptContext> crate::expression::FromTree
     }
 }
 
-impl<Pk: crate::FromStrKey, Ctx: ScriptContext> str::FromStr for Miniscript<Pk, Ctx> {
+impl<Pk: FromStrKey, Ctx: ScriptContext> str::FromStr for Miniscript<Pk, Ctx> {
     type Err = Error;
     /// Parse a Miniscript from string and perform sanity checks
     /// See [Miniscript::from_str_insane] to parse scripts from string that
@@ -705,7 +702,7 @@ mod tests {
     use sync::Arc;
 
     use super::{Miniscript, ScriptContext, Segwitv0, Tap};
-    use crate::miniscript::types::{self, ExtData, Property, Type};
+    use crate::miniscript::types::{self, ExtData, Type};
     use crate::miniscript::Terminal;
     use crate::policy::Liftable;
     use crate::prelude::*;
@@ -893,8 +890,8 @@ mod tests {
 
         let pk_node = Terminal::Check(Arc::new(Miniscript {
             node: Terminal::PkK(String::from("")),
-            ty: Type::from_pk_k::<Segwitv0>(),
-            ext: types::extra_props::ExtData::from_pk_k::<Segwitv0>(),
+            ty: Type::pk_k(),
+            ext: types::extra_props::ExtData::pk_k::<Segwitv0>(),
             phantom: PhantomData,
         }));
         let pkk_ms: Miniscript<String, Segwitv0> = Miniscript::from_ast(pk_node).unwrap();
@@ -902,8 +899,8 @@ mod tests {
 
         let pkh_node = Terminal::Check(Arc::new(Miniscript {
             node: Terminal::PkH(String::from("")),
-            ty: Type::from_pk_h::<Segwitv0>(),
-            ext: types::extra_props::ExtData::from_pk_h::<Segwitv0>(),
+            ty: Type::pk_h(),
+            ext: types::extra_props::ExtData::pk_h::<Segwitv0>(),
             phantom: PhantomData,
         }));
         let pkh_ms: Miniscript<String, Segwitv0> = Miniscript::from_ast(pkh_node).unwrap();
@@ -923,8 +920,8 @@ mod tests {
 
         let pkk_node = Terminal::Check(Arc::new(Miniscript {
             node: Terminal::PkK(pk),
-            ty: Type::from_pk_k::<Segwitv0>(),
-            ext: types::extra_props::ExtData::from_pk_k::<Segwitv0>(),
+            ty: Type::pk_k(),
+            ext: types::extra_props::ExtData::pk_k::<Segwitv0>(),
             phantom: PhantomData,
         }));
         let pkk_ms: Segwitv0Script = Miniscript::from_ast(pkk_node).unwrap();
@@ -938,12 +935,12 @@ mod tests {
         let pkh_ms: Segwitv0Script = Miniscript {
             node: Terminal::Check(Arc::new(Miniscript {
                 node: Terminal::RawPkH(hash),
-                ty: Type::from_pk_h::<Segwitv0>(),
-                ext: types::extra_props::ExtData::from_pk_h::<Segwitv0>(),
+                ty: Type::pk_h(),
+                ext: types::extra_props::ExtData::pk_h::<Segwitv0>(),
                 phantom: PhantomData,
             })),
-            ty: Type::cast_check(Type::from_pk_h::<Segwitv0>()).unwrap(),
-            ext: ExtData::cast_check(ExtData::from_pk_h::<Segwitv0>()).unwrap(),
+            ty: Type::cast_check(Type::pk_h()).unwrap(),
+            ext: ExtData::cast_check(ExtData::pk_h::<Segwitv0>()),
             phantom: PhantomData,
         };
 
diff --git a/src/miniscript/types/correctness.rs b/src/miniscript/types/correctness.rs
index 4b6365a66..ad7597f5f 100644
--- a/src/miniscript/types/correctness.rs
+++ b/src/miniscript/types/correctness.rs
@@ -2,8 +2,7 @@
 
 //! Correctness/Soundness type properties
 
-use super::{ErrorKind, Property};
-use crate::ScriptContext;
+use super::ErrorKind;
 
 /// Basic type representing where the fragment can go
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
@@ -47,12 +46,24 @@ pub enum Input {
 }
 
 impl Input {
+    // FIXME rustc should eventually support derived == on enums in constfns
+    const fn constfn_eq(self, other: Self) -> bool {
+        matches!(
+            (self, other),
+            (Input::Zero, Input::Zero)
+                | (Input::One, Input::One)
+                | (Input::Any, Input::Any)
+                | (Input::OneNonZero, Input::OneNonZero)
+                | (Input::AnyNonZero, Input::AnyNonZero)
+        )
+    }
+
     /// Check whether given `Input` is a subtype of `other`. That is,
     /// if some Input is `OneNonZero` then it must be `One`, hence `OneNonZero` is
     /// a subtype if `One`. Returns `true` for `a.is_subtype(a)`.
-    fn is_subtype(&self, other: Self) -> bool {
+    const fn is_subtype(&self, other: Self) -> bool {
         match (*self, other) {
-            (x, y) if x == y => true,
+            (x, y) if x.constfn_eq(y) => true,
             (Input::OneNonZero, Input::One)
             | (Input::OneNonZero, Input::AnyNonZero)
             | (_, Input::Any) => true,
@@ -95,16 +106,15 @@ impl Correctness {
     /// This checks whether the argument `other` has attributes which are present
     /// in the given `Type`. This returns `true` on same arguments
     /// `a.is_subtype(a)` is `true`.
-    pub fn is_subtype(&self, other: Self) -> bool {
-        self.base == other.base
+    pub const fn is_subtype(&self, other: Self) -> bool {
+        self.base as u8 == other.base as u8
             && self.input.is_subtype(other.input)
             && self.dissatisfiable >= other.dissatisfiable
             && self.unit >= other.unit
     }
-}
 
-impl Property for Correctness {
-    fn sanity_checks(&self) {
+    /// Confirm invariants of the correctness checker.
+    pub fn sanity_checks(&self) {
         match self.base {
             Base::B => {}
             Base::K => {
@@ -115,21 +125,19 @@ impl Property for Correctness {
                 debug_assert!(!self.dissatisfiable);
             }
             Base::W => {
-                debug_assert!(self.input != Input::OneNonZero);
-                debug_assert!(self.input != Input::AnyNonZero);
+                debug_assert!(!self.input.constfn_eq(Input::OneNonZero));
+                debug_assert!(!self.input.constfn_eq(Input::AnyNonZero));
             }
         }
     }
 
-    fn from_true() -> Self { Self::TRUE }
-
-    fn from_false() -> Self { Self::FALSE }
-
-    fn from_pk_k<Ctx: ScriptContext>() -> Self {
+    /// Constructor for the correctness properties of the `pk_k` fragment.
+    pub const fn pk_k() -> Self {
         Correctness { base: Base::K, input: Input::OneNonZero, dissatisfiable: true, unit: true }
     }
 
-    fn from_pk_h<Ctx: ScriptContext>() -> Self {
+    /// Constructor for the correctness properties of the `pk_h` fragment.
+    pub const fn pk_h() -> Self {
         Correctness {
             base: Base::K,
             input: Input::AnyNonZero,
@@ -138,23 +146,28 @@ impl Property for Correctness {
         }
     }
 
-    fn from_multi(_: usize, _: usize) -> Self {
+    /// Constructor for the correctness properties of the `multi` fragment.
+    pub const fn multi() -> Self {
         Correctness { base: Base::B, input: Input::AnyNonZero, dissatisfiable: true, unit: true }
     }
 
-    fn from_multi_a(_: usize, _: usize) -> Self {
+    /// Constructor for the correctness properties of the `multi_a` fragment.
+    pub const fn multi_a() -> Self {
         Correctness { base: Base::B, input: Input::Any, dissatisfiable: true, unit: true }
     }
 
-    fn from_hash() -> Self {
+    /// Constructor for the correctness properties of any of the hash fragments.
+    pub const fn hash() -> Self {
         Correctness { base: Base::B, input: Input::OneNonZero, dissatisfiable: true, unit: true }
     }
 
-    fn from_time(_: u32) -> Self {
+    /// Constructor for the correctness properties of either of the time fragments.
+    pub const fn time() -> Self {
         Correctness { base: Base::B, input: Input::Zero, dissatisfiable: false, unit: false }
     }
 
-    fn cast_alt(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `a:` fragment.
+    pub const fn cast_alt(self) -> Result<Self, ErrorKind> {
         Ok(Correctness {
             base: match self.base {
                 Base::B => Base::W,
@@ -166,7 +179,8 @@ impl Property for Correctness {
         })
     }
 
-    fn cast_swap(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `s:` fragment.
+    pub const fn cast_swap(self) -> Result<Self, ErrorKind> {
         Ok(Correctness {
             base: match self.base {
                 Base::B => Base::W,
@@ -181,7 +195,8 @@ impl Property for Correctness {
         })
     }
 
-    fn cast_check(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `c:` fragment.
+    pub const fn cast_check(self) -> Result<Self, ErrorKind> {
         Ok(Correctness {
             base: match self.base {
                 Base::K => Base::B,
@@ -193,7 +208,8 @@ impl Property for Correctness {
         })
     }
 
-    fn cast_dupif(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `d:` fragment.
+    pub const fn cast_dupif(self) -> Result<Self, ErrorKind> {
         Ok(Correctness {
             base: match self.base {
                 Base::V => Base::B,
@@ -208,7 +224,8 @@ impl Property for Correctness {
         })
     }
 
-    fn cast_verify(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `v:` fragment.
+    pub const fn cast_verify(self) -> Result<Self, ErrorKind> {
         Ok(Correctness {
             base: match self.base {
                 Base::B => Base::V,
@@ -220,8 +237,9 @@ impl Property for Correctness {
         })
     }
 
-    fn cast_nonzero(self) -> Result<Self, ErrorKind> {
-        if self.input != Input::OneNonZero && self.input != Input::AnyNonZero {
+    /// Constructor for the correctness properties of the `j:` fragment.
+    pub const fn cast_nonzero(self) -> Result<Self, ErrorKind> {
+        if !self.input.constfn_eq(Input::OneNonZero) && !self.input.constfn_eq(Input::AnyNonZero) {
             return Err(ErrorKind::NonZeroZero);
         }
         Ok(Correctness {
@@ -235,7 +253,8 @@ impl Property for Correctness {
         })
     }
 
-    fn cast_zeronotequal(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `n:` fragment.
+    pub const fn cast_zeronotequal(self) -> Result<Self, ErrorKind> {
         Ok(Correctness {
             base: match self.base {
                 Base::B => Base::B,
@@ -247,7 +266,8 @@ impl Property for Correctness {
         })
     }
 
-    fn cast_true(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `t:` fragment.
+    pub const fn cast_true(self) -> Result<Self, ErrorKind> {
         Ok(Correctness {
             base: match self.base {
                 Base::V => Base::B,
@@ -259,7 +279,8 @@ impl Property for Correctness {
         })
     }
 
-    fn cast_or_i_false(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `l:` and `u:` fragments.
+    pub const fn cast_or_i_false(self) -> Result<Self, ErrorKind> {
         Ok(Correctness {
             base: match self.base {
                 Base::B => Base::B,
@@ -276,7 +297,8 @@ impl Property for Correctness {
         })
     }
 
-    fn and_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `and_b` fragment
+    pub const fn and_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
         Ok(Correctness {
             base: match (left.base, right.base) {
                 (Base::B, Base::W) => Base::B,
@@ -298,7 +320,8 @@ impl Property for Correctness {
         })
     }
 
-    fn and_v(left: Self, right: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `and_v` fragment
+    pub const fn and_v(left: Self, right: Self) -> Result<Self, ErrorKind> {
         Ok(Correctness {
             base: match (left.base, right.base) {
                 (Base::V, Base::B) => Base::B,
@@ -322,7 +345,8 @@ impl Property for Correctness {
         })
     }
 
-    fn or_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `or_b` fragment
+    pub const fn or_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
         if !left.dissatisfiable {
             return Err(ErrorKind::LeftNotDissatisfiable);
         }
@@ -347,7 +371,8 @@ impl Property for Correctness {
         })
     }
 
-    fn or_d(left: Self, right: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `or_d` fragment
+    pub const fn or_d(left: Self, right: Self) -> Result<Self, ErrorKind> {
         if !left.dissatisfiable {
             return Err(ErrorKind::LeftNotDissatisfiable);
         }
@@ -369,7 +394,8 @@ impl Property for Correctness {
         })
     }
 
-    fn or_c(left: Self, right: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `or_c` fragment
+    pub const fn or_c(left: Self, right: Self) -> Result<Self, ErrorKind> {
         if !left.dissatisfiable {
             return Err(ErrorKind::LeftNotDissatisfiable);
         }
@@ -391,7 +417,8 @@ impl Property for Correctness {
         })
     }
 
-    fn or_i(left: Self, right: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `or_i` fragment
+    pub const fn or_i(left: Self, right: Self) -> Result<Self, ErrorKind> {
         Ok(Correctness {
             base: match (left.base, right.base) {
                 (Base::B, Base::B) => Base::B,
@@ -408,7 +435,8 @@ impl Property for Correctness {
         })
     }
 
-    fn and_or(a: Self, b: Self, c: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the correctness properties of the `andor` fragment
+    pub const fn and_or(a: Self, b: Self, c: Self) -> Result<Self, ErrorKind> {
         if !a.dissatisfiable {
             return Err(ErrorKind::LeftNotDissatisfiable);
         }
@@ -437,13 +465,15 @@ impl Property for Correctness {
         })
     }
 
-    fn threshold<S>(_k: usize, n: usize, mut sub_ck: S) -> Result<Self, ErrorKind>
+    /// Constructor for the correctness properties of the `thresh` fragment
+    // Cannot be constfn because it takes a closure.
+    pub fn threshold<S>(_k: usize, n: usize, mut sub_ck: S) -> Result<Self, ErrorKind>
     where
-        S: FnMut(usize) -> Result<Self, ErrorKind>,
+        S: FnMut(usize) -> Self,
     {
         let mut num_args = 0;
         for i in 0..n {
-            let subtype = sub_ck(i)?;
+            let subtype = sub_ck(i);
             num_args += match subtype.input {
                 Input::Zero => 0,
                 Input::One | Input::OneNonZero => 1,
diff --git a/src/miniscript/types/extra_props.rs b/src/miniscript/types/extra_props.rs
index 9d57b2df6..9d8c9274c 100644
--- a/src/miniscript/types/extra_props.rs
+++ b/src/miniscript/types/extra_props.rs
@@ -8,7 +8,7 @@ use core::iter::once;
 
 use bitcoin::{absolute, Sequence};
 
-use super::{Error, ErrorKind, Property, ScriptContext};
+use super::{Error, ErrorKind, ScriptContext};
 use crate::miniscript::context::SigType;
 use crate::prelude::*;
 use crate::{script_num_size, MiniscriptKey, Terminal};
@@ -174,8 +174,9 @@ impl ExtData {
     };
 }
 
-impl Property for ExtData {
-    fn sanity_checks(&self) {
+impl ExtData {
+    /// Confirm invariants of the extra property checker.
+    pub fn sanity_checks(&self) {
         debug_assert_eq!(
             self.stack_elem_count_sat.is_some(),
             self.exec_stack_elem_count_sat.is_some()
@@ -186,11 +187,8 @@ impl Property for ExtData {
         );
     }
 
-    fn from_true() -> Self { Self::TRUE }
-
-    fn from_false() -> Self { Self::FALSE }
-
-    fn from_pk_k<Ctx: ScriptContext>() -> Self {
+    /// Extra properties for the `pk_k` fragment.
+    pub fn pk_k<Ctx: ScriptContext>() -> Self {
         ExtData {
             pk_cost: match Ctx::sig_type() {
                 SigType::Ecdsa => 34,
@@ -211,7 +209,8 @@ impl Property for ExtData {
         }
     }
 
-    fn from_pk_h<Ctx: ScriptContext>() -> Self {
+    /// Extra properties for the `pk_h` fragment.
+    pub fn pk_h<Ctx: ScriptContext>() -> Self {
         ExtData {
             pk_cost: 24,
             has_free_verify: false,
@@ -232,7 +231,8 @@ impl Property for ExtData {
         }
     }
 
-    fn from_multi(k: usize, n: usize) -> Self {
+    /// Extra properties for the `multi` fragment.
+    pub fn multi(k: usize, n: usize) -> Self {
         let num_cost = match (k > 16, n > 16) {
             (true, true) => 4,
             (false, true) => 3,
@@ -249,13 +249,14 @@ impl Property for ExtData {
             stack_elem_count_dissat: Some(k + 1),
             max_sat_size: Some((1 + 73 * k, 1 + 73 * k)),
             max_dissat_size: Some((1 + k, 1 + k)),
-            timelock_info: TimelockInfo::default(),
+            timelock_info: TimelockInfo::new(),
             exec_stack_elem_count_sat: Some(n), // n pks
             exec_stack_elem_count_dissat: Some(n),
         }
     }
 
-    fn from_multi_a(k: usize, n: usize) -> Self {
+    /// Extra properties for the `multi_a` fragment.
+    pub fn multi_a(k: usize, n: usize) -> Self {
         let num_cost = match (k > 16, n > 16) {
             (true, true) => 4,
             (false, true) => 3,
@@ -271,18 +272,14 @@ impl Property for ExtData {
             stack_elem_count_dissat: Some(n),
             max_sat_size: Some(((n - k) + 66 * k, (n - k) + 66 * k)),
             max_dissat_size: Some((n, n)),
-            timelock_info: TimelockInfo::default(),
+            timelock_info: TimelockInfo::new(),
             exec_stack_elem_count_sat: Some(2), // the two nums before num equal verify
             exec_stack_elem_count_dissat: Some(2),
         }
     }
 
-    fn from_hash() -> Self {
-        //never called directly
-        unreachable!()
-    }
-
-    fn from_sha256() -> Self {
+    /// Extra properties for the `sha256` fragment.
+    pub const fn sha256() -> Self {
         ExtData {
             pk_cost: 33 + 6,
             has_free_verify: true,
@@ -291,13 +288,14 @@ impl Property for ExtData {
             stack_elem_count_dissat: Some(1),
             max_sat_size: Some((33, 33)),
             max_dissat_size: Some((33, 33)),
-            timelock_info: TimelockInfo::default(),
+            timelock_info: TimelockInfo::new(),
             exec_stack_elem_count_sat: Some(2), // either size <32> or <hash256> <32 byte>
             exec_stack_elem_count_dissat: Some(2),
         }
     }
 
-    fn from_hash256() -> Self {
+    /// Extra properties for the `hash256` fragment.
+    pub const fn hash256() -> Self {
         ExtData {
             pk_cost: 33 + 6,
             has_free_verify: true,
@@ -306,13 +304,14 @@ impl Property for ExtData {
             stack_elem_count_dissat: Some(1),
             max_sat_size: Some((33, 33)),
             max_dissat_size: Some((33, 33)),
-            timelock_info: TimelockInfo::default(),
+            timelock_info: TimelockInfo::new(),
             exec_stack_elem_count_sat: Some(2), // either size <32> or <hash256> <32 byte>
             exec_stack_elem_count_dissat: Some(2),
         }
     }
 
-    fn from_ripemd160() -> Self {
+    /// Extra properties for the `ripemd160` fragment.
+    pub const fn ripemd160() -> Self {
         ExtData {
             pk_cost: 21 + 6,
             has_free_verify: true,
@@ -321,13 +320,14 @@ impl Property for ExtData {
             stack_elem_count_dissat: Some(1),
             max_sat_size: Some((33, 33)),
             max_dissat_size: Some((33, 33)),
-            timelock_info: TimelockInfo::default(),
+            timelock_info: TimelockInfo::new(),
             exec_stack_elem_count_sat: Some(2), // either size <32> or <hash256> <20 byte>
             exec_stack_elem_count_dissat: Some(2),
         }
     }
 
-    fn from_hash160() -> Self {
+    /// Extra properties for the `hash160` fragment.
+    pub const fn hash160() -> Self {
         ExtData {
             pk_cost: 21 + 6,
             has_free_verify: true,
@@ -336,15 +336,14 @@ impl Property for ExtData {
             stack_elem_count_dissat: Some(1),
             max_sat_size: Some((33, 33)),
             max_dissat_size: Some((33, 33)),
-            timelock_info: TimelockInfo::default(),
+            timelock_info: TimelockInfo::new(),
             exec_stack_elem_count_sat: Some(2), // either size <32> or <hash256> <20 byte>
             exec_stack_elem_count_dissat: Some(2),
         }
     }
 
-    fn from_time(_t: u32) -> Self { unreachable!() }
-
-    fn from_after(t: absolute::LockTime) -> Self {
+    /// Extra properties for the `after` fragment.
+    pub fn after(t: absolute::LockTime) -> Self {
         ExtData {
             pk_cost: script_num_size(t.to_consensus_u32() as usize) + 1,
             has_free_verify: false,
@@ -365,7 +364,8 @@ impl Property for ExtData {
         }
     }
 
-    fn from_older(t: Sequence) -> Self {
+    /// Extra properties for the `older` fragment.
+    pub fn older(t: Sequence) -> Self {
         ExtData {
             pk_cost: script_num_size(t.to_consensus_u32() as usize) + 1,
             has_free_verify: false,
@@ -386,8 +386,9 @@ impl Property for ExtData {
         }
     }
 
-    fn cast_alt(self) -> Result<Self, ErrorKind> {
-        Ok(ExtData {
+    /// Extra properties for the `a:` fragment.
+    pub const fn cast_alt(self) -> Self {
+        ExtData {
             pk_cost: self.pk_cost + 2,
             has_free_verify: false,
             ops: OpLimits::new(2 + self.ops.count, self.ops.sat, self.ops.nsat),
@@ -398,11 +399,12 @@ impl Property for ExtData {
             timelock_info: self.timelock_info,
             exec_stack_elem_count_sat: self.exec_stack_elem_count_sat,
             exec_stack_elem_count_dissat: self.exec_stack_elem_count_dissat,
-        })
+        }
     }
 
-    fn cast_swap(self) -> Result<Self, ErrorKind> {
-        Ok(ExtData {
+    /// Extra properties for the `s:` fragment.
+    pub const fn cast_swap(self) -> Self {
+        ExtData {
             pk_cost: self.pk_cost + 1,
             has_free_verify: self.has_free_verify,
             ops: OpLimits::new(1 + self.ops.count, self.ops.sat, self.ops.nsat),
@@ -413,11 +415,12 @@ impl Property for ExtData {
             timelock_info: self.timelock_info,
             exec_stack_elem_count_sat: self.exec_stack_elem_count_sat,
             exec_stack_elem_count_dissat: self.exec_stack_elem_count_dissat,
-        })
+        }
     }
 
-    fn cast_check(self) -> Result<Self, ErrorKind> {
-        Ok(ExtData {
+    /// Extra properties for the `c:` fragment.
+    pub const fn cast_check(self) -> Self {
+        ExtData {
             pk_cost: self.pk_cost + 1,
             has_free_verify: true,
             ops: OpLimits::new(1 + self.ops.count, self.ops.sat, self.ops.nsat),
@@ -428,11 +431,12 @@ impl Property for ExtData {
             timelock_info: self.timelock_info,
             exec_stack_elem_count_sat: self.exec_stack_elem_count_sat,
             exec_stack_elem_count_dissat: self.exec_stack_elem_count_dissat,
-        })
+        }
     }
 
-    fn cast_dupif(self) -> Result<Self, ErrorKind> {
-        Ok(ExtData {
+    /// Extra properties for the `d:` fragment.
+    pub fn cast_dupif(self) -> Self {
+        ExtData {
             pk_cost: self.pk_cost + 3,
             has_free_verify: false,
             ops: OpLimits::new(3 + self.ops.count, self.ops.sat, Some(0)),
@@ -446,12 +450,13 @@ impl Property for ExtData {
             // Even all V types push something onto the stack and then remove them
             exec_stack_elem_count_sat: self.exec_stack_elem_count_sat,
             exec_stack_elem_count_dissat: Some(1),
-        })
+        }
     }
 
-    fn cast_verify(self) -> Result<Self, ErrorKind> {
+    /// Extra properties for the `v:` fragment.
+    pub fn cast_verify(self) -> Self {
         let verify_cost = usize::from(!self.has_free_verify);
-        Ok(ExtData {
+        ExtData {
             pk_cost: self.pk_cost + usize::from(!self.has_free_verify),
             has_free_verify: false,
             ops: OpLimits::new(verify_cost + self.ops.count, self.ops.sat, None),
@@ -462,11 +467,12 @@ impl Property for ExtData {
             timelock_info: self.timelock_info,
             exec_stack_elem_count_sat: self.exec_stack_elem_count_sat,
             exec_stack_elem_count_dissat: None,
-        })
+        }
     }
 
-    fn cast_nonzero(self) -> Result<Self, ErrorKind> {
-        Ok(ExtData {
+    /// Extra properties for the `j:` fragment.
+    pub const fn cast_nonzero(self) -> Self {
+        ExtData {
             pk_cost: self.pk_cost + 4,
             has_free_verify: false,
             ops: OpLimits::new(4 + self.ops.count, self.ops.sat, Some(0)),
@@ -477,11 +483,12 @@ impl Property for ExtData {
             timelock_info: self.timelock_info,
             exec_stack_elem_count_sat: self.exec_stack_elem_count_sat,
             exec_stack_elem_count_dissat: Some(1),
-        })
+        }
     }
 
-    fn cast_zeronotequal(self) -> Result<Self, ErrorKind> {
-        Ok(ExtData {
+    /// Extra properties for the `n:` fragment.
+    pub const fn cast_zeronotequal(self) -> Self {
+        ExtData {
             pk_cost: self.pk_cost + 1,
             has_free_verify: false,
             ops: OpLimits::new(1 + self.ops.count, self.ops.sat, self.ops.nsat),
@@ -493,16 +500,23 @@ impl Property for ExtData {
             // Technically max(1, self.exec_stack_elem_count_sat), same rationale as cast_dupif
             exec_stack_elem_count_sat: self.exec_stack_elem_count_sat,
             exec_stack_elem_count_dissat: self.exec_stack_elem_count_dissat,
-        })
+        }
     }
 
-    fn cast_or_i_false(self) -> Result<Self, ErrorKind> {
-        // never called directly
-        unreachable!()
-    }
+    /// Cast by changing `[X]` to `AndV([X], True)`
+    pub fn cast_true(self) -> Self { Self::and_v(self, Self::TRUE) }
+
+    /// Cast by changing `[X]` to `or_i([X], 0)`. Default implementation
+    /// simply passes through to `cast_or_i_false`
+    pub fn cast_unlikely(self) -> Self { Self::or_i(self, Self::FALSE) }
 
-    fn and_b(l: Self, r: Self) -> Result<Self, ErrorKind> {
-        Ok(ExtData {
+    /// Cast by changing `[X]` to `or_i(0, [X])`. Default implementation
+    /// simply passes through to `cast_or_i_false`
+    pub fn cast_likely(self) -> Self { Self::or_i(Self::FALSE, self) }
+
+    /// Extra properties for the `and_b` fragment.
+    pub fn and_b(l: Self, r: Self) -> Self {
+        ExtData {
             pk_cost: l.pk_cost + r.pk_cost + 1,
             has_free_verify: false,
             ops: OpLimits::new(
@@ -533,11 +547,12 @@ impl Property for ExtData {
                 l.exec_stack_elem_count_dissat,
                 r.exec_stack_elem_count_dissat.map(|x| x + 1),
             ),
-        })
+        }
     }
 
-    fn and_v(l: Self, r: Self) -> Result<Self, ErrorKind> {
-        Ok(ExtData {
+    /// Extra properties for the `and_v` fragment.
+    pub fn and_v(l: Self, r: Self) -> Self {
+        ExtData {
             pk_cost: l.pk_cost + r.pk_cost,
             has_free_verify: r.has_free_verify,
             ops: OpLimits::new(l.ops.count + r.ops.count, opt_add(l.ops.sat, r.ops.sat), None),
@@ -556,11 +571,12 @@ impl Property for ExtData {
                 r.exec_stack_elem_count_sat,
             ),
             exec_stack_elem_count_dissat: None,
-        })
+        }
     }
 
-    fn or_b(l: Self, r: Self) -> Result<Self, ErrorKind> {
-        Ok(ExtData {
+    /// Extra properties for the `or_b` fragment.
+    pub fn or_b(l: Self, r: Self) -> Self {
+        ExtData {
             pk_cost: l.pk_cost + r.pk_cost + 1,
             has_free_verify: false,
             ops: OpLimits::new(
@@ -595,11 +611,12 @@ impl Property for ExtData {
                 l.exec_stack_elem_count_dissat,
                 r.exec_stack_elem_count_dissat.map(|x| x + 1),
             ),
-        })
+        }
     }
 
-    fn or_d(l: Self, r: Self) -> Result<Self, ErrorKind> {
-        let res = ExtData {
+    /// Extra properties for the `or_d` fragment.
+    pub fn or_d(l: Self, r: Self) -> Self {
+        ExtData {
             pk_cost: l.pk_cost + r.pk_cost + 3,
             has_free_verify: false,
             ops: OpLimits::new(
@@ -632,12 +649,12 @@ impl Property for ExtData {
                 l.exec_stack_elem_count_dissat,
                 r.exec_stack_elem_count_dissat.map(|x| x + 1),
             ),
-        };
-        Ok(res)
+        }
     }
 
-    fn or_c(l: Self, r: Self) -> Result<Self, ErrorKind> {
-        Ok(ExtData {
+    /// Extra properties for the `or_c` fragment.
+    pub fn or_c(l: Self, r: Self) -> Self {
+        ExtData {
             pk_cost: l.pk_cost + r.pk_cost + 2,
             has_free_verify: false,
             ops: OpLimits::new(
@@ -663,11 +680,12 @@ impl Property for ExtData {
                 opt_max(r.exec_stack_elem_count_sat, l.exec_stack_elem_count_dissat),
             ),
             exec_stack_elem_count_dissat: None,
-        })
+        }
     }
 
-    fn or_i(l: Self, r: Self) -> Result<Self, ErrorKind> {
-        Ok(ExtData {
+    /// Extra properties for the `or_i` fragment.
+    pub fn or_i(l: Self, r: Self) -> Self {
+        ExtData {
             pk_cost: l.pk_cost + r.pk_cost + 3,
             has_free_verify: false,
             ops: OpLimits::new(
@@ -709,11 +727,12 @@ impl Property for ExtData {
                 l.exec_stack_elem_count_dissat,
                 r.exec_stack_elem_count_dissat,
             ),
-        })
+        }
     }
 
-    fn and_or(a: Self, b: Self, c: Self) -> Result<Self, ErrorKind> {
-        Ok(ExtData {
+    /// Extra properties for the `andor` fragment.
+    pub fn and_or(a: Self, b: Self, c: Self) -> Self {
+        ExtData {
             pk_cost: a.pk_cost + b.pk_cost + c.pk_cost + 3,
             has_free_verify: false,
             ops: OpLimits::new(
@@ -751,12 +770,13 @@ impl Property for ExtData {
                 a.exec_stack_elem_count_dissat,
                 c.exec_stack_elem_count_dissat,
             ),
-        })
+        }
     }
 
-    fn threshold<S>(k: usize, n: usize, mut sub_ck: S) -> Result<Self, ErrorKind>
+    /// Extra properties for the `thresh` fragment.
+    pub fn threshold<S>(k: usize, n: usize, mut sub_ck: S) -> Self
     where
-        S: FnMut(usize) -> Result<Self, ErrorKind>,
+        S: FnMut(usize) -> Self,
     {
         let mut pk_cost = 1 + script_num_size(k); //Equal and k
         let mut ops_count = 0;
@@ -772,7 +792,7 @@ impl Property for ExtData {
         let mut exec_stack_elem_count_dissat = Some(0);
 
         for i in 0..n {
-            let sub = sub_ck(i)?;
+            let sub = sub_ck(i);
 
             pk_cost += sub.pk_cost;
             ops_count += sub.ops.count;
@@ -854,7 +874,7 @@ impl Property for ExtData {
                 }
             });
 
-        Ok(ExtData {
+        ExtData {
             pk_cost: pk_cost + n - 1, //all pk cost + (n-1)*ADD
             has_free_verify: true,
             ops: OpLimits::new(
@@ -869,11 +889,9 @@ impl Property for ExtData {
             timelock_info: TimelockInfo::combine_threshold(k, timelocks),
             exec_stack_elem_count_sat,
             exec_stack_elem_count_dissat,
-        })
+        }
     }
-}
 
-impl ExtData {
     /// Compute the type of a fragment assuming all the children of
     /// Miniscript have been computed already.
     pub fn type_check<Pk, Ctx>(fragment: &Terminal<Pk, Ctx>) -> Result<Self, Error>
@@ -881,15 +899,11 @@ impl ExtData {
         Ctx: ScriptContext,
         Pk: MiniscriptKey,
     {
-        let wrap_err = |result: Result<Self, ErrorKind>| {
-            result.map_err(|kind| Error { fragment_string: fragment.to_string(), error: kind })
-        };
-
         let ret = match *fragment {
-            Terminal::True => Ok(Self::from_true()),
-            Terminal::False => Ok(Self::from_false()),
-            Terminal::PkK(..) => Ok(Self::from_pk_k::<Ctx>()),
-            Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::from_pk_h::<Ctx>()),
+            Terminal::True => Self::TRUE,
+            Terminal::False => Self::FALSE,
+            Terminal::PkK(..) => Self::pk_k::<Ctx>(),
+            Terminal::PkH(..) | Terminal::RawPkH(..) => Self::pk_h::<Ctx>(),
             Terminal::Multi(k, ref pks) | Terminal::MultiA(k, ref pks) => {
                 if k == 0 {
                     return Err(Error {
@@ -904,8 +918,8 @@ impl ExtData {
                     });
                 }
                 match *fragment {
-                    Terminal::Multi(..) => Ok(Self::from_multi(k, pks.len())),
-                    Terminal::MultiA(..) => Ok(Self::from_multi_a(k, pks.len())),
+                    Terminal::Multi(..) => Self::multi(k, pks.len()),
+                    Terminal::MultiA(..) => Self::multi_a(k, pks.len()),
                     _ => unreachable!(),
                 }
             }
@@ -919,7 +933,7 @@ impl ExtData {
                         error: ErrorKind::InvalidTime,
                     });
                 }
-                Ok(Self::from_after(t.into()))
+                Self::after(t.into())
             }
             Terminal::Older(t) => {
                 if t == Sequence::ZERO || !t.is_relative_lock_time() {
@@ -928,54 +942,54 @@ impl ExtData {
                         error: ErrorKind::InvalidTime,
                     });
                 }
-                Ok(Self::from_older(t))
+                Self::older(t)
             }
-            Terminal::Sha256(..) => Ok(Self::from_sha256()),
-            Terminal::Hash256(..) => Ok(Self::from_hash256()),
-            Terminal::Ripemd160(..) => Ok(Self::from_ripemd160()),
-            Terminal::Hash160(..) => Ok(Self::from_hash160()),
-            Terminal::Alt(ref sub) => wrap_err(Self::cast_alt(sub.ext)),
-            Terminal::Swap(ref sub) => wrap_err(Self::cast_swap(sub.ext)),
-            Terminal::Check(ref sub) => wrap_err(Self::cast_check(sub.ext)),
-            Terminal::DupIf(ref sub) => wrap_err(Self::cast_dupif(sub.ext)),
-            Terminal::Verify(ref sub) => wrap_err(Self::cast_verify(sub.ext)),
-            Terminal::NonZero(ref sub) => wrap_err(Self::cast_nonzero(sub.ext)),
-            Terminal::ZeroNotEqual(ref sub) => wrap_err(Self::cast_zeronotequal(sub.ext)),
+            Terminal::Sha256(..) => Self::sha256(),
+            Terminal::Hash256(..) => Self::hash256(),
+            Terminal::Ripemd160(..) => Self::ripemd160(),
+            Terminal::Hash160(..) => Self::hash160(),
+            Terminal::Alt(ref sub) => Self::cast_alt(sub.ext),
+            Terminal::Swap(ref sub) => Self::cast_swap(sub.ext),
+            Terminal::Check(ref sub) => Self::cast_check(sub.ext),
+            Terminal::DupIf(ref sub) => Self::cast_dupif(sub.ext),
+            Terminal::Verify(ref sub) => Self::cast_verify(sub.ext),
+            Terminal::NonZero(ref sub) => Self::cast_nonzero(sub.ext),
+            Terminal::ZeroNotEqual(ref sub) => Self::cast_zeronotequal(sub.ext),
             Terminal::AndB(ref l, ref r) => {
                 let ltype = l.ext;
                 let rtype = r.ext;
-                wrap_err(Self::and_b(ltype, rtype))
+                Self::and_b(ltype, rtype)
             }
             Terminal::AndV(ref l, ref r) => {
                 let ltype = l.ext;
                 let rtype = r.ext;
-                wrap_err(Self::and_v(ltype, rtype))
+                Self::and_v(ltype, rtype)
             }
             Terminal::OrB(ref l, ref r) => {
                 let ltype = l.ext;
                 let rtype = r.ext;
-                wrap_err(Self::or_b(ltype, rtype))
+                Self::or_b(ltype, rtype)
             }
             Terminal::OrD(ref l, ref r) => {
                 let ltype = l.ext;
                 let rtype = r.ext;
-                wrap_err(Self::or_d(ltype, rtype))
+                Self::or_d(ltype, rtype)
             }
             Terminal::OrC(ref l, ref r) => {
                 let ltype = l.ext;
                 let rtype = r.ext;
-                wrap_err(Self::or_c(ltype, rtype))
+                Self::or_c(ltype, rtype)
             }
             Terminal::OrI(ref l, ref r) => {
                 let ltype = l.ext;
                 let rtype = r.ext;
-                wrap_err(Self::or_i(ltype, rtype))
+                Self::or_i(ltype, rtype)
             }
             Terminal::AndOr(ref a, ref b, ref c) => {
                 let atype = a.ext;
                 let btype = b.ext;
                 let ctype = c.ext;
-                wrap_err(Self::and_or(atype, btype, ctype))
+                Self::and_or(atype, btype, ctype)
             }
             Terminal::Thresh(k, ref subs) => {
                 if k == 0 {
@@ -991,15 +1005,11 @@ impl ExtData {
                     });
                 }
 
-                let res = Self::threshold(k, subs.len(), |n| Ok(subs[n].ext));
-
-                res.map_err(|kind| Error { fragment_string: fragment.to_string(), error: kind })
+                Self::threshold(k, subs.len(), |n| subs[n].ext)
             }
         };
-        if let Ok(ref ret) = ret {
-            ret.sanity_checks()
-        }
-        ret
+        ret.sanity_checks();
+        Ok(ret)
     }
 }
 
diff --git a/src/miniscript/types/malleability.rs b/src/miniscript/types/malleability.rs
index 71d644d97..4285e2ac3 100644
--- a/src/miniscript/types/malleability.rs
+++ b/src/miniscript/types/malleability.rs
@@ -2,9 +2,6 @@
 
 //! Malleability-related Type properties
 
-use super::{ErrorKind, Property};
-use crate::ScriptContext;
-
 /// Whether the fragment has a dissatisfaction, and if so, whether
 /// it is unique. Affects both correctness and malleability-freeness,
 /// since we assume 3rd parties are able to produce dissatisfactions
@@ -27,11 +24,21 @@ pub enum Dissat {
 }
 
 impl Dissat {
+    // FIXME rustc should eventually support derived == on enums in constfns
+    const fn constfn_eq(self, other: Self) -> bool {
+        matches!(
+            (self, other),
+            (Dissat::None, Dissat::None)
+                | (Dissat::Unique, Dissat::Unique)
+                | (Dissat::Unknown, Dissat::Unknown)
+        )
+    }
+
     /// Check whether given `Dissat` is a subtype of `other`. That is,
     /// if some Dissat is `Unique` then it must be `Unknown`.
-    fn is_subtype(&self, other: Self) -> bool {
+    const fn is_subtype(&self, other: Self) -> bool {
         match (*self, other) {
-            (x, y) if x == y => true,
+            (x, y) if x.constfn_eq(y) => true,
             (_, Dissat::Unknown) => true,
             _ => false,
         }
@@ -66,108 +73,108 @@ impl Malleability {
     /// This checks whether the argument `other` has attributes which are present
     /// in the given `Type`. This returns `true` on same arguments
     /// `a.is_subtype(a)` is `true`.
-    pub fn is_subtype(&self, other: Self) -> bool {
+    pub const fn is_subtype(&self, other: Self) -> bool {
         self.dissat.is_subtype(other.dissat)
             && self.safe >= other.safe
             && self.non_malleable >= other.non_malleable
     }
 }
 
-impl Property for Malleability {
-    fn from_true() -> Self {
-        Malleability { dissat: Dissat::None, safe: false, non_malleable: true }
-    }
-
-    fn from_false() -> Self {
-        Malleability { dissat: Dissat::Unique, safe: true, non_malleable: true }
-    }
-
-    fn from_pk_k<Ctx: ScriptContext>() -> Self {
+impl Malleability {
+    /// Constructor for the malleabilitiy properties of the `pk_k` fragment.
+    pub const fn pk_k() -> Self {
         Malleability { dissat: Dissat::Unique, safe: true, non_malleable: true }
     }
 
-    fn from_pk_h<Ctx: ScriptContext>() -> Self {
+    /// Constructor for the malleabilitiy properties of the `pk_h` fragment.
+    pub const fn pk_h() -> Self {
         Malleability { dissat: Dissat::Unique, safe: true, non_malleable: true }
     }
 
-    fn from_multi(_: usize, _: usize) -> Self {
+    /// Constructor for the malleabilitiy properties of the `multi` fragment.
+    pub const fn multi() -> Self {
         Malleability { dissat: Dissat::Unique, safe: true, non_malleable: true }
     }
 
-    fn from_multi_a(_: usize, _: usize) -> Self {
+    /// Constructor for the malleabilitiy properties of the `multi_a` fragment.
+    pub const fn multi_a() -> Self {
         Malleability { dissat: Dissat::Unique, safe: true, non_malleable: true }
     }
 
-    fn from_hash() -> Self {
+    /// Constructor for the malleabilitiy properties of any of the hash fragments.
+    pub const fn hash() -> Self {
         Malleability { dissat: Dissat::Unknown, safe: false, non_malleable: true }
     }
 
-    fn from_time(_: u32) -> Self {
+    /// Constructor for the malleabilitiy properties of either `after` or `older`.
+    pub const fn time() -> Self {
         Malleability { dissat: Dissat::None, safe: false, non_malleable: true }
     }
 
-    fn cast_alt(self) -> Result<Self, ErrorKind> { Ok(self) }
+    /// Constructor for the malleabilitiy properties of the `a:` fragment.
+    pub const fn cast_alt(self) -> Self { self }
 
-    fn cast_swap(self) -> Result<Self, ErrorKind> { Ok(self) }
+    /// Constructor for the malleabilitiy properties of the `s:` fragment.
+    pub const fn cast_swap(self) -> Self { self }
 
-    fn cast_check(self) -> Result<Self, ErrorKind> { Ok(self) }
+    /// Constructor for the malleabilitiy properties of the `c:` fragment.
+    pub const fn cast_check(self) -> Self { self }
 
-    fn cast_dupif(self) -> Result<Self, ErrorKind> {
-        Ok(Malleability {
-            dissat: if self.dissat == Dissat::None {
+    /// Constructor for the malleabilitiy properties of the `d:` fragment.
+    pub const fn cast_dupif(self) -> Self {
+        Malleability {
+            dissat: if self.dissat.constfn_eq(Dissat::None) {
                 Dissat::Unique
             } else {
                 Dissat::Unknown
             },
             safe: self.safe,
             non_malleable: self.non_malleable,
-        })
+        }
     }
 
-    fn cast_verify(self) -> Result<Self, ErrorKind> {
-        Ok(Malleability {
-            dissat: Dissat::None,
-            safe: self.safe,
-            non_malleable: self.non_malleable,
-        })
+    /// Constructor for the malleabilitiy properties of the `v:` fragment.
+    pub const fn cast_verify(self) -> Self {
+        Malleability { dissat: Dissat::None, safe: self.safe, non_malleable: self.non_malleable }
     }
 
-    fn cast_nonzero(self) -> Result<Self, ErrorKind> {
-        Ok(Malleability {
-            dissat: if self.dissat == Dissat::None {
+    /// Constructor for the malleabilitiy properties of the `j:` fragment.
+    pub const fn cast_nonzero(self) -> Self {
+        Malleability {
+            dissat: if self.dissat.constfn_eq(Dissat::None) {
                 Dissat::Unique
             } else {
                 Dissat::Unknown
             },
             safe: self.safe,
             non_malleable: self.non_malleable,
-        })
+        }
     }
 
-    fn cast_zeronotequal(self) -> Result<Self, ErrorKind> { Ok(self) }
+    /// Constructor for the malleabilitiy properties of the `n:` fragment.
+    pub const fn cast_zeronotequal(self) -> Self { self }
 
-    fn cast_true(self) -> Result<Self, ErrorKind> {
-        Ok(Malleability {
-            dissat: Dissat::None,
-            safe: self.safe,
-            non_malleable: self.non_malleable,
-        })
+    /// Constructor for the malleabilitiy properties of the `t:` fragment.
+    pub const fn cast_true(self) -> Self {
+        Malleability { dissat: Dissat::None, safe: self.safe, non_malleable: self.non_malleable }
     }
 
-    fn cast_or_i_false(self) -> Result<Self, ErrorKind> {
-        Ok(Malleability {
-            dissat: if self.dissat == Dissat::None {
+    /// Constructor for the malleabilitiy properties of the `l:` or `u:` fragments.
+    pub const fn cast_or_i_false(self) -> Self {
+        Malleability {
+            dissat: if self.dissat.constfn_eq(Dissat::None) {
                 Dissat::Unique
             } else {
                 Dissat::Unknown
             },
             safe: self.safe,
             non_malleable: self.non_malleable,
-        })
+        }
     }
 
-    fn and_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
-        Ok(Malleability {
+    /// Constructor for the malleabilitiy properties of the `and_b` fragment.
+    pub const fn and_b(left: Self, right: Self) -> Self {
+        Malleability {
             dissat: match (left.dissat, right.dissat) {
                 (Dissat::None, Dissat::None) => Dissat::None,
                 (Dissat::None, _) if left.safe => Dissat::None,
@@ -183,11 +190,12 @@ impl Property for Malleability {
             },
             safe: left.safe || right.safe,
             non_malleable: left.non_malleable && right.non_malleable,
-        })
+        }
     }
 
-    fn and_v(left: Self, right: Self) -> Result<Self, ErrorKind> {
-        Ok(Malleability {
+    /// Constructor for the malleabilitiy properties of the `and_v` fragment.
+    pub const fn and_v(left: Self, right: Self) -> Self {
+        Malleability {
             dissat: match (left.safe, right.dissat) {
                 (_, Dissat::None) => Dissat::None, // fy
                 (true, _) => Dissat::None,         // sx
@@ -195,45 +203,49 @@ impl Property for Malleability {
             },
             safe: left.safe || right.safe,
             non_malleable: left.non_malleable && right.non_malleable,
-        })
+        }
     }
 
-    fn or_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
-        Ok(Malleability {
+    /// Constructor for the malleabilitiy properties of the `or_b` fragment.
+    pub const fn or_b(left: Self, right: Self) -> Self {
+        Malleability {
             dissat: Dissat::Unique,
             safe: left.safe && right.safe,
             non_malleable: left.non_malleable
-                && left.dissat == Dissat::Unique
+                && left.dissat.constfn_eq(Dissat::Unique)
                 && right.non_malleable
-                && right.dissat == Dissat::Unique
+                && right.dissat.constfn_eq(Dissat::Unique)
                 && (left.safe || right.safe),
-        })
+        }
     }
 
-    fn or_d(left: Self, right: Self) -> Result<Self, ErrorKind> {
-        Ok(Malleability {
+    /// Constructor for the malleabilitiy properties of the `or_d` fragment.
+    pub const fn or_d(left: Self, right: Self) -> Self {
+        Malleability {
             dissat: right.dissat,
             safe: left.safe && right.safe,
             non_malleable: left.non_malleable
-                && left.dissat == Dissat::Unique
+                && left.dissat.constfn_eq(Dissat::Unique)
                 && right.non_malleable
                 && (left.safe || right.safe),
-        })
+        }
     }
 
-    fn or_c(left: Self, right: Self) -> Result<Self, ErrorKind> {
-        Ok(Malleability {
+    /// Constructor for the malleabilitiy properties of the `or_c` fragment.
+    pub const fn or_c(left: Self, right: Self) -> Self {
+        Malleability {
             dissat: Dissat::None,
             safe: left.safe && right.safe,
             non_malleable: left.non_malleable
-                && left.dissat == Dissat::Unique
+                && left.dissat.constfn_eq(Dissat::Unique)
                 && right.non_malleable
                 && (left.safe || right.safe),
-        })
+        }
     }
 
-    fn or_i(left: Self, right: Self) -> Result<Self, ErrorKind> {
-        Ok(Malleability {
+    /// Constructor for the malleabilitiy properties of the `or_i` fragment.
+    pub const fn or_i(left: Self, right: Self) -> Self {
+        Malleability {
             dissat: match (left.dissat, right.dissat) {
                 (Dissat::None, Dissat::None) => Dissat::None,
                 (Dissat::Unique, Dissat::None) => Dissat::Unique,
@@ -242,11 +254,12 @@ impl Property for Malleability {
             },
             safe: left.safe && right.safe,
             non_malleable: left.non_malleable && right.non_malleable && (left.safe || right.safe),
-        })
+        }
     }
 
-    fn and_or(a: Self, b: Self, c: Self) -> Result<Self, ErrorKind> {
-        Ok(Malleability {
+    /// Constructor for the malleabilitiy properties of the `andor` fragment.
+    pub const fn and_or(a: Self, b: Self, c: Self) -> Self {
+        Malleability {
             dissat: match (a.safe, b.dissat, c.dissat) {
                 (_, Dissat::None, Dissat::Unique) => Dissat::Unique, //E: ez fy
                 (true, _, Dissat::Unique) => Dissat::Unique,         // E: ez sx
@@ -257,26 +270,28 @@ impl Property for Malleability {
             safe: (a.safe || b.safe) && c.safe,
             non_malleable: a.non_malleable
                 && c.non_malleable
-                && a.dissat == Dissat::Unique
+                && a.dissat.constfn_eq(Dissat::Unique)
                 && b.non_malleable
                 && (a.safe || b.safe || c.safe),
-        })
+        }
     }
 
-    fn threshold<S>(k: usize, n: usize, mut sub_ck: S) -> Result<Self, ErrorKind>
+    /// Constructor for the malleabilitiy properties of the `thresh` fragment.
+    // Cannot be constfn because it takes a closure.
+    pub fn threshold<S>(k: usize, n: usize, mut sub_ck: S) -> Self
     where
-        S: FnMut(usize) -> Result<Self, ErrorKind>,
+        S: FnMut(usize) -> Self,
     {
         let mut safe_count = 0;
         let mut all_are_dissat_unique = true;
         let mut all_are_non_malleable = true;
         for i in 0..n {
-            let subtype = sub_ck(i)?;
+            let subtype = sub_ck(i);
             safe_count += usize::from(subtype.safe);
             all_are_dissat_unique &= subtype.dissat == Dissat::Unique;
             all_are_non_malleable &= subtype.non_malleable;
         }
-        Ok(Malleability {
+        Malleability {
             dissat: if all_are_dissat_unique && safe_count == n {
                 Dissat::Unique
             } else {
@@ -284,6 +299,6 @@ impl Property for Malleability {
             },
             safe: safe_count > n - k,
             non_malleable: all_are_non_malleable && safe_count >= n - k && all_are_dissat_unique,
-        })
+        }
     }
 }
diff --git a/src/miniscript/types/mod.rs b/src/miniscript/types/mod.rs
index ae154e004..887aa8b8d 100644
--- a/src/miniscript/types/mod.rs
+++ b/src/miniscript/types/mod.rs
@@ -226,310 +226,251 @@ impl Type {
     /// This checks whether the argument `other` has attributes which are present
     /// in the given `Type`. This returns `true` on same arguments
     /// `a.is_subtype(a)` is `true`.
-    pub fn is_subtype(&self, other: Self) -> bool {
+    pub const fn is_subtype(&self, other: Self) -> bool {
         self.corr.is_subtype(other.corr) && self.mall.is_subtype(other.mall)
     }
-}
-/// Trait representing a type property, which defines how the property
-/// propagates from terminals to the root of a Miniscript
-pub trait Property: Sized {
-    /// Any extra sanity checks/assertions that should be applied after
-    /// typechecking
-    fn sanity_checks(&self) {
-        // no checks by default
-    }
-
-    /// Type property of the `True` fragment
-    fn from_true() -> Self;
-
-    /// Type property of the `False` fragment
-    fn from_false() -> Self;
-
-    /// Type property of the `PkK` fragment
-    fn from_pk_k<Ctx: ScriptContext>() -> Self;
-
-    /// Type property of the `PkH` fragment
-    fn from_pk_h<Ctx: ScriptContext>() -> Self;
-
-    /// Type property of a `Multi` fragment
-    fn from_multi(k: usize, n: usize) -> Self;
-
-    /// Type property of a `MultiA` fragment
-    fn from_multi_a(k: usize, n: usize) -> Self;
-
-    /// Type property of a hash fragment
-    fn from_hash() -> Self;
-
-    /// Type property of a `Sha256` hash. Default implementation simply
-    /// passes through to `from_hash`
-    fn from_sha256() -> Self { Self::from_hash() }
-
-    /// Type property of a `Hash256` hash. Default implementation simply
-    /// passes through to `from_hash`
-    fn from_hash256() -> Self { Self::from_hash() }
-
-    /// Type property of a `Ripemd160` hash. Default implementation simply
-    /// passes through to `from_hash`
-    fn from_ripemd160() -> Self { Self::from_hash() }
-
-    /// Type property of a `Hash160` hash. Default implementation simply
-    /// passes through to `from_hash`
-    fn from_hash160() -> Self { Self::from_hash() }
-
-    /// Type property of a timelock
-    fn from_time(t: u32) -> Self;
-
-    /// Type property of an absolute timelock. Default implementation simply
-    /// passes through to `from_time`
-    fn from_after(t: absolute::LockTime) -> Self { Self::from_time(t.to_consensus_u32()) }
-
-    /// Type property of a relative timelock. Default implementation simply
-    /// passes through to `from_time`
-    fn from_older(t: Sequence) -> Self { Self::from_time(t.to_consensus_u32()) }
-
-    /// Cast using the `Alt` wrapper
-    fn cast_alt(self) -> Result<Self, ErrorKind>;
-
-    /// Cast using the `Swap` wrapper
-    fn cast_swap(self) -> Result<Self, ErrorKind>;
-
-    /// Cast using the `Check` wrapper
-    fn cast_check(self) -> Result<Self, ErrorKind>;
-
-    /// Cast using the `DupIf` wrapper
-    fn cast_dupif(self) -> Result<Self, ErrorKind>;
-
-    /// Cast using the `Verify` wrapper
-    fn cast_verify(self) -> Result<Self, ErrorKind>;
-
-    /// Cast using the `NonZero` wrapper
-    fn cast_nonzero(self) -> Result<Self, ErrorKind>;
-
-    /// Cast using the `ZeroNotEqual` wrapper
-    fn cast_zeronotequal(self) -> Result<Self, ErrorKind>;
-
-    /// Cast by changing `[X]` to `AndV([X], True)`
-    fn cast_true(self) -> Result<Self, ErrorKind> { Self::and_v(self, Self::from_true()) }
-
-    /// Cast by changing `[X]` to `or_i([X], 0)` or `or_i(0, [X])`
-    fn cast_or_i_false(self) -> Result<Self, ErrorKind>;
-
-    /// Cast by changing `[X]` to `or_i([X], 0)`. Default implementation
-    /// simply passes through to `cast_or_i_false`
-    fn cast_unlikely(self) -> Result<Self, ErrorKind> { Self::or_i(self, Self::from_false()) }
-
-    /// Cast by changing `[X]` to `or_i(0, [X])`. Default implementation
-    /// simply passes through to `cast_or_i_false`
-    fn cast_likely(self) -> Result<Self, ErrorKind> { Self::or_i(Self::from_false(), self) }
-
-    /// Computes the type of an `AndB` fragment
-    fn and_b(left: Self, right: Self) -> Result<Self, ErrorKind>;
-
-    /// Computes the type of an `AndV` fragment
-    fn and_v(left: Self, right: Self) -> Result<Self, ErrorKind>;
-
-    /// Computes the type of an `AndN` fragment
-    fn and_n(left: Self, right: Self) -> Result<Self, ErrorKind> {
-        Self::and_or(left, right, Self::from_false())
-    }
-
-    /// Computes the type of an `OrB` fragment
-    fn or_b(left: Self, right: Self) -> Result<Self, ErrorKind>;
-
-    /// Computes the type of an `OrD` fragment
-    fn or_d(left: Self, right: Self) -> Result<Self, ErrorKind>;
 
-    /// Computes the type of an `OrC` fragment
-    fn or_c(left: Self, right: Self) -> Result<Self, ErrorKind>;
-
-    /// Computes the type of an `OrI` fragment
-    fn or_i(left: Self, right: Self) -> Result<Self, ErrorKind>;
-
-    /// Computes the type of an `AndOr` fragment
-    fn and_or(a: Self, b: Self, c: Self) -> Result<Self, ErrorKind>;
-
-    /// Computes the type of an `Thresh` fragment
-    fn threshold<S>(k: usize, n: usize, sub_ck: S) -> Result<Self, ErrorKind>
-    where
-        S: FnMut(usize) -> Result<Self, ErrorKind>;
-}
-
-impl Property for Type {
-    fn sanity_checks(&self) {
+    /// Confirm invariants of the type checker.
+    pub fn sanity_checks(&self) {
         debug_assert!(!self.corr.dissatisfiable || self.mall.dissat != Dissat::None);
         debug_assert!(self.mall.dissat == Dissat::None || self.corr.base != Base::V);
         debug_assert!(self.mall.safe || self.corr.base != Base::K);
         debug_assert!(self.mall.non_malleable || self.corr.input != Input::Zero);
     }
 
-    fn from_true() -> Self { Type { corr: Property::from_true(), mall: Property::from_true() } }
-
-    fn from_false() -> Self { Type { corr: Property::from_false(), mall: Property::from_false() } }
+    /// Constructor for the type of the `pk_k` fragment.
+    pub const fn pk_k() -> Self { Type { corr: Correctness::pk_k(), mall: Malleability::pk_k() } }
 
-    fn from_pk_k<Ctx: ScriptContext>() -> Self {
-        Type { corr: Property::from_pk_k::<Ctx>(), mall: Property::from_pk_k::<Ctx>() }
-    }
+    /// Constructor for the type of the `pk_h` fragment.
+    pub const fn pk_h() -> Self { Type { corr: Correctness::pk_h(), mall: Malleability::pk_h() } }
 
-    fn from_pk_h<Ctx: ScriptContext>() -> Self {
-        Type { corr: Property::from_pk_h::<Ctx>(), mall: Property::from_pk_h::<Ctx>() }
+    /// Constructor for the type of the `multi` fragment.
+    pub const fn multi() -> Self {
+        Type { corr: Correctness::multi(), mall: Malleability::multi() }
     }
 
-    fn from_multi(k: usize, n: usize) -> Self {
-        Type { corr: Property::from_multi(k, n), mall: Property::from_multi(k, n) }
+    /// Constructor for the type of the `multi_a` fragment.
+    pub const fn multi_a() -> Self {
+        Type { corr: Correctness::multi_a(), mall: Malleability::multi_a() }
     }
 
-    fn from_multi_a(k: usize, n: usize) -> Self {
-        Type { corr: Property::from_multi_a(k, n), mall: Property::from_multi_a(k, n) }
-    }
+    /// Constructor for the type of all the hash fragments.
+    pub const fn hash() -> Self { Type { corr: Correctness::hash(), mall: Malleability::hash() } }
 
-    fn from_hash() -> Self { Type { corr: Property::from_hash(), mall: Property::from_hash() } }
-
-    fn from_sha256() -> Self {
-        Type { corr: Property::from_sha256(), mall: Property::from_sha256() }
-    }
-
-    fn from_hash256() -> Self {
-        Type { corr: Property::from_hash256(), mall: Property::from_hash256() }
-    }
-
-    fn from_ripemd160() -> Self {
-        Type { corr: Property::from_ripemd160(), mall: Property::from_ripemd160() }
-    }
-
-    fn from_hash160() -> Self {
-        Type { corr: Property::from_hash160(), mall: Property::from_hash160() }
-    }
+    /// Constructor for the type of the `after` and `older` fragments.
+    pub const fn time() -> Self { Type { corr: Correctness::time(), mall: Malleability::time() } }
 
-    fn from_time(t: u32) -> Self {
-        Type { corr: Property::from_time(t), mall: Property::from_time(t) }
-    }
-
-    fn from_after(t: absolute::LockTime) -> Self {
-        Type { corr: Property::from_after(t), mall: Property::from_after(t) }
-    }
-
-    fn from_older(t: Sequence) -> Self {
-        Type { corr: Property::from_older(t), mall: Property::from_older(t) }
-    }
-
-    fn cast_alt(self) -> Result<Self, ErrorKind> {
-        Ok(Type { corr: Property::cast_alt(self.corr)?, mall: Property::cast_alt(self.mall)? })
-    }
-
-    fn cast_swap(self) -> Result<Self, ErrorKind> {
-        Ok(Type { corr: Property::cast_swap(self.corr)?, mall: Property::cast_swap(self.mall)? })
+    /// Constructor for the type of the `a:` fragment.
+    pub const fn cast_alt(self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
+        Ok(Type {
+            corr: match Correctness::cast_alt(self.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::cast_alt(self.mall),
+        })
     }
 
-    fn cast_check(self) -> Result<Self, ErrorKind> {
-        Ok(Type { corr: Property::cast_check(self.corr)?, mall: Property::cast_check(self.mall)? })
+    /// Constructor for the type of the `s:` fragment.
+    pub const fn cast_swap(self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
+        Ok(Type {
+            corr: match Correctness::cast_swap(self.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::cast_swap(self.mall),
+        })
     }
 
-    fn cast_dupif(self) -> Result<Self, ErrorKind> {
-        Ok(Type { corr: Property::cast_dupif(self.corr)?, mall: Property::cast_dupif(self.mall)? })
+    /// Constructor for the type of the `c:` fragment.
+    pub const fn cast_check(self) -> Result<Self, ErrorKind> {
+        Ok(Type {
+            corr: match Correctness::cast_check(self.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::cast_check(self.mall),
+        })
     }
 
-    fn cast_verify(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the type of the `d:` fragment.
+    pub const fn cast_dupif(self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
         Ok(Type {
-            corr: Property::cast_verify(self.corr)?,
-            mall: Property::cast_verify(self.mall)?,
+            corr: match Correctness::cast_dupif(self.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::cast_dupif(self.mall),
         })
     }
 
-    fn cast_nonzero(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the type of the `v:` fragment.
+    pub const fn cast_verify(self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
         Ok(Type {
-            corr: Property::cast_nonzero(self.corr)?,
-            mall: Property::cast_nonzero(self.mall)?,
+            corr: match Correctness::cast_verify(self.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::cast_verify(self.mall),
         })
     }
 
-    fn cast_zeronotequal(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the type of the `j:` fragment.
+    pub const fn cast_nonzero(self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
         Ok(Type {
-            corr: Property::cast_zeronotequal(self.corr)?,
-            mall: Property::cast_zeronotequal(self.mall)?,
+            corr: match Correctness::cast_nonzero(self.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::cast_nonzero(self.mall),
         })
     }
 
-    fn cast_true(self) -> Result<Self, ErrorKind> {
-        Ok(Type { corr: Property::cast_true(self.corr)?, mall: Property::cast_true(self.mall)? })
+    /// Constructor for the type of the `n:` fragment.
+    pub const fn cast_zeronotequal(self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
+        Ok(Type {
+            corr: match Correctness::cast_zeronotequal(self.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::cast_zeronotequal(self.mall),
+        })
     }
 
-    fn cast_or_i_false(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the type of the `t:` fragment.
+    pub const fn cast_true(self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
         Ok(Type {
-            corr: Property::cast_or_i_false(self.corr)?,
-            mall: Property::cast_or_i_false(self.mall)?,
+            corr: match Correctness::cast_true(self.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::cast_true(self.mall),
         })
     }
 
-    fn cast_unlikely(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the type of the `u:` fragment.
+    pub const fn cast_unlikely(self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
         Ok(Type {
-            corr: Property::cast_unlikely(self.corr)?,
-            mall: Property::cast_unlikely(self.mall)?,
+            corr: match Correctness::cast_or_i_false(self.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::cast_or_i_false(self.mall),
         })
     }
 
-    fn cast_likely(self) -> Result<Self, ErrorKind> {
+    /// Constructor for the type of the `l:` fragment.
+    pub const fn cast_likely(self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
         Ok(Type {
-            corr: Property::cast_likely(self.corr)?,
-            mall: Property::cast_likely(self.mall)?,
+            corr: match Correctness::cast_or_i_false(self.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::cast_or_i_false(self.mall),
         })
     }
 
-    fn and_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the type of the `and_b` fragment.
+    pub const fn and_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
         Ok(Type {
-            corr: Property::and_b(left.corr, right.corr)?,
-            mall: Property::and_b(left.mall, right.mall)?,
+            corr: match Correctness::and_b(left.corr, right.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::and_b(left.mall, right.mall),
         })
     }
 
-    fn and_v(left: Self, right: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the type of the `and_v` fragment.
+    pub const fn and_v(left: Self, right: Self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
         Ok(Type {
-            corr: Property::and_v(left.corr, right.corr)?,
-            mall: Property::and_v(left.mall, right.mall)?,
+            corr: match Correctness::and_v(left.corr, right.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::and_v(left.mall, right.mall),
         })
     }
 
-    fn or_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the type of the `or_b` fragment.
+    pub const fn or_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
         Ok(Type {
-            corr: Property::or_b(left.corr, right.corr)?,
-            mall: Property::or_b(left.mall, right.mall)?,
+            corr: match Correctness::or_b(left.corr, right.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::or_b(left.mall, right.mall),
         })
     }
 
-    fn or_d(left: Self, right: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the type of the `or_b` fragment.
+    pub const fn or_d(left: Self, right: Self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
         Ok(Type {
-            corr: Property::or_d(left.corr, right.corr)?,
-            mall: Property::or_d(left.mall, right.mall)?,
+            corr: match Correctness::or_d(left.corr, right.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::or_d(left.mall, right.mall),
         })
     }
 
-    fn or_c(left: Self, right: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the type of the `or_c` fragment.
+    pub const fn or_c(left: Self, right: Self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
         Ok(Type {
-            corr: Property::or_c(left.corr, right.corr)?,
-            mall: Property::or_c(left.mall, right.mall)?,
+            corr: match Correctness::or_c(left.corr, right.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::or_c(left.mall, right.mall),
         })
     }
 
-    fn or_i(left: Self, right: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the type of the `or_i` fragment.
+    pub const fn or_i(left: Self, right: Self) -> Result<Self, ErrorKind> {
         Ok(Type {
-            corr: Property::or_i(left.corr, right.corr)?,
-            mall: Property::or_i(left.mall, right.mall)?,
+            corr: match Correctness::or_i(left.corr, right.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::or_i(left.mall, right.mall),
         })
     }
 
-    fn and_or(a: Self, b: Self, c: Self) -> Result<Self, ErrorKind> {
+    /// Constructor for the type of the `and_or` fragment.
+    pub const fn and_or(a: Self, b: Self, c: Self) -> Result<Self, ErrorKind> {
+        // FIXME need to do manual `?` because ? is not supported in constfns.
         Ok(Type {
-            corr: Property::and_or(a.corr, b.corr, c.corr)?,
-            mall: Property::and_or(a.mall, b.mall, c.mall)?,
+            corr: match Correctness::and_or(a.corr, b.corr, c.corr) {
+                Ok(x) => x,
+                Err(e) => return Err(e),
+            },
+            mall: Malleability::and_or(a.mall, b.mall, c.mall),
         })
     }
 
-    fn threshold<S>(k: usize, n: usize, mut sub_ck: S) -> Result<Self, ErrorKind>
+    /// Constructor for the type of the `thresh` fragment.
+    // Cannot be a constfn because it takes a closure.
+    pub fn threshold<S>(k: usize, n: usize, mut sub_ck: S) -> Result<Self, ErrorKind>
     where
-        S: FnMut(usize) -> Result<Self, ErrorKind>,
+        S: FnMut(usize) -> Self,
     {
         Ok(Type {
-            corr: Property::threshold(k, n, |n| Ok(sub_ck(n)?.corr))?,
-            mall: Property::threshold(k, n, |n| Ok(sub_ck(n)?.mall))?,
+            corr: Correctness::threshold(k, n, |n| sub_ck(n).corr)?,
+            mall: Malleability::threshold(k, n, |n| sub_ck(n).mall),
         })
     }
 }
@@ -547,10 +488,10 @@ impl Type {
         };
 
         let ret = match *fragment {
-            Terminal::True => Ok(Self::from_true()),
-            Terminal::False => Ok(Self::from_false()),
-            Terminal::PkK(..) => Ok(Self::from_pk_k::<Ctx>()),
-            Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::from_pk_h::<Ctx>()),
+            Terminal::True => Ok(Self::TRUE),
+            Terminal::False => Ok(Self::FALSE),
+            Terminal::PkK(..) => Ok(Self::pk_k()),
+            Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::pk_h()),
             Terminal::Multi(k, ref pks) | Terminal::MultiA(k, ref pks) => {
                 if k == 0 {
                     return Err(Error {
@@ -565,8 +506,8 @@ impl Type {
                     });
                 }
                 match *fragment {
-                    Terminal::Multi(..) => Ok(Self::from_multi(k, pks.len())),
-                    Terminal::MultiA(..) => Ok(Self::from_multi_a(k, pks.len())),
+                    Terminal::Multi(..) => Ok(Self::multi()),
+                    Terminal::MultiA(..) => Ok(Self::multi_a()),
                     _ => unreachable!(),
                 }
             }
@@ -580,7 +521,7 @@ impl Type {
                         error: ErrorKind::InvalidTime,
                     });
                 }
-                Ok(Self::from_after(t.into()))
+                Ok(Self::time())
             }
             Terminal::Older(t) => {
                 if t == Sequence::ZERO || !t.is_relative_lock_time() {
@@ -589,12 +530,12 @@ impl Type {
                         error: ErrorKind::InvalidTime,
                     });
                 }
-                Ok(Self::from_older(t))
+                Ok(Self::time())
             }
-            Terminal::Sha256(..) => Ok(Self::from_sha256()),
-            Terminal::Hash256(..) => Ok(Self::from_hash256()),
-            Terminal::Ripemd160(..) => Ok(Self::from_ripemd160()),
-            Terminal::Hash160(..) => Ok(Self::from_hash160()),
+            Terminal::Sha256(..) => Ok(Self::hash()),
+            Terminal::Hash256(..) => Ok(Self::hash()),
+            Terminal::Ripemd160(..) => Ok(Self::hash()),
+            Terminal::Hash160(..) => Ok(Self::hash()),
             Terminal::Alt(ref sub) => wrap_err(Self::cast_alt(sub.ty)),
             Terminal::Swap(ref sub) => wrap_err(Self::cast_swap(sub.ty)),
             Terminal::Check(ref sub) => wrap_err(Self::cast_check(sub.ty)),
@@ -652,7 +593,7 @@ impl Type {
                     });
                 }
 
-                let res = Self::threshold(k, subs.len(), |n| Ok(subs[n].ty));
+                let res = Self::threshold(k, subs.len(), |n| subs[n].ty);
 
                 res.map_err(|kind| Error { fragment_string: fragment.to_string(), error: kind })
             }
diff --git a/src/policy/compiler.rs b/src/policy/compiler.rs
index 1b1ced6e7..c24946d57 100644
--- a/src/policy/compiler.rs
+++ b/src/policy/compiler.rs
@@ -14,7 +14,7 @@ use sync::Arc;
 
 use crate::miniscript::context::SigType;
 use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
-use crate::miniscript::types::{self, ErrorKind, ExtData, Property, Type};
+use crate::miniscript::types::{self, ErrorKind, ExtData, Type};
 use crate::miniscript::ScriptContext;
 use crate::policy::Concrete;
 use crate::prelude::*;
@@ -31,7 +31,7 @@ impl Eq for OrdF64 {}
 // We could derive PartialOrd, but we can't derive Ord, and clippy wants us
 // to derive both or neither. Better to be explicit.
 impl PartialOrd for OrdF64 {
-    fn partial_cmp(&self, other: &OrdF64) -> Option<cmp::Ordering> { self.0.partial_cmp(&other.0) }
+    fn partial_cmp(&self, other: &OrdF64) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
 }
 impl Ord for OrdF64 {
     fn cmp(&self, other: &OrdF64) -> cmp::Ordering {
@@ -145,16 +145,13 @@ struct CompilerExtData {
     dissat_cost: Option<f64>,
 }
 
-impl Property for CompilerExtData {
-    fn from_true() -> Self {
-        CompilerExtData { branch_prob: None, sat_cost: 0.0, dissat_cost: None }
-    }
+impl CompilerExtData {
+    const TRUE: Self = CompilerExtData { branch_prob: None, sat_cost: 0.0, dissat_cost: None };
 
-    fn from_false() -> Self {
-        CompilerExtData { branch_prob: None, sat_cost: f64::MAX, dissat_cost: Some(0.0) }
-    }
+    const FALSE: Self =
+        CompilerExtData { branch_prob: None, sat_cost: f64::MAX, dissat_cost: Some(0.0) };
 
-    fn from_pk_k<Ctx: ScriptContext>() -> Self {
+    fn pk_k<Ctx: ScriptContext>() -> Self {
         CompilerExtData {
             branch_prob: None,
             sat_cost: match Ctx::sig_type() {
@@ -165,7 +162,7 @@ impl Property for CompilerExtData {
         }
     }
 
-    fn from_pk_h<Ctx: ScriptContext>() -> Self {
+    fn pk_h<Ctx: ScriptContext>() -> Self {
         CompilerExtData {
             branch_prob: None,
             sat_cost: match Ctx::sig_type() {
@@ -181,7 +178,7 @@ impl Property for CompilerExtData {
         }
     }
 
-    fn from_multi(k: usize, _n: usize) -> Self {
+    fn multi(k: usize, _n: usize) -> Self {
         CompilerExtData {
             branch_prob: None,
             sat_cost: 1.0 + 73.0 * k as f64,
@@ -189,7 +186,7 @@ impl Property for CompilerExtData {
         }
     }
 
-    fn from_multi_a(k: usize, n: usize) -> Self {
+    fn multi_a(k: usize, n: usize) -> Self {
         CompilerExtData {
             branch_prob: None,
             sat_cost: 66.0 * k as f64 + (n - k) as f64,
@@ -197,158 +194,139 @@ impl Property for CompilerExtData {
         }
     }
 
-    fn from_hash() -> Self {
+    fn hash() -> Self {
         CompilerExtData { branch_prob: None, sat_cost: 33.0, dissat_cost: Some(33.0) }
     }
 
-    fn from_time(_t: u32) -> Self {
-        CompilerExtData { branch_prob: None, sat_cost: 0.0, dissat_cost: None }
-    }
+    fn time() -> Self { CompilerExtData { branch_prob: None, sat_cost: 0.0, dissat_cost: None } }
 
-    fn cast_alt(self) -> Result<Self, types::ErrorKind> {
-        Ok(CompilerExtData {
+    fn cast_alt(self) -> Self {
+        CompilerExtData {
             branch_prob: None,
             sat_cost: self.sat_cost,
             dissat_cost: self.dissat_cost,
-        })
+        }
     }
 
-    fn cast_swap(self) -> Result<Self, types::ErrorKind> {
-        Ok(CompilerExtData {
+    fn cast_swap(self) -> Self {
+        CompilerExtData {
             branch_prob: None,
             sat_cost: self.sat_cost,
             dissat_cost: self.dissat_cost,
-        })
+        }
     }
 
-    fn cast_check(self) -> Result<Self, types::ErrorKind> {
-        Ok(CompilerExtData {
+    fn cast_check(self) -> Self {
+        CompilerExtData {
             branch_prob: None,
             sat_cost: self.sat_cost,
             dissat_cost: self.dissat_cost,
-        })
+        }
     }
 
-    fn cast_dupif(self) -> Result<Self, types::ErrorKind> {
-        Ok(CompilerExtData {
-            branch_prob: None,
-            sat_cost: 2.0 + self.sat_cost,
-            dissat_cost: Some(1.0),
-        })
+    fn cast_dupif(self) -> Self {
+        CompilerExtData { branch_prob: None, sat_cost: 2.0 + self.sat_cost, dissat_cost: Some(1.0) }
     }
 
-    fn cast_verify(self) -> Result<Self, types::ErrorKind> {
-        Ok(CompilerExtData { branch_prob: None, sat_cost: self.sat_cost, dissat_cost: None })
+    fn cast_verify(self) -> Self {
+        CompilerExtData { branch_prob: None, sat_cost: self.sat_cost, dissat_cost: None }
     }
 
-    fn cast_nonzero(self) -> Result<Self, types::ErrorKind> {
-        Ok(CompilerExtData { branch_prob: None, sat_cost: self.sat_cost, dissat_cost: Some(1.0) })
+    fn cast_nonzero(self) -> Self {
+        CompilerExtData { branch_prob: None, sat_cost: self.sat_cost, dissat_cost: Some(1.0) }
     }
 
-    fn cast_zeronotequal(self) -> Result<Self, types::ErrorKind> {
-        Ok(CompilerExtData {
+    fn cast_zeronotequal(self) -> Self {
+        CompilerExtData {
             branch_prob: None,
             sat_cost: self.sat_cost,
             dissat_cost: self.dissat_cost,
-        })
-    }
-
-    fn cast_true(self) -> Result<Self, types::ErrorKind> {
-        Ok(CompilerExtData { branch_prob: None, sat_cost: self.sat_cost, dissat_cost: None })
+        }
     }
 
-    fn cast_or_i_false(self) -> Result<Self, types::ErrorKind> {
-        // never called directly
-        unreachable!()
+    fn cast_true(self) -> Self {
+        CompilerExtData { branch_prob: None, sat_cost: self.sat_cost, dissat_cost: None }
     }
 
-    fn cast_unlikely(self) -> Result<Self, types::ErrorKind> {
-        Ok(CompilerExtData {
-            branch_prob: None,
-            sat_cost: 2.0 + self.sat_cost,
-            dissat_cost: Some(1.0),
-        })
+    fn cast_unlikely(self) -> Self {
+        CompilerExtData { branch_prob: None, sat_cost: 2.0 + self.sat_cost, dissat_cost: Some(1.0) }
     }
 
-    fn cast_likely(self) -> Result<Self, types::ErrorKind> {
-        Ok(CompilerExtData {
-            branch_prob: None,
-            sat_cost: 1.0 + self.sat_cost,
-            dissat_cost: Some(2.0),
-        })
+    fn cast_likely(self) -> Self {
+        CompilerExtData { branch_prob: None, sat_cost: 1.0 + self.sat_cost, dissat_cost: Some(2.0) }
     }
 
-    fn and_b(left: Self, right: Self) -> Result<Self, types::ErrorKind> {
-        Ok(CompilerExtData {
+    fn and_b(left: Self, right: Self) -> Self {
+        CompilerExtData {
             branch_prob: None,
             sat_cost: left.sat_cost + right.sat_cost,
             dissat_cost: match (left.dissat_cost, right.dissat_cost) {
                 (Some(l), Some(r)) => Some(l + r),
                 _ => None,
             },
-        })
+        }
     }
 
-    fn and_v(left: Self, right: Self) -> Result<Self, types::ErrorKind> {
-        Ok(CompilerExtData {
+    fn and_v(left: Self, right: Self) -> Self {
+        CompilerExtData {
             branch_prob: None,
             sat_cost: left.sat_cost + right.sat_cost,
             dissat_cost: None,
-        })
+        }
     }
 
-    fn or_b(l: Self, r: Self) -> Result<Self, types::ErrorKind> {
+    fn or_b(l: Self, r: Self) -> Self {
         let lprob = l
             .branch_prob
             .expect("BUG: left branch prob must be set for disjunctions");
         let rprob = r
             .branch_prob
             .expect("BUG: right branch prob must be set for disjunctions");
-        Ok(CompilerExtData {
+        CompilerExtData {
             branch_prob: None,
             sat_cost: lprob * (l.sat_cost + r.dissat_cost.unwrap())
                 + rprob * (r.sat_cost + l.dissat_cost.unwrap()),
             dissat_cost: Some(l.dissat_cost.unwrap() + r.dissat_cost.unwrap()),
-        })
+        }
     }
 
-    fn or_d(l: Self, r: Self) -> Result<Self, types::ErrorKind> {
+    fn or_d(l: Self, r: Self) -> Self {
         let lprob = l
             .branch_prob
             .expect("BUG: left branch prob must be set for disjunctions");
         let rprob = r
             .branch_prob
             .expect("BUG: right branch prob must be set for disjunctions");
-        Ok(CompilerExtData {
+        CompilerExtData {
             branch_prob: None,
             sat_cost: lprob * l.sat_cost + rprob * (r.sat_cost + l.dissat_cost.unwrap()),
             dissat_cost: r.dissat_cost.map(|rd| l.dissat_cost.unwrap() + rd),
-        })
+        }
     }
 
-    fn or_c(l: Self, r: Self) -> Result<Self, types::ErrorKind> {
+    fn or_c(l: Self, r: Self) -> Self {
         let lprob = l
             .branch_prob
             .expect("BUG: left branch prob must be set for disjunctions");
         let rprob = r
             .branch_prob
             .expect("BUG: right branch prob must be set for disjunctions");
-        Ok(CompilerExtData {
+        CompilerExtData {
             branch_prob: None,
             sat_cost: lprob * l.sat_cost + rprob * (r.sat_cost + l.dissat_cost.unwrap()),
             dissat_cost: None,
-        })
+        }
     }
 
     #[allow(clippy::manual_map)] // Complex if/let is better as is.
-    fn or_i(l: Self, r: Self) -> Result<Self, types::ErrorKind> {
+    fn or_i(l: Self, r: Self) -> Self {
         let lprob = l
             .branch_prob
             .expect("BUG: left branch prob must be set for disjunctions");
         let rprob = r
             .branch_prob
             .expect("BUG: right branch prob must be set for disjunctions");
-        Ok(CompilerExtData {
+        CompilerExtData {
             branch_prob: None,
             sat_cost: lprob * (2.0 + l.sat_cost) + rprob * (1.0 + r.sat_cost),
             dissat_cost: if let (Some(ldis), Some(rdis)) = (l.dissat_cost, r.dissat_cost) {
@@ -364,7 +342,7 @@ impl Property for CompilerExtData {
             } else {
                 None
             },
-        })
+        }
     }
 
     fn and_or(a: Self, b: Self, c: Self) -> Result<Self, types::ErrorKind> {
@@ -386,14 +364,6 @@ impl Property for CompilerExtData {
         })
     }
 
-    fn and_n(a: Self, b: Self) -> Result<Self, types::ErrorKind> {
-        Ok(CompilerExtData {
-            branch_prob: None,
-            sat_cost: a.sat_cost + b.sat_cost,
-            dissat_cost: a.dissat_cost,
-        })
-    }
-
     fn threshold<S>(k: usize, n: usize, mut sub_ck: S) -> Result<Self, types::ErrorKind>
     where
         S: FnMut(usize) -> Result<Self, types::ErrorKind>,
@@ -452,16 +422,11 @@ impl CompilerExtData {
         Pk: MiniscriptKey,
         Ctx: ScriptContext,
     {
-        let wrap_err = |result: Result<Self, ErrorKind>| {
-            result
-                .map_err(|kind| types::Error { fragment_string: fragment.to_string(), error: kind })
-        };
-
-        let ret = match *fragment {
-            Terminal::True => Ok(Self::from_true()),
-            Terminal::False => Ok(Self::from_false()),
-            Terminal::PkK(..) => Ok(Self::from_pk_k::<Ctx>()),
-            Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::from_pk_h::<Ctx>()),
+        match *fragment {
+            Terminal::True => Ok(Self::TRUE),
+            Terminal::False => Ok(Self::FALSE),
+            Terminal::PkK(..) => Ok(Self::pk_k::<Ctx>()),
+            Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::pk_h::<Ctx>()),
             Terminal::Multi(k, ref pks) | Terminal::MultiA(k, ref pks) => {
                 if k == 0 {
                     return Err(types::Error {
@@ -476,8 +441,8 @@ impl CompilerExtData {
                     });
                 }
                 match *fragment {
-                    Terminal::Multi(..) => Ok(Self::from_multi(k, pks.len())),
-                    Terminal::MultiA(..) => Ok(Self::from_multi_a(k, pks.len())),
+                    Terminal::Multi(..) => Ok(Self::multi(k, pks.len())),
+                    Terminal::MultiA(..) => Ok(Self::multi_a(k, pks.len())),
                     _ => unreachable!(),
                 }
             }
@@ -491,7 +456,7 @@ impl CompilerExtData {
                         error: types::ErrorKind::InvalidTime,
                     });
                 }
-                Ok(Self::from_after(t.into()))
+                Ok(Self::time())
             }
             Terminal::Older(t) => {
                 if t == Sequence::ZERO || !t.is_relative_lock_time() {
@@ -500,56 +465,59 @@ impl CompilerExtData {
                         error: types::ErrorKind::InvalidTime,
                     });
                 }
-                Ok(Self::from_older(t))
+                Ok(Self::time())
             }
-            Terminal::Sha256(..) => Ok(Self::from_sha256()),
-            Terminal::Hash256(..) => Ok(Self::from_hash256()),
-            Terminal::Ripemd160(..) => Ok(Self::from_ripemd160()),
-            Terminal::Hash160(..) => Ok(Self::from_hash160()),
-            Terminal::Alt(ref sub) => wrap_err(Self::cast_alt(get_child(&sub.node, 0)?)),
-            Terminal::Swap(ref sub) => wrap_err(Self::cast_swap(get_child(&sub.node, 0)?)),
-            Terminal::Check(ref sub) => wrap_err(Self::cast_check(get_child(&sub.node, 0)?)),
-            Terminal::DupIf(ref sub) => wrap_err(Self::cast_dupif(get_child(&sub.node, 0)?)),
-            Terminal::Verify(ref sub) => wrap_err(Self::cast_verify(get_child(&sub.node, 0)?)),
-            Terminal::NonZero(ref sub) => wrap_err(Self::cast_nonzero(get_child(&sub.node, 0)?)),
+            Terminal::Sha256(..) => Ok(Self::hash()),
+            Terminal::Hash256(..) => Ok(Self::hash()),
+            Terminal::Ripemd160(..) => Ok(Self::hash()),
+            Terminal::Hash160(..) => Ok(Self::hash()),
+            Terminal::Alt(ref sub) => Ok(Self::cast_alt(get_child(&sub.node, 0)?)),
+            Terminal::Swap(ref sub) => Ok(Self::cast_swap(get_child(&sub.node, 0)?)),
+            Terminal::Check(ref sub) => Ok(Self::cast_check(get_child(&sub.node, 0)?)),
+            Terminal::DupIf(ref sub) => Ok(Self::cast_dupif(get_child(&sub.node, 0)?)),
+            Terminal::Verify(ref sub) => Ok(Self::cast_verify(get_child(&sub.node, 0)?)),
+            Terminal::NonZero(ref sub) => Ok(Self::cast_nonzero(get_child(&sub.node, 0)?)),
             Terminal::ZeroNotEqual(ref sub) => {
-                wrap_err(Self::cast_zeronotequal(get_child(&sub.node, 0)?))
+                Ok(Self::cast_zeronotequal(get_child(&sub.node, 0)?))
             }
             Terminal::AndB(ref l, ref r) => {
                 let ltype = get_child(&l.node, 0)?;
                 let rtype = get_child(&r.node, 1)?;
-                wrap_err(Self::and_b(ltype, rtype))
+                Ok(Self::and_b(ltype, rtype))
             }
             Terminal::AndV(ref l, ref r) => {
                 let ltype = get_child(&l.node, 0)?;
                 let rtype = get_child(&r.node, 1)?;
-                wrap_err(Self::and_v(ltype, rtype))
+                Ok(Self::and_v(ltype, rtype))
             }
             Terminal::OrB(ref l, ref r) => {
                 let ltype = get_child(&l.node, 0)?;
                 let rtype = get_child(&r.node, 1)?;
-                wrap_err(Self::or_b(ltype, rtype))
+                Ok(Self::or_b(ltype, rtype))
             }
             Terminal::OrD(ref l, ref r) => {
                 let ltype = get_child(&l.node, 0)?;
                 let rtype = get_child(&r.node, 1)?;
-                wrap_err(Self::or_d(ltype, rtype))
+                Ok(Self::or_d(ltype, rtype))
             }
             Terminal::OrC(ref l, ref r) => {
                 let ltype = get_child(&l.node, 0)?;
                 let rtype = get_child(&r.node, 1)?;
-                wrap_err(Self::or_c(ltype, rtype))
+                Ok(Self::or_c(ltype, rtype))
             }
             Terminal::OrI(ref l, ref r) => {
                 let ltype = get_child(&l.node, 0)?;
                 let rtype = get_child(&r.node, 1)?;
-                wrap_err(Self::or_i(ltype, rtype))
+                Ok(Self::or_i(ltype, rtype))
             }
             Terminal::AndOr(ref a, ref b, ref c) => {
                 let atype = get_child(&a.node, 0)?;
                 let btype = get_child(&b.node, 1)?;
                 let ctype = get_child(&c.node, 2)?;
-                wrap_err(Self::and_or(atype, btype, ctype))
+                Self::and_or(atype, btype, ctype).map_err(|kind| types::Error {
+                    fragment_string: fragment.to_string(),
+                    error: kind,
+                })
             }
             Terminal::Thresh(k, ref subs) => {
                 if k == 0 {
@@ -566,24 +534,16 @@ impl CompilerExtData {
                 }
 
                 let mut last_err_frag = None;
-                let res = Self::threshold(k, subs.len(), |n| match get_child(&subs[n].node, n) {
+                Self::threshold(k, subs.len(), |n| match get_child(&subs[n].node, n) {
                     Ok(x) => Ok(x),
                     Err(e) => {
                         last_err_frag = Some(e.fragment_string);
                         Err(e.error)
                     }
-                });
-
-                res.map_err(|kind| types::Error {
-                    fragment_string: last_err_frag.unwrap_or_else(|| fragment.to_string()),
-                    error: kind,
                 })
+                .map_err(|kind| types::Error { fragment_string: fragment.to_string(), error: kind })
             }
-        };
-        if let Ok(ref ret) = ret {
-            ret.sanity_checks()
         }
-        ret
     }
 }
 
@@ -671,8 +631,8 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> AstElemExt<Pk, Ctx> {
 struct Cast<Pk: MiniscriptKey, Ctx: ScriptContext> {
     node: fn(Arc<Miniscript<Pk, Ctx>>) -> Terminal<Pk, Ctx>,
     ast_type: fn(types::Type) -> Result<types::Type, ErrorKind>,
-    ext_data: fn(types::ExtData) -> Result<types::ExtData, ErrorKind>,
-    comp_ext_data: fn(CompilerExtData) -> Result<CompilerExtData, types::ErrorKind>,
+    ext_data: fn(types::ExtData) -> types::ExtData,
+    comp_ext_data: fn(CompilerExtData) -> CompilerExtData,
 }
 
 impl<Pk: MiniscriptKey, Ctx: ScriptContext> Cast<Pk, Ctx> {
@@ -681,9 +641,9 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Cast<Pk, Ctx> {
             ms: Arc::new(Miniscript::from_components_unchecked(
                 (self.node)(Arc::clone(&ast.ms)),
                 (self.ast_type)(ast.ms.ty)?,
-                (self.ext_data)(ast.ms.ext)?,
+                (self.ext_data)(ast.ms.ext),
             )),
-            comp_ext_data: (self.comp_ext_data)(ast.comp_ext_data)?,
+            comp_ext_data: (self.comp_ext_data)(ast.comp_ext_data),
         })
     }
 }
@@ -953,11 +913,7 @@ where
             compile_binary!(&mut right, &mut left, [1.0, 1.0], Terminal::AndV);
             let mut zero_comp = BTreeMap::new();
             zero_comp.insert(
-                CompilationKey::from_type(
-                    Type::from_false(),
-                    ExtData::from_false().has_free_verify,
-                    dissat_prob,
-                ),
+                CompilationKey::from_type(Type::FALSE, ExtData::FALSE.has_free_verify, dissat_prob),
                 AstElemExt::terminal(Terminal::False),
             );
             compile_tern!(&mut left, &mut q_zero_right, &mut zero_comp, [1.0, 0.0]);
diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs
index 5e235bb4b..bfb40a85f 100644
--- a/src/policy/concrete.rs
+++ b/src/policy/concrete.rs
@@ -29,7 +29,7 @@ use crate::prelude::*;
 use crate::sync::Arc;
 #[cfg(all(doc, not(feature = "compiler")))]
 use crate::Descriptor;
-use crate::{errstr, AbsLockTime, Error, ForEachKey, MiniscriptKey, Translator};
+use crate::{errstr, AbsLockTime, Error, ForEachKey, FromStrKey, MiniscriptKey, Translator};
 
 /// Maximum TapLeafs allowed in a compiled TapTree
 #[cfg(feature = "compiler")]
@@ -930,7 +930,7 @@ impl<Pk: MiniscriptKey> fmt::Display for Policy<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> str::FromStr for Policy<Pk> {
+impl<Pk: FromStrKey> str::FromStr for Policy<Pk> {
     type Err = Error;
     fn from_str(s: &str) -> Result<Policy<Pk>, Error> {
         expression::check_valid_chars(s)?;
@@ -945,7 +945,7 @@ impl<Pk: crate::FromStrKey> str::FromStr for Policy<Pk> {
 serde_string_impl_pk!(Policy, "a miniscript concrete policy");
 
 #[rustfmt::skip]
-impl<Pk: crate::FromStrKey> Policy<Pk> {
+impl<Pk: FromStrKey> Policy<Pk> {
     /// Helper function for `from_tree` to parse subexpressions with
     /// names of the form x@y
     fn from_tree_prob(top: &expression::Tree, allow_prob: bool,)
@@ -1050,7 +1050,7 @@ impl<Pk: crate::FromStrKey> Policy<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> expression::FromTree for Policy<Pk> {
+impl<Pk: FromStrKey> expression::FromTree for Policy<Pk> {
     fn from_tree(top: &expression::Tree) -> Result<Policy<Pk>, Error> {
         Policy::from_tree_prob(top, false).map(|(_, result)| result)
     }
@@ -1093,7 +1093,7 @@ fn with_huffman_tree<Pk: MiniscriptKey>(
 /// any one of the conditions exclusively.
 #[cfg(feature = "compiler")]
 fn generate_combination<Pk: MiniscriptKey>(
-    policy_vec: &Vec<Arc<Policy<Pk>>>,
+    policy_vec: &[Arc<Policy<Pk>>],
     prob: f64,
     k: usize,
 ) -> Vec<(f64, Arc<Policy<Pk>>)> {
diff --git a/src/policy/semantic.rs b/src/policy/semantic.rs
index 6bed0f710..8b00d0bd8 100644
--- a/src/policy/semantic.rs
+++ b/src/policy/semantic.rs
@@ -15,7 +15,9 @@ use super::ENTAILMENT_MAX_TERMINALS;
 use crate::iter::{Tree, TreeLike};
 use crate::prelude::*;
 use crate::sync::Arc;
-use crate::{errstr, expression, AbsLockTime, Error, ForEachKey, MiniscriptKey, Translator};
+use crate::{
+    errstr, expression, AbsLockTime, Error, ForEachKey, FromStrKey, MiniscriptKey, Translator,
+};
 
 /// Abstract policy which corresponds to the semantics of a miniscript and
 /// which allows complex forms of analysis, e.g. filtering and normalization.
@@ -308,7 +310,7 @@ impl<Pk: MiniscriptKey> fmt::Display for Policy<Pk> {
     }
 }
 
-impl<Pk: crate::FromStrKey> str::FromStr for Policy<Pk> {
+impl<Pk: FromStrKey> str::FromStr for Policy<Pk> {
     type Err = Error;
     fn from_str(s: &str) -> Result<Policy<Pk>, Error> {
         expression::check_valid_chars(s)?;
@@ -320,7 +322,7 @@ impl<Pk: crate::FromStrKey> str::FromStr for Policy<Pk> {
 
 serde_string_impl_pk!(Policy, "a miniscript semantic policy");
 
-impl<Pk: crate::FromStrKey> expression::FromTree for Policy<Pk> {
+impl<Pk: FromStrKey> expression::FromTree for Policy<Pk> {
     fn from_tree(top: &expression::Tree) -> Result<Policy<Pk>, Error> {
         match (top.name, top.args.len()) {
             ("UNSATISFIABLE", 0) => Ok(Policy::Unsatisfiable),