diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index d452fedd9..5a77b4a01 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6715,16 +6715,17 @@ bool FxStructMember::RequestAddress(FCompileContext &ctx, bool *writable) if ((classx->ExprType == EFX_Self) && (ctx.Function && (ctx.Function->Variants[0].Flags & VARF_ReadOnly))) bWritable = false; } - // [ZZ] plain data "inherits" scope of whatever it was defined in. - if (bWritable) // don't do complex checks on early fail + // [ZZ] implement write barrier between different scopes + if (bWritable) { int outerflags = 0; if (ctx.Function) outerflags = ctx.Function->Variants[0].Flags; - FScopeBarrier scopeBarrier(outerflags, FScopeBarrier::FlagsFromSide(BarrierSide), membervar->SymbolName.GetChars()); + FScopeBarrier scopeBarrier(outerflags, FScopeBarrier::FlagsFromSide(BarrierSide), ""); if (!scopeBarrier.writable) bWritable = false; } + *writable = bWritable; } return true; @@ -6769,7 +6770,7 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) } BarrierSide = scopeBarrier.sidelast; - if (classx->ExprType == EFX_StructMember || classx->ExprType == EFX_ClassMember) + if (classx->ExprType == EFX_StructMember) // note: only do this for structs now { FxStructMember* pmember = (FxStructMember*)classx; if (BarrierSide == FScopeBarrier::Side_PlainData && pmember) @@ -6793,7 +6794,8 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx) { auto parentfield = static_cast(classx)->membervar; // PFields are garbage collected so this will be automatically taken care of later. - auto newfield = new PField(NAME_None, membervar->Type, membervar->Flags | parentfield->Flags, membervar->Offset + parentfield->Offset); + // [ZZ] call ChangeSideInFlags to ensure that we don't get ui+play + auto newfield = new PField(NAME_None, membervar->Type, FScopeBarrier::ChangeSideInFlags(membervar->Flags | parentfield->Flags, BarrierSide), membervar->Offset + parentfield->Offset); newfield->BitValue = membervar->BitValue; static_cast(classx)->membervar = newfield; classx->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away. @@ -8023,22 +8025,22 @@ isresolved: // [ZZ] if self is a struct or a class member, check if it's valid to call this function at all. // implement more magic - if (Self->ExprType == EFX_ClassMember || Self->ExprType == EFX_StructMember) + int outerflags = 0; + if (ctx.Function) + outerflags = ctx.Function->Variants[0].Flags; + int innerflags = afd->Variants[0].Flags; + if (Self->ExprType == EFX_StructMember) { FxStructMember* pmember = (FxStructMember*)Self; - int outerflags = 0; - if (ctx.Function) - outerflags = ctx.Function->Variants[0].Flags; - int innerflags = afd->Variants[0].Flags; if (FScopeBarrier::SideFromFlags(innerflags) == FScopeBarrier::Side_PlainData) innerflags = FScopeBarrier::ChangeSideInFlags(innerflags, pmember->BarrierSide); - FScopeBarrier scopeBarrier(outerflags, innerflags, MethodName.GetChars()); - if (!scopeBarrier.callable) - { - ScriptPosition.Message(MSG_ERROR, "%s", scopeBarrier.callerror.GetChars()); - delete this; - return nullptr; - } + } + FScopeBarrier scopeBarrier(outerflags, innerflags, MethodName.GetChars()); + if (!scopeBarrier.callable) + { + ScriptPosition.Message(MSG_ERROR, "%s", scopeBarrier.callerror.GetChars()); + delete this; + return nullptr; } if (staticonly && (afd->Variants[0].Flags & VARF_Method)) diff --git a/src/scripting/backend/codegen.h b/src/scripting/backend/codegen.h index 39c893e33..cbf02f954 100644 --- a/src/scripting/backend/codegen.h +++ b/src/scripting/backend/codegen.h @@ -187,7 +187,7 @@ struct FScopeBarrier sidelast = sideto; else sideto = sidelast; - if ((sideto == Side_UI) != (sidefrom == Side_UI)) // only ui -> ui is readable + if ((sideto == Side_UI) && (sidefrom != Side_UI)) // only ui -> ui is readable { readable = false; readerror.Format("Can't read %s field %s from %s context", StringFromSide(sideto), name, StringFromSide(sidefrom)); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 3afb80ff3..3eb36624a 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2397,9 +2397,9 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool if (varflags & VARF_Final) sym->Variants[0].Implementation->Final = true; // [ZZ] unspecified virtual function inherits old scope. virtual function scope can't be changed. - if (f->Flags & ZCC_UIFlag) // only direct specification here (varflags can also have owning class scope applied, we don't want that) + if (varflags & VARF_UI) sym->Variants[0].Implementation->ScopeUI = true; - if (f->Flags & ZCC_Play) // only direct specification here + if (varflags & VARF_Play) sym->Variants[0].Implementation->ScopePlay = true; if (varflags & VARF_ReadOnly) sym->Variants[0].Implementation->FuncConst = true; @@ -2422,8 +2422,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool Error(f, "Attempt to override final function %s", FName(f->Name).GetChars()); } // you can't change ui/play/clearscope for a virtual method. - if ((oldfunc->ScopePlay != sym->Variants[0].Implementation->ScopePlay) || - (oldfunc->ScopeUI != sym->Variants[0].Implementation->ScopeUI)) + if (f->Flags & (ZCC_UIFlag|ZCC_Play|ZCC_ClearScope)) { Error(f, "Attempt to change scope for virtual function %s", FName(f->Name).GetChars()); } @@ -2432,7 +2431,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { Error(f, "Attempt to change const qualifier for virtual function %s", FName(f->Name).GetChars()); } - // inherit scope of original function + // inherit scope of original function if override not specified if (sym->Variants[0].Implementation->ScopeUI = oldfunc->ScopeUI) sym->Variants[0].Flags = (sym->Variants[0].Flags&~(VARF_Play)) | VARF_UI; else if (sym->Variants[0].Implementation->ScopePlay = oldfunc->ScopePlay)