Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/IRGen/GenProto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1751,6 +1751,10 @@ class AccessorConformanceInfo : public ConformanceInfo {
#endif

auto typeWitness = Conformance.getTypeWitness(assocType);
if (langOpts.hasFeature(Feature::EmbeddedExistentials) &&
SILWT->isSpecialized()) {
typeWitness = entry.getAssociatedTypeWitness().Witness;
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't make sense, why is Conformance.getTypeWitness() returning the wrong thing? Is Conformance is not a SpecializedProtocolConformance? If not, it should be fixed to match up and the conditional removed.

Copy link
Contributor

Choose a reason for hiding this comment

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

You could use either Conformance.getTypeWitness() or entry.getAssociatedTypeWitness().Witness here, but they should be equal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Conformance.getTypeWitness returns the wrong thing because Conformance is the root conformance not the specialized_conformance.

Example:

protocol PWithAssoc {
  associatedtype Assoc
  func a() -> Assoc
}

struct Conformer<T> {
  var t: T
  init(_ t: T) {
    self.t = t
  }
}

struct Container<T> : PWithAssoc {
  var x: Conformer<T>

  init(_ t: T) {
    self.x = Conformer(t)
  }

  func a() -> Conformer<T> {
    return x
  }
}

func test(_ p: PWithAssoc) {
  let _ = p.a()
}

test(Container(1))

In this example, we have a specialized SILWitness table:

sil_witness_table shared [specialized] Container<Int>: specialize <Int> (<T> Container<T>: PWithAssoc module t3) {
  associated_type Assoc: Conformer<Int>
  method #PWithAssoc.a: <Self where Self : PWithAssoc> (Self) -> () -> Self.Assoc : @$e2t39ContainerVyxGAA10PWithAssocA2aEP1a0D0QzyFTWSi_Tg5    // specialized protocol witness for PWithAssoc.a() in conformance Container<A>
}

In the witnesstablebuilder code Conformance is initialized to the root conformance SILWT->getConformance()->getRootConformance():

  class WitnessTableBuilderBase {                                               
  protected:                                                                    
    IRGenModule &IGM;                                                           
    SILWitnessTable *SILWT;                                                     
    const RootProtocolConformance &Conformance;                                 
    const ProtocolConformance &ConformanceInContext;                            
    CanType ConcreteType;                                                       
                                                                                
    std::optional<FulfillmentMap> Fulfillments;                                 
                                                                                
    WitnessTableBuilderBase(IRGenModule &IGM, SILWitnessTable *SILWT)           
        : IGM(IGM), SILWT(SILWT),                                               
          Conformance(*SILWT->getConformance()->getRootConformance()),          
          ConformanceInContext(*mapConformanceIntoContext(SILWT->getConformance()->getRootConformance())),
          ConcreteType(Conformance.getDeclContext()                             
                         ->mapTypeIntoEnvironment(Conformance.getType())        
                         ->getCanonicalType()) {}    

And that ends up being the normal conformance:

(normal_conformance type="Container<T>" protocol="PWithAssoc" explicit nonisolated
  (assoc_type req="Assoc" type="Conformer<T>")
  (value req="a()" witness="t3.(file).Container.a()@t3.swift:20:8")
  (assoc_conformance type="Self" proto="Copyable"
    (builtin_conformance type="Container<T>" protocol="Copyable" synthesized nonisolated))
  (assoc_conformance type="Self" proto="Escapable"
    (builtin_conformance type="Container<T>" protocol="Escapable" synthesized nonisolated))
  (assoc_conformance type="Self.Assoc" proto="Copyable"
    (builtin_conformance type="Conformer<T>" protocol="Copyable" synthesized nonisolated))
  (assoc_conformance type="Self.Assoc" proto="Escapable"
    (builtin_conformance type="Conformer<T>" protocol="Escapable" synthesized nonisolated)))

And then we end up with: (bound_generic_struct_type decl="t3.(file)[email protected]:6:8" (generic_type_param_type depth=0 index=0 name="T" param_kind=type)) Instead of Conformer<Int> for

auto typeWitness = Conformance.getTypeWitness(assocType); 

If we used SILWT->getConformance().getTypeWitness(assocType) we would get the the right type (bound_generic_struct_type decl="t3.(file)[email protected]:6:8" (struct_type decl="Swift.(file).Int"))

}

if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
// In Embedded Swift associated type witness point to the metadata.
Expand Down
49 changes: 49 additions & 0 deletions test/embedded/existential.swift
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,47 @@ func test13(_ p: any P4) {
p.t.printit()
}

struct GenericConformer<T> : ValuePrinter {
var t: T?
var l = (0, 1, 2, 3)

init(_ t: T) { self.t = t }

func printValue() {
print("GenericConformer \(l.0) \(l.1) \(l.2) \(l.3)")
}

mutating func mutate() {
l = (4, 5, 6, 7)
}
}

struct GenericConformerWithAssoc<T> : WithAssoc {
var g : GenericConformer<T>

init( _ g: T) {
self.g = GenericConformer(g)
}

func a() -> GenericConformer<T> {
return g
}
}

func test14(_ p: any ValuePrinter) {
print("test any ValuePrinter")
p.printValue()
var p2 = p
p2.mutate()
p2.printValue()
}

func test15(_ p: any WithAssoc) {
print("test any WithAssoc")
let l = p.a()
l.printValue()
}

@main
struct Main {
static func main() {
Expand Down Expand Up @@ -423,5 +464,13 @@ struct Main {
test13(P4Conformer())
// OUTPUT: test13
// OUTPUT: QConformer 3
// OUTPUT-NOT: deinit
test14(GenericConformer(1))
// OUTPUT: test any ValuePrinter
// OUTPUT: GenericConformer 0 1 2 3
// OUTPUT: GenericConformer 4 5 6 7
test15(GenericConformerWithAssoc(1))
// OUTPUT: test any WithAssoc
// OUTPUT: GenericConformer 0 1 2 3
}
}