Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
387dbbc
First iteration of named instances
rodrigogribeiro Mar 12, 2026
935858d
Adding test cases for named instances
rodrigogribeiro Mar 12, 2026
c3c3df0
Ormulu formating.
rodrigogribeiro Mar 12, 2026
a0ebdc6
Fix named instance receiver dispatch
Y-Nak Apr 11, 2026
814bca5
Allow named labels for distinct instance heads
Y-Nak Apr 12, 2026
a6d4d7f
Support class-qualified named calls
Y-Nak Apr 12, 2026
ffcd28f
Add shared-label parameter disambiguation test
Y-Nak Apr 13, 2026
e60346a
Add named-instance ambiguity regression tests
Y-Nak Apr 13, 2026
51d0612
Fix ormolu formatting
Y-Nak Apr 14, 2026
e9a4fb3
Fix named instance rebase integration
Y-Nak Apr 29, 2026
8196806
Treat named instances as declarations
Y-Nak Apr 30, 2026
1d56ee6
Keep named instances explicit in specialisation
Y-Nak Apr 30, 2026
e4d28af
Allow named evidence on constrained calls
Y-Nak May 1, 2026
393c6c0
Support explicit instance slots
Y-Nak May 1, 2026
c39620a
format
Y-Nak May 2, 2026
d251860
Fix dev shell evmone path on Darwin
Y-Nak May 3, 2026
4a53384
Qualify class stub type references
Y-Nak May 4, 2026
439c768
Prefer named evidence for constrained calls
Y-Nak May 5, 2026
0ac91e4
Update src/Solcore/Frontend/Parser/SolcoreParser.y
Y-Nak May 8, 2026
097c1ee
Address named instance review feedback
Y-Nak May 8, 2026
8ffcafa
Merge upstream/main into named-instances
Y-Nak May 8, 2026
58b0b35
Prefer exact named instance slot matches
Y-Nak May 19, 2026
0d9c00b
Merge branch 'main' into named-instances
Y-Nak May 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
});
texlive = pkgs.texlive.combine { inherit (pkgs.texlive) scheme-small thmtools pdfsync lkproof cm-super; };
evmone-lib = pkgs.callPackage ./nix/evmone.nix { };
evmone-shared-lib =
if pkgs.stdenv.hostPlatform.isDarwin
then "${evmone-lib}/lib/libevmone.dylib"
else "${evmone-lib}/lib/libevmone.so";

testrunner = pkgs.stdenv.mkDerivation {
pname = "testrunner";
Expand Down Expand Up @@ -147,7 +151,7 @@
(pkgs.callPackage ./nix/goevmlab.nix { src = inputs.goevmlab; })
pkgs.mdbook
];
evmone="${evmone-lib}/lib/${if pkgs.stdenv.isDarwin then "libevmone.dylib" else "libevmone.so"}";
evmone = evmone-shared-lib;
};
}
);
Expand Down
180 changes: 164 additions & 16 deletions src/Solcore/Backend/Specialise.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ import Control.Monad
import Control.Monad.Except
import Control.Monad.State
import Data.Generics
import Data.List (intercalate, union, (\\))
import Data.List (find, intercalate, union, (\\))
import Data.Map qualified as Map
import Data.Maybe (fromMaybe)
import Data.Maybe (fromMaybe, isJust)
import Solcore.Backend.Mast
import Solcore.Desugarer.IfDesugarer (desugaredBoolTy)
import Solcore.Frontend.Pretty.ShortName
import Solcore.Frontend.Pretty.SolcorePretty
import Solcore.Frontend.Syntax hiding (decls, name)
import Solcore.Frontend.TypeInference.Id (Id (..))
import Solcore.Frontend.TypeInference.NameSupply
import Solcore.Frontend.TypeInference.TcEnv (TcEnv (typeTable), TypeInfo (..))
import Solcore.Frontend.TypeInference.TcEnv (TcEnv (ctx, namedInstEnv, typeTable), TypeInfo (..))
import Solcore.Frontend.TypeInference.TcUnify (typesDoNotUnify)
import Solcore.Primitives.Primitives

Expand All @@ -46,6 +46,7 @@ data SpecState = SpecState
spGlobalEnv :: TcEnv,
splocalEnv :: Table Ty,
spSubst :: TVSubst,
spNamedEvidence :: [(ImplArg, Instance Name)],
spDebug :: Bool,
spNS :: NameSupply
}
Expand Down Expand Up @@ -91,6 +92,7 @@ initSpecState debugp env =
spGlobalEnv = env,
splocalEnv = emptyTable,
spSubst = emptyTVSubst,
spNamedEvidence = [],
spDebug = debugp,
spNS = namePool
}
Expand Down Expand Up @@ -183,6 +185,23 @@ extSpSubst subst = modify $ \s -> s {spSubst = spSubst s <> subst}
atCurrentSubst :: (HasTV a) => a -> SM a
atCurrentSubst a = flip applytv a <$> getSpSubst

withNamedEvidence :: ImplArg -> SM a -> SM a
withNamedEvidence implArg action = do
env <- gets spGlobalEnv
case Map.lookup (implArgName implArg) (namedInstEnv env) of
Nothing -> action
Just inst -> do
saved <- gets spNamedEvidence
modify $ \s -> s {spNamedEvidence = (implArg, inst) : saved}
result <- action
modify $ \s -> s {spNamedEvidence = saved}
pure result

withNamedEvidences :: [ImplArg] -> SM a -> SM a
withNamedEvidences [] action = action
withNamedEvidences (implArg : implArgs) action =
withNamedEvidence implArg (withNamedEvidences implArgs action)

addData :: DataTy -> SM ()
addData dt = modify (\s -> s {spDataTable = Map.insert (dataName dt) dt (spDataTable s)})

Expand Down Expand Up @@ -218,7 +237,32 @@ addDeclResolutions (TMutualDef decls) = forM_ decls addDeclResolutions
addDeclResolutions _ = return ()

addInstResolutions :: Instance Id -> SM ()
addInstResolutions inst = forM_ (instFunctions inst) (addMethodResolution (instName inst) (mainTy inst))
addInstResolutions inst = forM_ (instFunctions inst) addMethod
where
addMethod fd = do
case instLabel inst of
Nothing ->
addMethodResolution (instName inst) (mainTy inst) fd
-- Named instances are explicit evidence and should not participate in
-- ordinary method resolution.
Just lbl ->
addNamedInstMethodResolution lbl (mainTy inst) fd

-- Register a named-instance method under QualName lbl methodUnqualName.
-- After type inference, method names are QualName className method; we
-- strip the class qualifier and substitute the instance label.
addNamedInstMethodResolution :: Name -> Ty -> TcFunDef -> SM ()
addNamedInstMethodResolution lbl ty fd = do
let sig = funSignature fd
methUnq = case sigName sig of
QualName _ m -> m
Name s -> s
qname = QualName lbl methUnq
name' = specName qname [ty]
funType = typeOfTcFunDef fd
fd' = FunDef sig {sigName = name'} (funDefBody fd)
addResolution qname funType fd'
debug ["+ addNamedInstMethodResolution: ", show qname, " / ", show name', " : ", pretty funType]

specialiseTopDecl :: TopDecl Id -> SM [TopDecl Id]
specialiseTopDecl (TContr (Contract name args decls)) = withLocalState do
Expand Down Expand Up @@ -300,14 +344,101 @@ addMethodResolution cname ty fd = do
addResolution qname funType fd'
debug ["+ addMethodResolution: ", show qname, " / ", show name', " : ", pretty funType]

methodNameString :: Name -> String
methodNameString (QualName _ meth) = meth
methodNameString (Name meth) = meth

methodName :: Name -> Name
methodName = Name . methodNameString

instanceHasMethod :: Name -> Instance a -> Bool
instanceHasMethod meth inst =
any ((== meth) . methodName . sigName . funSignature) (instFunctions inst)

namedEvidenceHasMethod :: Name -> Id -> SM Bool
namedEvidenceHasMethod lbl i = do
env <- gets spGlobalEnv
pure $
case Map.lookup lbl (namedInstEnv env) of
Nothing -> False
Just inst ->
case idName i of
QualName cls _ -> cls == instName inst && hasMethod inst
Name _ -> hasMethod inst
where
hasMethod = instanceHasMethod (methodName (idName i))

explicitNamedEvidenceForCall :: Id -> [TcExp] -> Ty -> SM (Maybe Name)
explicitNamedEvidenceForCall i args ty =
case idName i of
QualName cls meth -> do
argTypes <- atCurrentSubst (map typeOfTcExp args)
ty' <- atCurrentSubst ty
let funType = foldr (:->) ty' argTypes
methName = Name meth
evidences <- gets spNamedEvidence
pure $ implArgName . fst <$> find (matchesNamedEvidence cls methName funType) evidences
Name _ -> pure Nothing

matchesNamedEvidence :: Name -> Name -> Ty -> (ImplArg, Instance Name) -> Bool
matchesNamedEvidence cls meth funType (_, inst) =
instName inst == cls
&& instanceHasMethod meth inst
&& maybe False (`methodTypeMatches` funType) (namedInstanceMethodType meth inst)

namedInstanceMethodType :: Name -> Instance Name -> Maybe Ty
namedInstanceMethodType meth inst =
typeOfNamedFunDef <$> find ((== meth) . methodName . sigName . funSignature) (instFunctions inst)

typeOfNamedFunDef :: FunDef Name -> Ty
typeOfNamedFunDef (FunDef sig _) = typeOfNamedSignature sig

typeOfNamedSignature :: Signature Name -> Ty
typeOfNamedSignature sig =
funtype (map typeOfParam (sigParams sig)) returnType
where
returnType = case sigReturn sig of
Just t -> t
Nothing -> error ("no return type in signature of: " ++ show (sigName sig))
typeOfParam (Typed _ t) = t
typeOfParam p = error ("untyped parameter in signature of: " ++ show p)

methodTypeMatches :: Ty -> Ty -> Bool
methodTypeMatches methodTy callTy =
case specmgu methodTy callTy of
Right _ -> True
Left _ -> False

-- | `specExp` specialises an expression to given type
specExp :: TcExp -> Ty -> SM TcExp
specExp (Call Nothing i args) ty = do
specExp (Call Nothing i [] args) ty = do
-- debug ["> specExp (Call): ", pretty e, " : ", pretty (idType i), " ~> ", pretty ty]
(i', args') <- specCall i args ty
let e' = Call Nothing i' args'
mlbl <- explicitNamedEvidenceForCall i args ty
case mlbl of
Just lbl -> specExp (Call Nothing i [ImplArg Nothing lbl] args) ty
Nothing -> do
(i'', args') <- specCall i args ty
let e' = Call Nothing i'' [] args'
-- debug ["< specExp (Call): ", pretty e']
return e'
specExp (Call Nothing i [implArg] args) ty = do
-- A label can either select a named instance method directly, or supply
-- explicit evidence to a constrained function call.
let lbl = implArgName implArg
isNamedMethod <- namedEvidenceHasMethod lbl i
hasFunctionResolution <- callHasResolution i args ty
(i'', args') <-
if isNamedMethod && not hasFunctionResolution
then do
let i' = i {idName = QualName lbl (methodNameString (idName i))}
specCall i' args ty
else specCallWithEvidence [implArg] i args ty
let e' = Call Nothing i'' [] args'
-- debug ["< specExp (Call): ", pretty e']
return e'
specExp (Call Nothing i implArgs args) ty =
specCallWithEvidence implArgs i args ty >>= \(i'', args') ->
pure (Call Nothing i'' [] args')
specExp e@(Con i es) ty = do
debug ["> specConApp: ", pretty e, " : ", pretty (typeOfTcExp e), " ~> ", pretty ty]
(i', es') <- specConApp i es ty
Expand Down Expand Up @@ -347,8 +478,18 @@ specConApp i@(Id _n conTy) args ty = do
-- | Specialise a function call
-- given actual arguments and the expected result type
specCall :: Id -> [TcExp] -> Ty -> SM (Id, [TcExp])
specCall i@(Id (Name "revert") _) args _ = pure (i, args) -- FIXME
specCall i args ty = do
specCall = specCallWithEvidence []

callHasResolution :: Id -> [TcExp] -> Ty -> SM Bool
callHasResolution i args ty = do
i' <- atCurrentSubst i
ty' <- atCurrentSubst ty
argTypes' <- atCurrentSubst (map typeOfTcExp args)
isJust <$> lookupResolution (idName i') (foldr (:->) ty' argTypes')

specCallWithEvidence :: [ImplArg] -> Id -> [TcExp] -> Ty -> SM (Id, [TcExp])
specCallWithEvidence _ i@(Id (Name "revert") _) args _ = pure (i, args) -- FIXME
specCallWithEvidence implArgs i args ty = do
i' <- atCurrentSubst i
ty' <- atCurrentSubst ty
-- debug ["> specCall: ", pretty i', show args, " : ", pretty ty']
Expand All @@ -372,21 +513,28 @@ specCall i args ty = do
prettys (map snd varToVar),
"\n The intermediate type cannot be determined from this call site.",
"\n Expression: ",
pretty (Call Nothing i args),
pretty (Call Nothing i [] args),
"\n This often occurs when a polymorphic-return function (e.g. `require`)",
"\n is passed directly to a polymorphic-argument function (e.g. `void`)."
]
extSpSubst phi
subst <- getSpSubst
let ty'' = applytv subst fty
ensureClosed ty'' (Call Nothing i args) subst
name' <- specFunDef fd
ensureClosed ty'' (Call Nothing i [] args) subst
name' <- withNamedEvidences implArgs (specFunDef fd)
debug ["< specCall: ", pretty name', " : ", show ty'']
args'' <- atCurrentSubst args'
return (Id name' ty'', args'')
Nothing -> do
void $ panics ["! specCall: no resolution found for ", show name, " : ", pretty funType]
return (i, args')
-- Primitives are in primCtx but have no resolution entry; treat as monomorphic.
primEnv <- gets (ctx . spGlobalEnv)
if Map.member name primEnv
then do
debug ["< specCall (primitive): ", show name]
return (i', args')
else do
void $ panics ["! specCall: no resolution found for ", show name, " : ", pretty funType]
return (i, args')

-- | `specFunDef` specialises a function definition
-- to the given type of the form `arg1Ty -> arg2Ty -> ... -> resultTy`
Expand Down Expand Up @@ -587,7 +735,7 @@ typeOfTcExp e@(Con i args) = go (idType i) args
go _ _ = error $ "typeOfTcExp: " ++ show e
typeOfTcExp (Lit (IntLit _)) = word
typeOfTcExp (Lit (StrLit _)) = string
typeOfTcExp expr@(Call Nothing i args) = applyTo args funTy
typeOfTcExp expr@(Call Nothing i _ args) = applyTo args funTy
where
funTy = idType i
applyTo [] ty = ty
Expand Down Expand Up @@ -893,7 +1041,7 @@ toMastExp :: Exp Id -> MastExp
toMastExp (Var i) = MastVar (toMastId i)
toMastExp (Con i es) = MastCon (toMastId i) (map toMastExp es)
toMastExp (Lit l) = MastLit l
toMastExp (Call Nothing i es) = MastCall (toMastId i) (map toMastExp es)
toMastExp (Call Nothing i _ es) = MastCall (toMastId i) (map toMastExp es)
toMastExp (TyExp e _) = toMastExp e
toMastExp (Cond e1 e2 e3) = MastCond (toMastExp e1) (toMastExp e2) (toMastExp e3)
toMastExp e = error $ "toMastExp: unexpected " ++ show e
Expand Down
8 changes: 5 additions & 3 deletions src/Solcore/Desugarer/ContractDispatch.hs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ genMainFn addMain (Contract cname tys cdecls)
cdecls' = Set.unions (map (transformCDecl cname) cdecls'')
defaultConstructor = CConstrDecl (Constructor {constrParams = [], constrBody = []})
mainfn = FunDef (Signature [] [] "main" [] (Just unit)) body
body = [StmtExp (Call Nothing (QualName "RunContract" "exec") [cdata])]
body = [StmtExp (Call Nothing (QualName "RunContract" "exec") [] [cdata])]
cdata = Con "Contract" [methods, fallback]
methods = tupleExpFromList (fmap mkMethod (mapMaybe unwrapSigs cdecls))
fallback =
Expand Down Expand Up @@ -142,6 +142,7 @@ transformConstructor contractName cons
:= Call
Nothing
"abi_decode"
[]
[ Var "source",
proxyExp argsTuple,
proxyExp (TyCon "MemoryWordReader" [])
Expand All @@ -163,10 +164,10 @@ transformConstructor contractName cons
}
startBody =
[ Asm [yulBlock|{ mstore(64, memoryguard(128)) }|],
Let "conargs" (Just argsTuple) (Just (Call Nothing "copy_arguments_for_constructor" [])),
Let "conargs" (Just argsTuple) (Just (Call Nothing "copy_arguments_for_constructor" [] [])),
-- , Match [Var "conargs"] ...
Let "fun" Nothing (Just (Var initFunName)),
StmtExp $ Call Nothing "fun" [Var "conargs"],
StmtExp $ Call Nothing "fun" [] [Var "conargs"],
Asm
[yulBlock|{
let size := datasize(`yulContractName`)
Expand Down Expand Up @@ -195,6 +196,7 @@ mkNameInst (DataTy dname [] []) fname =
body = [Return (Lit (StrLit (show fname)))]
in Instance
{ instDefault = False,
instLabel = Nothing,
instVars = [],
instContext = [],
instName = "SigString",
Expand Down
10 changes: 5 additions & 5 deletions src/Solcore/Desugarer/DecisionTreeCompiler.hs
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,8 @@ instance Compile (Exp Id) where
flip FieldAccess n <$> compile me
compile l@(Lit _) =
pure l
compile (Call me f es) =
Call <$> compile me <*> pure f <*> compile es
compile (Call me f lbl es) =
Call <$> compile me <*> pure f <*> pure lbl <*> compile es
compile (Lam ps bd mt) =
Lam ps <$> pushCtx ("lambda (" ++ intercalate ", " (map pretty ps) ++ ")") (compile bd) <*> pure mt
compile (TyExp e t) =
Expand All @@ -287,8 +287,8 @@ instance Compile (Exp Id) where
Indexed <$> compile e1 <*> compile e2

instance Compile (Instance Id) where
compile (Instance d vs ps n ts t funs) =
Instance d vs ps n ts t
compile (Instance d lbl vs ps n ts t funs) =
Instance d lbl vs ps n ts t
<$> pushCtx ("instance " ++ pretty t ++ " : " ++ pretty n) (compile funs)

-- compiling a decision tree into a match
Expand Down Expand Up @@ -656,7 +656,7 @@ scrutineeType (Var i) = pure (idType i)
scrutineeType (Con i _) = pure (snd (splitTy (idType i)))
scrutineeType (Lit (IntLit _)) = pure word
scrutineeType (Lit (StrLit _)) = pure string
scrutineeType (Call _ i _) = pure (snd (splitTy (idType i)))
scrutineeType (Call _ i _ _) = pure (snd (splitTy (idType i)))
scrutineeType (Lam args _body (Just tb)) = pure (funtype (map typeOfParam args) tb)
scrutineeType (Lam _ _ Nothing) =
throwError
Expand Down
Loading
Loading