diff --git a/src/common/scripting/backend/codegen.cpp b/src/common/scripting/backend/codegen.cpp index 8932d91d631..758f3cd6c3c 100644 --- a/src/common/scripting/backend/codegen.cpp +++ b/src/common/scripting/backend/codegen.cpp @@ -2454,7 +2454,17 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx) SAFE_RESOLVE(Right, ctx); } } - else if (Base->ValueType == Right->ValueType) + else if (Right->IsNativeStruct() && Base->ValueType->isRealPointer() && Base->ValueType->toPointer()->PointedType == Right->ValueType) + { + // allow conversion of native structs to pointers of the same type. This is necessary to assign elements from global arrays like players, sectors, etc. to local pointers. + // For all other types this is not needed. Structs are not assignable and classes can only exist as references. + Right->RequestAddress(ctx, nullptr); + Right->ValueType = Base->ValueType; + } + else if ( Base->ValueType == Right->ValueType + || (Base->ValueType->isRealPointer() && Base->ValueType->toPointer()->PointedType == Right->ValueType) + || (Right->ValueType->isRealPointer() && Right->ValueType->toPointer()->PointedType == Base->ValueType) + ) { if (Base->ValueType->isArray()) { @@ -2462,28 +2472,97 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx) delete this; return nullptr; } - else if (Base->IsDynamicArray()) + else if(!Base->IsVector() && !Base->IsQuaternion()) { - ScriptPosition.Message(MSG_ERROR, "Cannot assign dynamic arrays, use Copy() or Move() function instead"); - delete this; - return nullptr; - } - if (!Base->IsVector() && !Base->IsQuaternion() && Base->ValueType->isStruct()) - { - ScriptPosition.Message(MSG_ERROR, "Struct assignment not implemented yet"); - delete this; - return nullptr; + PType * btype = Base->ValueType; + + if(btype->isRealPointer()) + { + PType * p = static_cast(btype)->PointedType; + if( p->isDynArray() || p->isMap() + || (p->isStruct() && !static_cast(p)->isNative) + ) + { //un-pointer dynarrays, maps and non-native structs for assignment checking + btype = p; + } + } + + if (btype->isDynArray()) + { + if(ctx.Version >= MakeVersion(4, 11, 0)) + { + FArgumentList args; + args.Push(Right); + auto call = new FxMemberFunctionCall(Base, NAME_Copy, args, ScriptPosition); + Right = Base = nullptr; + delete this; + return call->Resolve(ctx); + } + else + { + if(Base->ValueType->isRealPointer() && Right->ValueType->isRealPointer()) + { + ScriptPosition.Message(MSG_WARNING, "Dynamic Array assignments not allowed in ZScript versions below 4.11, use the Copy() or Move() functions instead\n" + TEXTCOLOR_RED "!! Assigning an out array pointer to another out array pointer does not alter either of the underlying arrays' values below ZScript version 4.11 !!"); + } + else + { + ScriptPosition.Message(MSG_ERROR, "Dynamic Array assignments not allowed in ZScript versions below 4.11, use the Copy() or Move() functions instead"); + delete this; + return nullptr; + } + } + } + else if (btype->isMap()) + { + if(ctx.Version >= MakeVersion(4, 11, 0)) + { + FArgumentList args; + args.Push(Right); + auto call = new FxMemberFunctionCall(Base, NAME_Copy, args, ScriptPosition); + Right = Base = nullptr; + delete this; + return call->Resolve(ctx); + } + else + { + if(Base->ValueType->isRealPointer() && Right->ValueType->isRealPointer()) + { // don't break existing code, but warn that it's a no-op + ScriptPosition.Message(MSG_WARNING, "Map assignments not allowed in ZScript versions below 4.11, use the Copy() or Move() functions instead\n" + TEXTCOLOR_RED "!! Assigning an out map pointer to another out map pointer does not alter either of the underlying maps' values below ZScript version 4.11 !!"); + } + else + { + ScriptPosition.Message(MSG_ERROR, "Map assignments not allowed in ZScript versions below 4.11, use the Copy() or Move() functions instead"); + delete this; + return nullptr; + } + } + } + else if (btype->isMapIterator()) + { + ScriptPosition.Message(MSG_ERROR, "Cannot assign map iterators"); + delete this; + return nullptr; + } + else if (btype->isStruct()) + { + if(Base->ValueType->isRealPointer() && Right->ValueType->isRealPointer()) + { // don't break existing code, but warn that it's a no-op + ScriptPosition.Message(MSG_WARNING, "Struct assignment not implemented yet\n" + TEXTCOLOR_RED "!! Assigning an out struct pointer to another out struct pointer does not alter either of the underlying structs' values !!"); + } + else + { + ScriptPosition.Message(MSG_ERROR, "Struct assignment not implemented yet"); + delete this; + return nullptr; + } + } } + // Both types are the same so this is ok. } - else if (Right->IsNativeStruct() && Base->ValueType->isRealPointer() && Base->ValueType->toPointer()->PointedType == Right->ValueType) - { - // allow conversion of native structs to pointers of the same type. This is necessary to assign elements from global arrays like players, sectors, etc. to local pointers. - // For all other types this is not needed. Structs are not assignable and classes can only exist as references. - bool writable; - Right->RequestAddress(ctx, &writable); - Right->ValueType = Base->ValueType; - } else { // pass it to FxTypeCast for complete handling.