New fixes in type inference / specialization for ABI encoding library#464
New fixes in type inference / specialization for ABI encoding library#464rodrigogribeiro wants to merge 8 commits into
Conversation
mbenke
left a comment
There was a problem hiding this comment.
$ cabal run sol-core -- -f test/examples/cases/bug-mptc-template-a.solc
PANIC: ! specCall: no resolution found for Encoder.encode : Foo -> word -> word -> word
// Bug: tryResolveMPTC Template A wrongly binds `rep` when a class method takes
// more than one argument.
//
// Template A matches storedTy against (mainTy :-> freshV).
// For Encoder.encode specialised at Foo, storedTy = Foo -> word -> word.
// specmgu (Foo -> word -> word) (Foo -> freshV) succeeds with freshV = word -> word
// because `word -> word` is a fully-concrete type (no free vars), the guard
// `null (freetv concrete)` passes, and rep gets bound to word -> word instead
// of word.
//
// The phantom variable `rep` has no return-type context here (encodeAndDiscard
// returns ()) so it cannot be resolved from the call site -- tryResolveMPTC is
// the only mechanism that fires, and it produces the wrong answer.
//
// Expected (main, without tryResolveMPTC): clear "unresolved type variable" error.
// Actual (PR branch, with tryResolveMPTC): panic -- no resolution for
// Encoder.encode : Foo -> word -> word -> word (wrong 4-arg type from rep=word->word)
forall self rep.
class self:Encoder(rep) {
function encode(x:self, hint:word) -> rep;
}
data Foo = Foo(word);
instance Foo:Encoder(word) {
function encode(x:Foo, hint:word) -> word {
match x { | Foo(w) => return w; }
}
}
forall a rep . a:Encoder(rep) =>
function encodeAndDiscard(x:a) -> () {
let enc : rep = Encoder.encode(x, 0);
return ();
}
contract C {
public function main() -> word {
encodeAndDiscard(Foo(42));
return 0;
}
}
|
Another regression: this seems to compile fine on main, but here fails with My understanding is that |
|
Some tests would be nice too :) |
| Right phi -> do | ||
| let concrete = applytv phi freshV | ||
| when (null (freetv concrete)) $ | ||
| forM_ extras $ \extra -> | ||
| case specmgu extra concrete of | ||
| Left _ -> return () | ||
| Right phi2 -> extSpSubst phi2 | ||
|
|
There was a problem hiding this comment.
| Right phi -> do | |
| let concrete = applytv phi freshV | |
| when (null (freetv concrete)) $ | |
| forM_ extras $ \extra -> | |
| case specmgu extra concrete of | |
| Left _ -> return () | |
| Right phi2 -> extSpSubst phi2 | |
| Right phi -> do | |
| let concrete = applytv phi freshV | |
| -- A representation type is never a function type. An arrow here means we | |
| -- matched a multi-argument method (e.g. self -> word -> rep) against the | |
| -- unary template (self -> freshV) and captured the trailing argument | |
| -- types by mistake; reject so the phantom var stays unresolved and the | |
| -- caller reports it cleanly instead of panicking on a bogus arity later. | |
| when (null (freetv concrete) && not (isArrow concrete)) $ | |
| forM_ extras $ \extra -> | |
| case specmgu extra concrete of | |
| Left _ -> return () | |
| Right phi2 -> extSpSubst phi2 | |
| where | |
| isArrow (_ :-> _) = True | |
| isArrow _ = False |
| -- predicates, not in the function type). sig2's context uses annotation | ||
| -- TyVars (e.g. "rep"), but the body uses body TyVars (e.g. "$106550"). | ||
| -- Build a TyVar-level renaming from the delta and patch sig2's context. | ||
| phantomDelta <- findPhantomPredBindings pann pinf |
There was a problem hiding this comment.
compute the set of metavariables that occur in the function type (funMetas = mv tinf' ∪ mv tann') and exclude them from delta, keeping only genuinely phantom variables (present in predicates, absent from the function type) - also requires changing findPhantomPredBindings
| phantomDelta <- findPhantomPredBindings pann pinf | |
| let funMetas = mv tinf' `union` mv tann' | |
| phantomDelta <- findPhantomPredBindings funMetas pann pinf |
| findPhantomPredBindings :: [Pred] -> [Pred] -> TcM [(MetaTv, Ty)] | ||
| findPhantomPredBindings pann pinf = do | ||
| s0 <- getSubst | ||
| let pann' = apply s0 (everywhere (mkT toMeta) pann) | ||
| pinf' = everywhere (mkT toMeta) pinf | ||
| dom_s0 = map fst (unSubst s0) | ||
| forM_ (phantomMatchingPreds dom_s0 pann' pinf') $ \(pa, pi_) -> | ||
| catchError (unifyPredExtras pa pi_) (\_ -> return ()) | ||
| s_full <- getSubst | ||
| let delta = filter (\(v, _) -> v `notElem` dom_s0) (unSubst s_full) |
There was a problem hiding this comment.
| findPhantomPredBindings :: [Pred] -> [Pred] -> TcM [(MetaTv, Ty)] | |
| findPhantomPredBindings pann pinf = do | |
| s0 <- getSubst | |
| let pann' = apply s0 (everywhere (mkT toMeta) pann) | |
| pinf' = everywhere (mkT toMeta) pinf | |
| dom_s0 = map fst (unSubst s0) | |
| forM_ (phantomMatchingPreds dom_s0 pann' pinf') $ \(pa, pi_) -> | |
| catchError (unifyPredExtras pa pi_) (\_ -> return ()) | |
| s_full <- getSubst | |
| let delta = filter (\(v, _) -> v `notElem` dom_s0) (unSubst s_full) | |
| findPhantomPredBindings :: [MetaTv] -> [Pred] -> [Pred] -> TcM [(MetaTv, Ty)] | |
| findPhantomPredBindings funMetas pann pinf = do | |
| s0 <- getSubst | |
| let pann' = apply s0 (everywhere (mkT toMeta) pann) | |
| pinf' = everywhere (mkT toMeta) pinf | |
| dom_s0 = map fst (unSubst s0) | |
| forM_ (phantomMatchingPreds dom_s0 pann' pinf') $ \(pa, pi_) -> | |
| catchError (unifyPredExtras pa pi_) (\_ -> return ()) | |
| s_full <- getSubst | |
| -- Keep only bindings for genuinely phantom vars: newly introduced (not in | |
| -- dom_s0) AND absent from the function type (funMetas). This drops stray | |
| -- bindings such as `a := b` produced by cross-product pairing of two | |
| -- same-class constraints, which would otherwise corrupt the signature. | |
| let delta = filter (\(v, _) -> v `notElem` dom_s0 && v `notElem` funMetas) (unSubst s_full) |
|
Added suggested fixes for the two issues mentioned in the review yesterday. |
…e-fixes' into new-infer-specialize-fixes
|
We need some real tests - currently all test pass even when |
No description provided.