Skip to content

Don't call copyMostAttrib on ScalarLogical result#5047

Merged
mattdowle merged 3 commits intomasterfrom
att_on_FALSE
Jun 17, 2021
Merged

Don't call copyMostAttrib on ScalarLogical result#5047
mattdowle merged 3 commits intomasterfrom
att_on_FALSE

Conversation

@mattdowle
Copy link
Copy Markdown
Member

@mattdowle mattdowle commented Jun 17, 2021

Follow up to #4350

The first commit "reprex" in this PR reproduces the problem. I'm using R 4.0.3 currently. Running R CMD check or test.data.table() is necessary to make 2195.[3-8] fail because test 2190.6 needs to run first. When I ran tests 2195 at the dev prompt they always passed and I couldn't reproduce.

Test 2190.6 is subassigning a logical vector with an attribute attached to an existing list column :

DT = data.table(a=list(1:2, 3, 4))
DT[1:2, a:=structure(c(TRUE, FALSE), att='t')]

This ended up attaching the "att" attribute with value "t" to R's internal global FALSE constant. This caused the !identical(incomparables, FALSE) in duplicated.R to fail. I debugged and confirmed that print(FALSE) showed that FALSE had indeed received the attribute whereas incomparables did not have the attribute. Presumably TRUE was also receiving the attribute but only FALSE was showing up so far in tests 2195.* on the FALSE value.

The error shows up as :

Running test id 2195.3          Test 2195.3 produced 1 errors but expected 0
Expected: 
Observed: argument 'incomparables != FALSE' is not used (yet)

In #4595 I worked around the issue by changing duplicated.R to use !isFALSE(incomparables) instead of !identical(incomparables, FALSE) to ignore any attributes attached to FALSE.

This PR now fixes the root cause. ScalarLogical(false) returns R's internal global FALSE value; allocVector() has to be used to return a new allocation of a length-1 logical value and a new allocation is necessary if attributes are to be attached. After this PR, duplicated.R could use !identical(incomparables, FALSE) again, but having gone through this, I'm now thinking that ignoring attributes attached to FALSE is more robust anyway so will leave the !isFALSE() in place in duplicated.R.

@mattdowle mattdowle added the dev label Jun 17, 2021
@mattdowle mattdowle added this to the 1.14.1 milestone Jun 17, 2021
Comment thread src/assign.c
// allocVector instead of ScalarLogical to avoid copyMostAttrib on R's internal global TRUE/FALSE values; #4595. Then because
// ScalarInteger may now or in future R also return R internal global small integer constants, the same for that. Then
// because we do that here for logical and integer, use allocVeector too for the other types to follow the same pattern and possibly
// in future R will also have some global constants for those types too.
Copy link
Copy Markdown
Member

@MichaelChirico MichaelChirico Jun 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw I believe base::pi counts as just such a constant for REALSXP already

Copy link
Copy Markdown
Member Author

@mattdowle mattdowle Jun 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting, where do you see that?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm on second thought I think I misunderstood. I was analogizing to base::T which isn't the same issue

Comment thread src/assign.c
case LGLSXP: BODY(int, LOGICAL, SEXP, PROTECT(allocVector(LGLSXP, 1));LOGICAL(cval)[0]=val;copyMostAttrib(source,cval);UNPROTECT(1), SET_VECTOR_ELT(target,off+i,cval))
case INTSXP: BODY(int, INTEGER, SEXP, PROTECT(allocVector(INTSXP, 1));INTEGER(cval)[0]=val;copyMostAttrib(source,cval);UNPROTECT(1), SET_VECTOR_ELT(target,off+i,cval))
case REALSXP: BODY(double, REAL, SEXP, PROTECT(allocVector(REALSXP, 1));REAL(cval)[0]=val;copyMostAttrib(source,cval);UNPROTECT(1), SET_VECTOR_ELT(target,off+i,cval))
case CPLXSXP: BODY(Rcomplex, COMPLEX, SEXP, PROTECT(allocVector(CPLXSXP, 1));COMPLEX(cval)[0]=val;copyMostAttrib(source,cval);UNPROTECT(1), SET_VECTOR_ELT(target,off+i,cval))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, does assigning like this work as expected for complex? or do we have to assign the real/imaginary parts individually?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes Rcomplex is a struct and in C you can assign structs; e.g. grep NA_CPLX data.table/src/*.

@mattdowle mattdowle merged commit 2e86911 into master Jun 17, 2021
@mattdowle mattdowle deleted the att_on_FALSE branch June 17, 2021 20:22
@jangorecki jangorecki modified the milestones: 1.14.9, 1.15.0 Oct 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants