Powershell: Type-constrained Boolean variables fail to implicitly convert from PSObject / PSCustomObject instances

Created on 22 Aug 2019  路  6Comments  路  Source: PowerShell/PowerShell

Steps to reproduce

# OK: convert via cast (assigns $true)
$b = [bool] [pscustomobject]::new()

# !! BROKEN: convert via type constraint
[bool] $b = [pscustomobject]::new()

Expected behavior

No output. Both assignments should succeed.

Actual behavior

The 2nd statement causes the following error:

Cannot convert value "System.Management.Automation.PSCustomObject" to type "System.Boolean". Boolean parameters accept only Boolean values and numbers, such as $True, $False, 1 or 0

Environment data

PowerShell Core 7.0.0-preview.3
Issue-Question WG-Engine

All 6 comments

@mklement0 What's happening is that the second example is using parameter binder logic. For parameters that are constrained to boolean we have special logic that restricts the allowed values that can be assigned to boolean to be true, false, 0 and 1. For example, you'll get the same error if you try to assign a string to the constrained variable. Historically, this behaviour was implemented as a result of usability feedback from the Exchange team. People kept trying to type "true" and "false" and getting an unexpected result. Anyway, given that the behaviour is consistent between parameter variables and non-parameter variables I don't really think there's an issue here.

@BrucePay: Thanks for explaining the background, but I do think there is a _usability_ issue here:

Users shouldn't have to think about _parameters_ in this context, and shouldn't have to worry about constraints that come with that, given that they're operating in _expression_ mode and thinking about _variables_, especially since in all other cases [<type>] $foo = ... and $foo = [<type>] ... result in the same value value getting assigned to $foo.
(And if there are other cases where this equivalence doesn't hold, they should be addressed too.)

That something is amiss is indicated by the fact that the error message mentions a _parameter_: Boolean parameters accept only, which is obviously confusing in the context of assigning a _variable_.

From a user's perspective, this coupling is an _implementation detail_, and the _internal_ consistency is not helpful.

Note that the coupling is _not_ documented in the only place that I'm aware of that (far too lightly) touches on casts, about_Variables.

As an aside: There's also no mention there of the little-known fact that - as a _useful_ case of sharing functionality with parameters - you can not only _type_-, but also _validation_-constrain your variables:

[ValidateNotNullorEmpty()] [string] $foo = ''  # fails

Given that this also doesn't happen with any other type, I think it makes sense to remove this special case. See #13651 for a related issue that arises because of this special casing.

/cc @SteveL-MSFT @rjmholt can we get y'all's thoughts on this one?

I think the big considerations for me are:

  • Can we make this change in attributed assignment without making it in parameter binding? (where I think the failure is valid UX, and more importantly is established behaviour)
  • If so, does that make PowerShell more complicated by once again introducing a slight variation of concepts or logic?

I suspect that most users don't think of constrained assignment as a form of parameter binding, so muddying the concept might not have dire consequences. But even if so, I suspect decoupling the two pieces of logic while still getting things like validation attributes to work properly will be a large task.

Seems to me the logic would be better placed in the binder logic itself, rather than in variable assignment. Might be a bit of work to do, but IMO typical variable assignments should not be half mixed up in what should be binder-specific logic. The binder does a fair bit of extra logic as it is already, it's kind of weird that this (and seemingly this alone) got thrown into the standard variable assignment logic.

@rjmholt:

Agreed on point 1.

muddying the concept might not have dire consequences

In the spirit of @vexx32's comment: It is the _current_ behavior that amounts to muddying: the inappropriate application of argument-mode parsing to expression-mode assignments.

(Most users are probably not even aware that you can use validation attributes in constrained assignments too, and I'm not sure it has any real-world uses. Similarly, that a nonsensical constrained assignment such as
[alias('hi')] [Parameter(Mandatory)] [string] $there = 'foo' is quietly accepted is worrisome, though that may be a manifestation of a larger problem of randomly placed attributes getting quietly ignored - see #10614)

Was this page helpful?
0 / 5 - 0 ratings