@@ -54,6 +54,7 @@ private enum Phase { LIGHT, HEAVY }
5454 private @ Nullable Element lastElement = null ;
5555 private final HashSet <String > trveWrapperFuncs = new HashSet <>();
5656 private final HashMap <String , HashSet <FunctionCall >> wrapperCalls = new HashMap <>();
57+ private final Map <ClassDef , Map <GlobalVarDef , Integer >> classVarInitOrderCache = new HashMap <>();
5758
5859 public WurstValidator (WurstModel root ) {
5960 this .prog = root ;
@@ -2881,6 +2882,59 @@ private void checkVarDef(VarDef v) {
28812882 v .addError ("Initial value of variable " + v .getName () + " is 'null'. Specify a concrete type." );
28822883 }
28832884
2885+ if (v instanceof GlobalVarDef ) {
2886+ checkClassMemberInitializerOrder ((GlobalVarDef ) v );
2887+ }
2888+
2889+ }
2890+
2891+ private void checkClassMemberInitializerOrder (GlobalVarDef v ) {
2892+ if (!v .attrIsDynamicClassMember ()) {
2893+ return ;
2894+ }
2895+ if (!(v .getInitialExpr () instanceof Expr )) {
2896+ return ;
2897+ }
2898+ ClassDef owner = v .attrNearestClassDef ();
2899+ if (owner == null ) {
2900+ return ;
2901+ }
2902+ Map <GlobalVarDef , Integer > order = classVarInitOrder (owner );
2903+ Integer currentPos = order .get (v );
2904+ if (currentPos == null ) {
2905+ return ;
2906+ }
2907+ Expr initExpr = (Expr ) v .getInitialExpr ();
2908+ for (NameDef used : initExpr .attrReadVariables ()) {
2909+ if (!(used instanceof GlobalVarDef )) {
2910+ continue ;
2911+ }
2912+ GlobalVarDef usedVar = (GlobalVarDef ) used ;
2913+ if (usedVar == v || !usedVar .attrIsDynamicClassMember ()) {
2914+ continue ;
2915+ }
2916+ if (usedVar .attrNearestClassDef () != owner ) {
2917+ continue ;
2918+ }
2919+ Integer usedPos = order .get (usedVar );
2920+ if (usedPos != null && usedPos > currentPos ) {
2921+ v .addError ("Class variable <" + usedVar .getName () + "> is used before it is initialized." );
2922+ return ;
2923+ }
2924+ }
2925+ }
2926+
2927+ private Map <GlobalVarDef , Integer > classVarInitOrder (ClassDef classDef ) {
2928+ return classVarInitOrderCache .computeIfAbsent (classDef , cd -> {
2929+ Map <GlobalVarDef , Integer > order = new IdentityHashMap <>();
2930+ int index = 0 ;
2931+ for (GlobalVarDef var : cd .getVars ()) {
2932+ if (var .attrIsDynamicClassMember ()) {
2933+ order .put (var , index ++);
2934+ }
2935+ }
2936+ return order ;
2937+ });
28842938 }
28852939
28862940 private void checkLocalShadowing (LocalVarDef v ) {
0 commit comments