[nem-bug] [Nemerle 0000980]: compiler erroneously triggers InErrorMode flag (after returning None from DelayMacro callback)

feedback at nemerle.org feedback at nemerle.org
Tue Mar 27 00:07:20 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:              03-27-2007 00:07 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:07 
----------------------------------------------------------------------
It seems like idea behind a code above was a lost cause. It's so much
easier to generate classes like this:

[Record] public class FooBar[A,B,C] : 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 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.

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                          
======================================================================




More information about the bugs mailing list