[nem-bug] [Nemerle 0000980]: compiler erroneously triggers
InErrorMode flag (after returning None from DelayMacro callback)
feedback at nemerle.org
feedback at nemerle.org
Tue Apr 3 20:44:26 CEST 2007
A NOTE has been added to this issue.
======================================================================
<http://nemerle.org/bugs/view.php?id=980>
======================================================================
Reported By: aleksey
Assigned To:
======================================================================
Project: Nemerle
Issue ID: 980
Category: Compiler (parser/macros engine)
Reproducibility: always
Severity: minor
Priority: normal
Status: new
======================================================================
Date Submitted: 03-26-2007 23:44 CEST
Last Modified: 04-03-2007 20:44 CEST
======================================================================
Summary: compiler erroneously triggers InErrorMode flag
(after returning None from DelayMacro callback)
Description:
Here's the code and it works fine, though compiler executes with
InErrorMode set to true. It's a macro implementing functionality similar
to anonymous types from upcoming C# 3.0. There is a also handful of hacks
and tinkering in it, so I'd be glad to hear any suggestions on improving
the code.
What I'm trying to do here is to play adequately well with Nemerle's type
inference and thus I need to delay code generation till the very moment
when the exact types are known. However when it finally happens it seems
that compiler triggers error mode, though no error message is written and
code compiles perfectly fine. Downside of this is that I cannot use
InErrorMode anymore. Also I wonder whether I'm doing or understanding
anything wrong.
Here is the code(it's not tiny so I also attached zip file with it and a
few tests):
using System
using Nemerle.Compiler
using Nemerle.Collections
using Nemerle.Utility
set namespace Nemerle.Extensions
public abstract class Anonymous[T]
public abstract __Fields : list[string] { get; }
public abstract __Values : T { get; }
public override GetHashCode() : int
__Values.GetHashCode() ^ __Fields.GetHashCode()
public override ToString() : string
$"{ ($(__Fields.ToString(\", \"))) : $__Values }"
module StringFilterExt
public Filter(this str : string, pred : char->bool) : string
def sb = Text.StringBuilder(str.Length)
foreach(c when pred(c) in str)
_ = sb.Append(c)
sb.ToString()
module MTypeExt
public IsNotFullyInfered(this ty : TyVar) : bool
if (ty is MType) match(ty :> MType)
| Tuple(args) | Class(_, args) =>
args.Exists(IsNotFullyInfered(_))
| Fun(a, t) =>
a.IsNotFullyInfered() || t.IsNotFullyInfered()
| Array (t, _) | Ref(t) | Out(t) =>
t.IsNotFullyInfered()
| Intersection(args) =>
args.Exists(IsNotFullyInfered(_))
| Void | TyVarRef => false
| _ => true
else match(ty.Hint)
| Some(mt) => mt.IsNotFullyInfered()
| _ => true
macro CreateAnonymousClass(body) syntax("new", body)
def typer = Macros.ImplicitCTX()
def exprs =
match(body)
| <[{.. $exprs }]> => exprs
| _ => Message.FatalError(body.Location, $"expected new { Name1 =
expr1, ... }; got $body")
def exprs = exprs.Map(
_ => {
| <[ $(field : name) = $value ]> => (field, value)
| expr => Message.FatalError(expr.Location, $"expected Name = expr1,
got $expr")})
def (fields, values) = List.Split(exprs)
def one_or_tuple(exprs)
if (exprs.Length > 1) <[ (.. $exprs) ]> else <[ $(exprs.Head) ]>
def values = one_or_tuple(values)
def tvalues = typer.TypeExpr(values)
def buildAnonymousClass(fail_loud)
match (tvalues.Type.Hint)
| Some(Tuple(args) as t) \
| Some(t) with args = [t] =>
when(t.IsNotFullyInfered())
Console.WriteLine($"not enough type information in $t")
Nemerle.Imperative.Return(None())
def t = t.DeepFix()
Console.WriteLine(t)
def members = List.Combine(fields, args).Map(
(field, typ) =>
<[ decl: [Accessor(flags=WantSetter)] mutable $(field.NewName("_" +
field.Id) : name) : $(typ : typed)]>)
def fields_strings = fields.Map(f => <[ $(f.Id : string) ]>)
def members = <[ decl: public override __Fields : list[string] { get {
[.. $fields_strings] } } ]>\
:: members
def fields_access_exprs = fields.Map(f => <[ $(f : name) ]>)
def members = <[ decl: public override __Values : $(t : typed) { get {
$(one_or_tuple(fields_access_exprs)) } } ]>\
:: members
def mangled_args = t.ToString().Filter(c => Char.IsLetterOrDigit(c) ||
c == '_')
def mangled_name =
$"Anonymous_$(mangled_args)_$(t.ToString().GetHashCode())"
Console.WriteLine(typer.InErrorMode)
unless(typer.Env.LookupType([mangled_name]) is Some(_))
def anonyclass =
<[ decl: [Record] public class $(mangled_name : usesite) :
Anonymous[$(t : typed)] { .. $members }]>
def tbuilder = typer.Env.Define(anonyclass)
// unless(typer.InErrorMode)
tbuilder.Compile()
Some(<[$(mangled_name : usesite)($values)]>)
| _ =>
when(fail_loud)
Message.Error(body.Location, "compiler was unable to analyze types of
expressions inside anonymous type")
None()
typer.DelayMacro(buildAnonymousClass)
======================================================================
----------------------------------------------------------------------
aleksey - 03-27-07 00:13
----------------------------------------------------------------------
Looks like the idea behind a code above was a lost cause. It's so much
easier to generate classes like:
[Record] public class FooBar[A,B] : Anonymous[A*B]
public override __Fields : list[string]
get
["Foo", "Bar"]
public override __Values : A*B
get
(Foo, Bar)
[Accessor(flags=WantSetter)] mutable _Foo : A;
[Accessor(flags=WantSetter)] mutable _Bar : B;
And let the compiler do heavy lifting:
// this code is actually impossible with the previous approach
def x = FooBar([], System.Collections.Generic.Dictionary())
x.Foo = [1,2,3]
x.Bar.Add("crazy", "stuff")
System.Console.WriteLine(x)
The only concern is performance, probably it will be much lower. Perhaps
mixed approach is the best. Where we can't figure the types using
DelayMacro we may just generate the class similar the code above and let
the compiler do the exact typing later.
----------------------------------------------------------------------
nazgul - 04-03-07 20:44
----------------------------------------------------------------------
Hmm, I tried your example and it seems that there is some regression in
compiler, because AnonymousClass.n does not compile. After changing
Tuple(args) into MType.Tuple(args) it compiled fine. Aslo, your simple.n
did compile also, but resulting assembly does not verify:
Unhandled Exception: System.TypeLoadException: Could not load type
'Anonymous_int_-1741290739' from assembly 'out, Version=0.0.0.0,
Culture=neutral, PublicKeyToken=null'.
at simple.Main()
I'm not sure, but it is possible, that verification problem is only
because of negative number in name of type ;)
Issue History
Date Modified Username Field Change
======================================================================
03-26-07 23:44 aleksey New Issue
03-26-07 23:44 aleksey File Added: anonymous_class.zip
03-27-07 00:07 aleksey Note Added: 0001830
03-27-07 00:08 aleksey Note Edited: 0001830
03-27-07 00:09 aleksey Note Edited: 0001830
03-27-07 00:13 aleksey Note Edited: 0001830
04-03-07 20:44 nazgul Note Added: 0001834
======================================================================
More information about the bugs
mailing list