Compilation directives priority and macro

Hej folks !

Sorry, again with macro misundertanding… :stuck_out_tongue:
I can’t really understand if it’s normal, when I build for js target I get this error : Class<Logger> has no field _log (Suggestion: log) : Try Haxe !

class Logger {
#if sys
  static function _log(){}
#end
  public static macro function log( exprs : Array<haxe.macro.Expr> ){
#if sys
	return macro @:privateAccess Logger._log();
#else
  	return macro null;
#end
  }
}

Same here : Try Haxe !

class Logger {
#if sys
  static function _log(){}
  public static macro function log( exprs : Array<haxe.macro.Expr> ){
		return macro @:privateAccess Logger._log();
  }
#else
  public static macro function log( exprs : Array<haxe.macro.Expr> ){
  	return macro null;
  }
#end
}

And a last one : Try Haxe ! here no errors, so If I understand well, the “non-macro” function “log” is prior to the macro one right ?..

class Logger {
#if sys
  static function _log(){}
  public static macro function log( exprs : Array<haxe.macro.Expr> ){
		return macro @:privateAccess Logger._log();
  }
#else
  public static function log( ?a ){}
#end
}

Can anybody explain me please what’s going on the 2 firsts examples ? Compiler doesn’t take care of # directives, but I thought it was prior to anything.
So the only way to “separate” between targets is to play with Context.defined( "sys" ) ? : Try Haxe !

class Logger {
#if sys
  static function _log(){}
#end
  public static macro function log( exprs : Array<haxe.macro.Expr> ){
    if( haxe.macro.Context.defined( "sys" ) ){
		return macro @:privateAccess Logger._log();
    }else{
		return macro null;
    }
  }
}

And I imagine it’s slower than with # ?..

And at the end, how would be the best way to have a function that on sys targets do something and no-sys, just be “bypassed” by compiler (best without even be treated at macro time…) ?

The macro context always has “sys” defined as true, regardless of whether the final target you are compiling to is a sys target (which javascript is not). So the macro code generates the call to the _log function, but compilation later fails because Javascript does not have this function defined.

The last example is probably what you want to have, where you are checking if specifically the final (non-macro) context for has “sys” defined. You are not trying to see if the macro context has “sys” features, but rather if the final javascript context once all macros are run has them. Also, it shouldn’t really be slower to use haxe.macro.Context.defined("sys") in a macro, because both that and the #if block happen at compile time and call on the same functionality in the compiler.

Thanks for your answer @tobil4sk

I’m happy to read that both # directive and Context.defined() call the same functionality, I was thinking directives are something “lighter” I don’t know how to say… anyway.
And yes that was what I was also thinking that macro context defined “sys”, it’s ok.

What about something like that ? Try Haxe !

class Logger {
#if sys
  public static macro function log( exprs : Array<haxe.macro.Expr> ){
		return macro trace( 1 );
  }
  public static macro function log( exprs : Array<haxe.macro.Expr> ){
  	return macro trace( 2 );
  }
#end
}

I we remove the directives, the compiler sees that this is duplicated, not here.

And I’m sorry, I put stupid examples sometimes because I try things and want to understand well how does it work…

In this case you would probably want to decide whether to generate “trace( 1 );” or “trace( 2 );” within a single macro function and use an if statement to branch between the two, as you did in the last example of your previous post. There you have no way of telling the compiler which log you actually want, as they are both defined in the same context with the same name.

Yes my last example is not linkd to the others, it just seems to me like a kind of bug because if your remove just the directives, the compiler complains that log is duplicated field, but with the directives, it give the error : Class<Logger> has no field log instead of telling Duplicate class field declaration : Logger.log, strange isn’t it ?

With the directives there are never any duplicate fields, because in the macro context it only uses the one defined in the #if sys block, and in the Javascript context only the one in the #else block, so never both at once.

@tobil4sk I mean my last example, there is only one #if sys, so it should throw “Duplicate fields” no ? :

class Logger {
#if sys
  public static macro function log( exprs : Array<haxe.macro.Expr> ){
		return macro trace( 1 );
  }
  public static macro function log( exprs : Array<haxe.macro.Expr> ){
  	return macro trace( 2 );
  }
#end
}

Or maybe I don’t understand what you are talking about … ?

Ah yes sorry I misunderstood what you were saying, that is weird. It would seem that now the “sys” block is completely preventing either of them from being recognised, even if there is only one… So maybe the diagnosis was wrong.

This seems to fail, which doesn’t make sense with the theory I gave.

class Logger {
#if sys
  static function _log(){}
  public static macro function log( exprs : Array<haxe.macro.Expr> ){
		return macro @:privateAccess Logger._log();
  }
#else
#end
}

Perhaps the #if sys block should not be around the “macro” function?

So if I understand well, sys is defined inside the macro function and here we must use ̀Context.defined()` to get the real output target, and outside the macro function, the compilation directive just follow the output target right ?
I don’t know if my explanation is understandable…

Yes, Context.defined() gives values for the actual target (so Javascript in this case).

As I understand it, when compiling files for the macro, then #if blocks will use the values for the macro target. However, after this, the #if blocks are evaluated again independently with the values of the “real” target for final compilation. (For reasons like this, it is better to completely separate macro and non-macro code into different files to avoid confusion)

Additionally, it would seem that any macro function has to be defined both in the macro context and the final context, so if we have:

public static macro function log( exprs : Array<haxe.macro.Expr> ) { ... }

this function must be accessible in both the macro and javascript contexts. (so for example, it cannot be behind a #if sys block, where it would only be visible in the macro context)

Yes it’s something like that, but IMHO, functions with “macro” keyword have only to be defined in no-macro context if not they are just not seen and then when we are inside this function it’s macro context that “begins”