Essentially, the compiler doesn't understand that you want the argument to your function to be of type string. Basically, you can NOT invoke instance methods on implicitly declared function arguments. You'll need to use something like the following.
let y (value:string) =
match value when
| x when x.Contains("hi") -> "HELLO"
| x when x.Contains("hello") -> "HI"
| x -> x
Interestingly, if you were not invoking an instance method, but instead using it in some other way (where the type was already known), you would be fine. In other words, suppose a StringUtils module contained a method Has, which took in two strings and performed the same check as Contains; if so, you could use the implicit argument, because the compiler already knows of which type the value must be.
module StringUtils =
let has (word:string) value = word.Contains(value)
module Parse =
let y = function
| x when x |> StringUtils.has "hi" -> "HELLO"
| x when x |> StringUtils.has "hello" -> "HI"
| x -> x
Obviously, in many cases, this sort of thing is unnecessarily verbose. But it demonstrates the larger point about F#'s type inference behavior.