swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.49k stars 10.35k forks source link

Improve the error message for property wrappers that don't support `init(wrappedValue:)` #61159

Open hborla opened 2 years ago

hborla commented 2 years ago

The following code produces a confusing error message:

@propertyWrapper
struct Wrapper<Value> {
  var wrappedValue: Value? = nil

  init() {}
}

struct S {
  @Wrapper var s: String? = "Hello" // error: Argument passed to call that takes no arguments
}

The real issue here is that @Wrapper doesn't support initialization via = because it doesn't have init(wrappedValue:). We should add a tailored error message to that effect.

hborla commented 2 years ago

cc @mazevedofs

hborla commented 2 years ago

@mazevedofs is working on this but I can't assign it to her for some reason 🤔

I have two high-level ideas for where to fix this:

  1. Use findSuitableWrapperInit or something similar to it in one of the property wrapper requests (perhaps PropertyWrapperInitializerInfoRequest) to see that the property wrapper is initialized with = but the property wrapper has no init(wrappedValue:).
  2. Diagnose this in the constraint system. We'd first need to add a new type of ConstraintFix and corresponding FailureDiagnostic that represents unsupported property wrapper initialization, and then we would need to record that in the constraint system. The hard part about this approach is we'd need to identify all the ways this error can manifest, e.g. argument to parameter mismatch, extraneous arguments, etc., and apply the new fix in all of those places.

I wonder if @amritpan might also have ideas, as she was working on adding something similar to findSuitableWrapperInit in order to implement out-of-line initialization for property wrappers with attribute arguments. Amrit, let us know if you have any thoughts!

amritpan commented 2 years ago

Hi Marina! Thanks for tagging me, Holly!

For idea 1, I wonder if we could diagnose this in PropertyWrapperBackingPropertyTypeRequest::evaluate() inside the if (binding && binding->isInitialized(index)) {} block that already checks if the binding is = initialized?

Decl::allAttachedPropertyWrappersHaveWrappedValueInit() will check if all attached property wrappers have a wrappedValue init and Decl::isInitialized() checks if the property wrapper is = initialized, if I understood those correctly.